Universal structured-data loader: native Rust codecs that decode any popular config
format into native Python objects — and, when you ask, into frozen msgspec models.
Two verbs, loads/dumps, sync and async twins, one wheel. json-module semantics,
strict typing, no surprises.
Part of the Eager (e-) stack by damvolkov, built on
open-source engines — the speed and robustness of C and Rust, for serialization in Python.
Install
uv add e-serde # or: pip install e-serde
Usage
import eserde
import msgspec
from eserde import Format
from pathlib import Path
Six functions, json semantics, keyword-only options. bytes/str sources name the
format; a Path autodetects from its suffix — .json .jsonc .yaml .yml .toml .ini .cfg .conf.
| Function | Input | Output |
|---|---|---|
loads |
bytes | str | Path |
plain tree — or the model in type= |
dumps |
any object | bytes |
load |
Path | BinaryIO |
like loads |
dump |
object → file | None |
aloads · adumps · aload · adump |
same | awaitables of the same |
loads — decode
cfg = eserde.loads(b'{"host": "0.0.0.0", "port": 8080}', format=Format.JSON)
# {'host': '0.0.0.0', 'port': 8080}
class Server(msgspec.Struct, frozen=True):
host: str
port: int
srv = eserde.loads(src, format=Format.JSON, type=Server) # Server(host='0.0.0.0', port=8080)
srv = eserde.loads(ini, format=Format.INI, type=Server, strict=False) # "8080" → 8080
net = eserde.loads(src, type=Net, dec_hook=lambda t, v: t(v)) # custom fields inside type=
| kwarg | effect |
|---|---|
format= |
required for bytes/str; inferred from a Path |
type= |
validate into a Struct, dataclass, TypedDict, attrs or pydantic model; violations raise LoadError |
strict=False |
msgspec coercion — the escape hatch INI needs |
object_hook= |
rewrite every decoded mapping, innermost first (json semantics) |
dec_hook= |
teach type= custom field types (requires type=) |
registry= |
swap the default codec set |
dumps — encode
raw = eserde.dumps({"name": "demian", "n": 42}, format=Format.YAML)
# b'name: demian\n"n": 42\n'
from fractions import Fraction
eserde.dumps({"f": Fraction(1, 2)}, format=Format.JSON, encoders={Fraction: str}) # b'{"f":"1/2"}'
eserde.dumps({"f": Fraction(1, 2)}, format=Format.JSON, default=float) # b'{"f":0.5}'
Every input is normalized through the Jsonable encoder first (datetime → ISO,
Enum → value, bytes → base64), so each format sees the same tree.
| kwarg | effect |
|---|---|
format= |
defaults to Format.JSON |
encoders= |
exact-type hooks, ahead of every built-in; results are re-walked |
default= |
json/orjson-style last resort for unknown types; none → EncoderError |
registry= |
swap the default codec set |
load / dump — files
cfg = eserde.load(Path("config.toml")) # format from the .toml suffix
with open("app.json", "rb") as fh:
data = eserde.load(fh, format=Format.JSON) # an open handle needs the format
eserde.dump(cfg, Path("out.jsonc")) # writes straight to disk → None
Same kwargs as loads / dumps.
async
aloads · adumps · aload · adump — same signatures; I/O and the GIL-free native
parse run off the event loop. The Rust codecs release the GIL, so concurrent aloads
parallelizes decode across cores.
async def main():
srv = await eserde.aloads(Path("config.yaml"), type=Server)
await eserde.adump(srv, Path("copy.json"))
compat — json drop-in
Frameworks duck-type the stdlib module (json_serialize=, renderers, formatters).
eserde.compat speaks json.dumps/json.loads exactly, str output; behaviours
e-serde cannot share faithfully delegate to the stdlib — slower, never a surprise.
from eserde import compat
compat.dumps({"a": 1}, ensure_ascii=False) # '{"a":1}' native compact utf-8
compat.dumps({"a": 1}) # byte-faithful to stdlib json
compat.loads('{"a": 1.5}', parse_float=Decimal) # delegated to stdlib, never guessed
Errors are the LoaderError family: FormatError (no/unknown format), LoadError
(decode or validation), DumpError / EncoderError (encode), CodecError (backend
missing). The full guide lives at damvolkov.github.io/e-serde.
Formats and backends
| Format | Extension | Spec | Engine (pinned) | Notable |
|---|---|---|---|---|
| JSON | .json |
RFC 8259 | msgspec.json 0.21 |
exact big ints, strict tokens |
| JSONC | .jsonc |
Deno jsonc | jsonc-parser 0.33 |
comments, trailing commas |
| YAML | .yaml .yml |
YAML 1.2 core + merge keys | saphyr 0.0.12 |
anchors, <<, bomb-guarded |
| TOML | .toml |
TOML v1.1 | toml-rs 1.1 |
inf/nan, no null, i64 ints |
| INI | .ini .cfg .conf |
de-facto | rust-ini 0.21 |
strings; merge on strict=False |
The contract lives in code — eserde.STANDARDS — and test_standards.py executes every
claim against the live codecs and the lockfiles. Bumping an engine or changing a format
capability is a deliberate act, never silent drift. Full per-format pages (capabilities,
limits, examples): docs → Formats.
Everything Rust is one extension module (eserde._native), compiled by maturin from
crates/native. The only runtime dependency is msgspec.
Benchmarks
Median decode of a 100 KB config on CPython 3.14 (release build). Regenerate with make bench.
| Format | e-serde | nearest rival | margin |
|---|---|---|---|
| JSON | 0.17 ms | orjson 0.17 ms | ≈ tie — same decoder (msgspec) |
| YAML | 2.20 ms | pyyaml C 12–16 ms | ≈6× — ruamel 66× |
| TOML | 1.53 ms | rtoml 2.3–2.5 ms | 1.5× — tomlkit 42× |
| JSONC | 0.79 ms | pyjson5 0.39 ms | the one format behind (×0.5) |
| INI | 2.00 ms | configparser 22 ms | 11× |
Because the Rust codecs release the GIL, aloads parallelizes decode: on 10 MB YAML/TOML the
async fan-out is ~2× faster than serial sync (JSON stays flat — msgspec's C decoder holds the
GIL). More charts in assets/benchmarks/:
dumps ·
typed ·
async ·
memory.
Architecture
crates/native/ single Rust cdylib, one submodule per format
src/eserde/
__init__.py the one façade: import eserde; eserde.loads(...)
infra/ contracts with zero internal deps: errors, formats, io, protocols
backends/ one folder per engine: native/ (Rust), msgspec/ (C) + registry
logic/ the facade functions: loads/dumps/load/dump/async + Jsonable encoder
tests/
unit/eserde/ exact mirror of src
benchmark/ rival matrix + report renderer
resources/ canonical sample.* fixtures
docs/ mkdocs-material site
Only the package root has an __init__.py; every subpackage is a namespace folder. Import
boundaries are enforced by tach: eserde → logic → backends → infra/_native.
Design rules: decoding is always Rust/C, validation is msgspec's or pydantic's —
type= only routes the decoded tree. Round-trip losses are explicit: JSONC comments are
dropped on dumps; TOML has no null; YAML non-scalar keys and multi-document streams are
rejected.
Development
uv sync # installs the dev group, builds the extension in place
make test # pytest
make check # ruff + format + ty + tach + pytest (what CI runs)
make bench # rival benchmark matrix → assets/benchmarks/*.png
Roadmap
Interop landed: eserde is a custom encoder for any framework that ducks-types json
(eserde.compat), validates pydantic/attrs models through type=, and accepts
per-call default=/encoders=/dec_hook= hooks. Shipped alongside a comparative
concurrency stress harness (make stress). Next: broaden interop — msgspec/pydantic
request-body and FastAPI response pipelines — and CI-gated regression against rival
decoders under sustained load.
License
MIT — see LICENSE.
Release files for e-serde 0.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| e_serde-0.2.1.tar.gz | 27.6 kB | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| e_serde-0.2.1-cp314-cp314-win_amd64.whl | CPython 3.14 | CPython 3.14 | Windows x86-64 | Details |
| e_serde-0.2.1-cp314-cp314-manylinux_2_28_x86_64.whl | CPython 3.14 | CPython 3.14 | Linux glibc 2.28+ x86-64 | Details |
| e_serde-0.2.1-cp314-cp314-manylinux_2_28_aarch64.whl | CPython 3.14 | CPython 3.14 | Linux glibc 2.28+ ARM64 | Details |
| e_serde-0.2.1-cp314-cp314-macosx_11_0_arm64.whl | CPython 3.14 | CPython 3.14 | macOS 11.0+ ARM64 | Details |
| e_serde-0.2.1-cp314-cp314-macosx_10_12_x86_64.whl | CPython 3.14 | CPython 3.14 | macOS 10.12+ x86-64 | Details |
Total release size: 2.1 MB
Release files / e_serde-0.2.1.tar.gz
| Download URL | e_serde-0.2.1.tar.gz |
|---|---|
| Size | 27.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
df363595ffd6f8bb4dea2e1fd823dda69a912c7fd7364ad8d9ee6eb4b2c66010
|
|
BLAKE2b-256 checksum How to use checksums |
865a47ab036859fcd3f48edfcf4cf79c6c1b256605aa964d28f1d86612222837
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 23, 2026.
Transparency logRelease files / e_serde-0.2.1-cp314-cp314-win_amd64.whl
| Download URL | e_serde-0.2.1-cp314-cp314-win_amd64.whl |
|---|---|
| Size | 361.9 kB |
| Tags | CPython 3.14 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
6d6e90f7cc31c098f0c66b112972746563a7933938367c8df843480044b8947f
|
|
BLAKE2b-256 checksum How to use checksums |
5ef4fec32199995a86b61b833c1c8c6b5c8e950da3f81eb6943bd9bfd8e65dd8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 23, 2026.
Transparency logRelease files / e_serde-0.2.1-cp314-cp314-manylinux_2_28_x86_64.whl
| Download URL | e_serde-0.2.1-cp314-cp314-manylinux_2_28_x86_64.whl |
|---|---|
| Size | 458.1 kB |
| Tags | CPython 3.14 Linux glibc 2.28+ x86-64 |
|
SHA-256 checksum How to use checksums |
64cd0e6de1690009c17e3bf2bafe004496453adf11b5cf3c4b953599c92f3710
|
|
BLAKE2b-256 checksum How to use checksums |
034bc46139ea5df04367ff8db3b8d355da7ddb7f846287d6b6d68b4a33b8fda1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 23, 2026.
Transparency logRelease files / e_serde-0.2.1-cp314-cp314-manylinux_2_28_aarch64.whl
| Download URL | e_serde-0.2.1-cp314-cp314-manylinux_2_28_aarch64.whl |
|---|---|
| Size | 442.6 kB |
| Tags | CPython 3.14 Linux glibc 2.28+ ARM64 |
|
SHA-256 checksum How to use checksums |
1a56247b5fbfa7034a405f01fa559d591351dda26ff0a1716104f068eeefe739
|
|
BLAKE2b-256 checksum How to use checksums |
b0999991a83373941e5d9ae3acf2c67496250399bb92b04a82f4cc37633eff0c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 23, 2026.
Transparency logRelease files / e_serde-0.2.1-cp314-cp314-macosx_11_0_arm64.whl
| Download URL | e_serde-0.2.1-cp314-cp314-macosx_11_0_arm64.whl |
|---|---|
| Size | 411.5 kB |
| Tags | CPython 3.14 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
c2b5b81fcf1cf704affd911bb92cb84acbf5fe74b090d58d0cf6baf1ccd8634b
|
|
BLAKE2b-256 checksum How to use checksums |
481876f117adec69d29ecc3cadf94e72227b99377dbf7185103af1812ec67836
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 23, 2026.
Transparency logRelease files / e_serde-0.2.1-cp314-cp314-macosx_10_12_x86_64.whl
| Download URL | e_serde-0.2.1-cp314-cp314-macosx_10_12_x86_64.whl |
|---|---|
| Size | 436.0 kB |
| Tags | CPython 3.14 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
647cc694197651f5911b39d09fb66104bdf4088bf88462e4e7e1c39db3cbe8c6
|
|
BLAKE2b-256 checksum How to use checksums |
46a98f9083aa571b824a1a75be00a1cef57eea680254151aba4177505470da53
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 23, 2026.
Transparency log