Skip to main content

firstclass

@firstclass works like @dataclass (same keyword args) and adds conversion to and from packed bytes, ctypes, a C struct header, JSON and XML. Pure Python 3.10+, no dependencies.

from firstclass import firstclass, uint8_t, uint16_t, int32_t, float32_t, bool_t, char_array, bytes_t, array_t

@firstclass
class Point:
    x: int32_t
    y: int32_t

@firstclass
class Packet:
    kind: uint8_t
    ok: bool_t
    temp: float32_t
    name: char_array[8]        # NUL-padded UTF-8
    mac: bytes_t[6]            # exactly 6 bytes
    samples: array_t[uint16_t, 4]
    origin: Point              # nested

p = Packet(1, True, 21.5, "sensor", b"\x00\x11\x22\x33\x44\x55", [1, 2, 3, 4], Point(10, -20))

raw = p.to_bytes()                 # packed, little-endian
assert Packet.from_bytes(raw) == p
Packet.sizeof()                    # 36

Types

Marker C Bytes
uint8_t uint16_t uint32_t uint64_t uintN_t 1/2/4/8
int8_t int16_t int32_t int64_t intN_t 1/2/4/8
float32_t / float64_t float / double 4 / 8
bool_t bool 1
char_t char 1
char_array[N] char x[N] N (UTF-8, NUL-padded)
bytes_t[N] uint8_t x[N] N
array_t[T, N] T x[N] N * size(T)
another @firstclass class struct Name its size

Values are checked: out of range ints, over-long strings and wrong lengths raise ValueError; wrong Python types raise TypeError. Nothing is truncated or coerced silently.

Raw ctypes types work as annotations too (packed, same layout as a _pack_ = 1 Structure): ctypes.c_int, c_uint64, c_size_t, c_bool, c_float, c_double, c_char, c_uint16 * 4, c_char * 8. Pointers (c_void_p, POINTER(T), c_char_p) are stored as the address (an int, 0 = NULL) at pointer size; to_ctypes() gives a real pointer field you can dereference. The address only means something inside the same process. Sizes follow the platform (c_long is 4 bytes on Windows, 8 on Linux).

Methods added to every class

Method
obj.to_bytes() / Cls.from_bytes(data) binary; from_bytes needs exactly sizeof() bytes
Cls.sizeof() size in bytes
obj.to_dict() / Cls.from_dict(d) JSON-compatible dict (bytes as hex)
obj.to_json(**kw) / Cls.from_json(s) kw goes to json.dumps
obj.to_xml(indent=None) / Cls.from_xml(s) <Packet><kind>1</kind>...</Packet>, arrays as <item>
obj.to_ctypes() / Cls.from_ctypes(c) / Cls.ctypes_type() ctypes.Structure with matching layout
Cls.c_header() C source for the struct(s), nested ones first

Layout options

@firstclass(byteorder="big")   # default "little"
@firstclass(align=True)        # default False = packed, no padding (like __attribute__((packed)))
@firstclass(frozen=True, slots=True)   # any dataclass option

align=True inserts natural-alignment padding like a normal C struct (assumes the x86-64 style rule: alignment equals size). Padding bytes are written as zero and ignored on read. C headers describe the struct only; C compilers use native byte order, so byteorder is noted in a comment.

Notes

  • Fields with plain types (str, int) work for JSON/XML only; to_bytes() raises TypeError naming the field.
  • Use from __future__ import annotations freely; annotations are resolved lazily. Classes defined inside functions that refer to local names are not resolvable.
  • Don't parse untrusted XML with from_xml without extra care (stdlib xml.etree).
  • Field names that collide with the generated methods (to_bytes, ...) raise TypeError.

Example

python examples/example.py shows bytes, JSON, XML, ctypes and the C header from one class.

Develop

python -m unittest discover -s tests

Metadata

Release files for firstclass 0.0.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for firstclass 0.0.2
File Size Uploaded Provenance Actions
firstclass-0.0.2.tar.gz 14.5 kB Without provenance Details for firstclass-0.0.2.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for firstclass 0.0.2
File Interpreter ABI Platform Actions
firstclass-0.0.2-py3-none-any.whl Python 3 none any Details for firstclass-0.0.2-py3-none-any.whl

Total release size: 24.6 kB

Release files / firstclass-0.0.2.tar.gz

Download URL firstclass-0.0.2.tar.gz
Size 14.5 kB
Tags Source
SHA-256 checksum
How to use checksums
c9b898ecf9ede13ef8e6746191ca16c27ad85a23f7f64d7eae620b17428977c2
BLAKE2b-256 checksum
How to use checksums
b4989a0addd432430c9ea0814af45cd577f0f8ed3931ac880de6e2f179ad6b28
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.16

Release files / firstclass-0.0.2-py3-none-any.whl

Download URL firstclass-0.0.2-py3-none-any.whl
Size 10.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9687afb2846782c159fa816456cc4fd71a1325e8a2539f3e1b16c9f058f91d3a
BLAKE2b-256 checksum
How to use checksums
e2fbe1ad71b0f5dc16da82e746401d86bea08f50657a2a6a317ea4cdf73dcd91
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.16

Security for firstclass 0.0.2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page