Drop-in JSON encoder for dataclasses, datetimes, Decimal, bytes, sets, exceptions, and custom repr
Project description
wireform
A json.dumps drop-in that serializes dataclasses, datetime, Decimal, bytes, sets, exceptions, and anything that implements __repr_in_dumps__.
Installation
pip install wireform
Requires Python 3.11+.
Why wireform?
The stdlib json.dumps refuses most real-world Python values:
json.dumps(MyDataclass(...))→TypeError— dataclasses are not serializable out of the box.json.dumps(datetime.now())→TypeError; same fordate,time,Decimal,bytes,set,frozenset, exceptions, and class objects.- Silently accepting naive
datetimeis a trap — the reader can't tell if the timestamp is UTC or local.wireformrefuses naive datetimes by design. dataclasses.asdictexists, but doesn't respectfield(repr=False)and can't coexist with custom encoders for other types.
custom_dumps is a single callable that handles all of these without schemas, models, or configuration.
Quick Start
import datetime
from dataclasses import dataclass
from decimal import Decimal
from wireform import custom_dumps
@dataclass
class Payment:
amount: Decimal
currency: str
paid_at: datetime.datetime
payment = Payment(
amount=Decimal("19.99"),
currency="EUR",
paid_at=datetime.datetime(2026, 4, 18, 12, 0, tzinfo=datetime.UTC),
)
custom_dumps(payment)
# '{"amount": "19.99", "currency": "EUR", "paid_at": "2026-04-18T12:00:00+00:00"}'
custom_dumps({"tags": {"admin", "editor"}, "blob": b"hello"})
# '{"tags": ["admin", "editor"], "blob": "data:application/octet-stream;base64,aGVsbG8="}'
# Naive datetime — refused by design:
custom_dumps(datetime.datetime.now())
# TypeError: datetime.datetime WITHOUT tzinfo is not JSON serializable
Overview
Serialization: custom_dumps | EnhancedJSONEncoder | ReprInDumps
File I/O: read_json_file_by_path
Types: JsonLoaded | Jsonable | CustomJsonable | JsonDumper
Serialization
custom_dumps
A partial(json.dumps, cls=EnhancedJSONEncoder). Same signature as json.dumps, same return type (str), but accepts the extended set of Python values listed below.
from wireform import custom_dumps
custom_dumps({"a": 1, "b": [1, 2, 3]})
# '{"a": 1, "b": [1, 2, 3]}'
Supported values beyond stdlib JSON:
| Python value | JSON representation |
|---|---|
@dataclass instance |
object of fields with repr=True (skips ClassVar, InitVar, repr=False) |
datetime (tz-aware) |
ISO 8601 string — "2026-04-18T12:00:00+00:00" |
datetime (naive) |
raises TypeError |
date, time |
ISO 8601 string |
Decimal |
string — "19.99" |
bytes |
data URL — "data:application/octet-stream;base64,..." |
set, frozenset |
JSON array (unordered) |
type (class object) |
{"_t": "PY::class", "key": "<qualname>"} |
Exception instance |
{"_t": "PY::Exception", "key": "<type qualname>", "args": [...]} |
ReprInDumps subclass |
value returned by __repr_in_dumps__() |
EnhancedJSONEncoder
The underlying json.JSONEncoder subclass. Pass it to json.dumps(..., cls=EnhancedJSONEncoder) if you need the extra dumps kwargs directly rather than through custom_dumps.
import json
from wireform import EnhancedJSONEncoder
json.dumps({"x": {1, 2, 3}}, cls=EnhancedJSONEncoder, indent=2)
ReprInDumps
Mixin that lets a class control its own JSON form. Subclass it and either (a) override __repr_in_dumps__ to return any Jsonable value, or (b) rely on the default, which delegates to repr(self) and produces a JSON string.
from wireform import ReprInDumps, custom_dumps
class Tag(ReprInDumps):
def __init__(self, name: str):
self.name = name
def __repr__(self):
return f"Tag({self.name!r})"
custom_dumps(Tag("admin"))
# '"Tag(\'admin\')"'
class Version(ReprInDumps):
def __init__(self, major: int, minor: int):
self.major = major
self.minor = minor
def __repr_in_dumps__(self):
return {"version": f"{self.major}.{self.minor}"}
custom_dumps(Version(1, 2))
# '{"version": "1.2"}'
File I/O
read_json_file_by_path
Open a file at the given path and parse it as JSON.
from wireform import read_json_file_by_path
data = read_json_file_by_path("config.json")
Returns a JsonLoaded — the exact tree shape you get from json.load.
Type Aliases
JsonLoaded— the strict output shape ofjson.load/json.loads:list,dict[str, ...],str,int,float,None.Jsonable— anything the stdlibjson.dumpsaccepts:JsonLoadedplustuple, and dicts withintkeys.CustomJsonable— anythingcustom_dumpsaccepts:Jsonableplusset,frozenset,bytes,ReprInDumps,type, dataclasses,datetime,date,time,Decimal,Exception.
JsonDumper
typing.Protocol matching the json.dumps signature. Use it when you want to type-hint a "something that behaves like json.dumps" parameter (e.g. to let callers swap custom_dumps for the stdlib version).
from wireform import JsonDumper, custom_dumps
def render(data, *, dumper: JsonDumper = custom_dumps) -> str:
return dumper(data, indent=2)
A Note on Roundtripping
custom_dumps is a one-way encoder. Values like set, bytes, Decimal, class objects and exceptions are encoded to JSON-compatible forms, but json.loads will give you back the encoded shape (list, str, etc.), not the original Python type. Use wireform when you need to emit JSON — not when you need a serializer/deserializer round-trip.
License
Project details
Release history Release notifications | RSS feed
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 wireform-1.0.0.tar.gz.
File metadata
- Download URL: wireform-1.0.0.tar.gz
- Upload date:
- Size: 7.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5eac44abb4f17f7c4ea7432d9f305513f3c1ac902912d578a00146e1f74f86fc
|
|
| MD5 |
bc7a09a8e2d1557625bac13329a32aa8
|
|
| BLAKE2b-256 |
9a6c78b44336d959b4bd0adf729aa121ce9fb46b6774df10b138a07653281bb7
|
Provenance
The following attestation bundles were made for wireform-1.0.0.tar.gz:
Publisher:
publish-pypi.yml on miriada-io/wireform
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
wireform-1.0.0.tar.gz -
Subject digest:
5eac44abb4f17f7c4ea7432d9f305513f3c1ac902912d578a00146e1f74f86fc - Sigstore transparency entry: 1349007734
- Sigstore integration time:
-
Permalink:
miriada-io/wireform@fae8d3b463a767b9d794528085aed37cf1f65a50 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/miriada-io
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@fae8d3b463a767b9d794528085aed37cf1f65a50 -
Trigger Event:
release
-
Statement type:
File details
Details for the file wireform-1.0.0-py3-none-any.whl.
File metadata
- Download URL: wireform-1.0.0-py3-none-any.whl
- Upload date:
- Size: 7.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
668d0f9416fe0e242dea994e6bbb62c486e497e107507f3a1ea61c2e3389114a
|
|
| MD5 |
7bd7d5011a9445e92ef6e58acc020d4f
|
|
| BLAKE2b-256 |
8c281c47b387253f2ea1395676a340505b46ac1f922044c09b006960a77ecd0b
|
Provenance
The following attestation bundles were made for wireform-1.0.0-py3-none-any.whl:
Publisher:
publish-pypi.yml on miriada-io/wireform
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
wireform-1.0.0-py3-none-any.whl -
Subject digest:
668d0f9416fe0e242dea994e6bbb62c486e497e107507f3a1ea61c2e3389114a - Sigstore transparency entry: 1349007846
- Sigstore integration time:
-
Permalink:
miriada-io/wireform@fae8d3b463a767b9d794528085aed37cf1f65a50 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/miriada-io
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@fae8d3b463a767b9d794528085aed37cf1f65a50 -
Trigger Event:
release
-
Statement type: