A flexible binary format serialization library for Python
Project description
fmtspec
fmtspec is a Python library for binary encoding and decoding built around composable format objects.
The only dependency is the excellent msgspec package.
Installation
pip install fmtspec
Core Features
encode(...)anddecode(...)for in-memory byte buffersencode_stream(...)anddecode_stream(...)for files, sockets, andBytesIOfmtspec.typesfor reusable primitives such as integers, enums, arrays, sized fields, bitfields, and tagged layoutsencode_inspect(...),decode_inspect(...), andformat_tree(...)for inspecting parse trees during encoding and decoding- Informative exceptions with failure paths and format context
Contextandfmtspec.streamfor implementing customTypeclasses on the public API
Typical Workflow
- Describe the wire format with
fmtspec.typesand plain Python containers. - Encode Python values with
encode(...)orencode_stream(...). - Decode the bytes back into builtins, dataclasses, or
msgspec.Structshapes.
Start With a Mapping Format
from fmtspec import decode, encode, types
packet_fmt = {
"name": types.TakeUntil(types.str_utf8, b"\0"),
"count": types.u32le,
}
packet = {
"name": "widget",
"count": 3,
}
data = encode(packet, packet_fmt)
assert data == b"widget\0\x03\x00\x00\x00"
decoded = decode(data, packet_fmt)
assert decoded == packet
This is the core fmtspec style: combine primitive format objects into mappings, tuples, or arrays, then round-trip ordinary Python values.
Derive the Format From a Typed Shape
If fields are annotated with typing.Annotated[..., fmt], fmtspec can derive the mapping format for you.
from dataclasses import dataclass
from typing import Annotated
from fmtspec import decode, encode, types
STR_FMT = types.TakeUntil(types.str_utf8, b"\0")
INT_FMT = types.u32le
@dataclass(frozen=True, slots=True)
class Record:
name: Annotated[str, STR_FMT]
count: Annotated[int, INT_FMT]
record = Record(name="widget", count=3)
data = encode(record)
roundtripped = decode(data, shape=Record)
assert roundtripped == record
This is the most ergonomic path when your wire layout already matches a dataclass or msgspec.Struct.
Reject Trailing Bytes When You Need Full Consumption
from fmtspec import DecodeError, decode, types
assert decode(b"\x00\x2a", types.u16, strict=True) == 42
try:
decode(b"\x00\x2a\xff", types.u16, strict=True)
except DecodeError:
pass
Use strict=True on decode(...) when extra bytes should be treated as a protocol error instead of being silently ignored.
Inspect Layouts While Debugging
from fmtspec import encode_inspect, format_tree, types
fmt = {
"x": types.u8,
"y": types.u16,
}
data, tree = encode_inspect({"x": 1, "y": 0x0203}, fmt)
print(data)
print(format_tree(tree))
Inspection shows offsets, sizes, values, and child structure for each encoding or decoding step. It is intended for debugging, tooling, and protocol exploration rather than performance.
For the example above, format_tree(tree) renders output like this:
* Mapping @ [0:3] (3 bytes) (2 items)
├─ [x] Int @ [0:1] (1 bytes)
│ value: 1
│ data: 01
└─ [y] Int @ [1:3] (2 bytes)
value: 515
data: 02 03
Error Model
The public API raises structured exceptions instead of only raw ValueError instances.
EncodeError: a Python value could not be serialized with the chosen formatDecodeError: the incoming bytes did not match the formatShapeError: decoding succeeded, but the result could not be converted into the requestedshape
These exceptions preserve context such as the active format, object, path, cause, and optional inspection node.
from fmtspec import DecodeError, decode, types
fmt = {
"kind": types.u8,
"payload": types.Sized(length=types.u8, fmt=types.Bytes()),
}
try:
decode(b"\x01\x05abc", fmt, strict=True)
except DecodeError as exc:
print(exc)
print(exc.path)
print(exc.fmt)
This context is especially useful with nested mappings, arrays, Switch(...), and custom types, where the failing field path matters as much as the raw message. See docs/core-api.md for more detail on errors and inspection.
Documentation
These reference pages cover the details by topic:
- docs/core-api.md for top-level encode/decode, format derivation, inspection, and errors
- docs/types-api.md for
fmtspec.types - docs/stream-api.md for custom
Typeimplementations,Context, andfmtspec.stream
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fmtspec-0.2.0.tar.gz.
File metadata
- Download URL: fmtspec-0.2.0.tar.gz
- Upload date:
- Size: 29.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.10.11 {"installer":{"name":"uv","version":"0.10.11","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d64722944d1cde4ac59ffddfbd7c027950a8236b823a67df2484568f1b970cf9
|
|
| MD5 |
4f92c522d4bcc85a7adf546e03256b92
|
|
| BLAKE2b-256 |
36e5c3bce9a2adddb5dfcabe0763f59af562e5ccea918704f47334ec27db2ab5
|
File details
Details for the file fmtspec-0.2.0-py3-none-any.whl.
File metadata
- Download URL: fmtspec-0.2.0-py3-none-any.whl
- Upload date:
- Size: 41.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.10.11 {"installer":{"name":"uv","version":"0.10.11","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
232552a85f1c0974fed8cdbb3aea4912ce173d5f40bb6a77b21fcb4a1439d159
|
|
| MD5 |
af1d451ebd5afa2d2fc17c9ceb7d1aad
|
|
| BLAKE2b-256 |
01bb2e5381c64e5cea900b9993f62b425e1be4fe0d11b01b3ba87dd9401b619b
|