Skip to main content

Drop-in JSON encoder for dataclasses, datetimes, Decimal, bytes, sets, exceptions, and custom repr

Project description

wireform

PyPI Python Tests License

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 for date, time, Decimal, bytes, set, frozenset, exceptions, and class objects.
  • Silently accepting naive datetime is a trap — the reader can't tell if the timestamp is UTC or local. wireform refuses naive datetimes by design.
  • dataclasses.asdict exists, but doesn't respect field(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 of json.load / json.loads: list, dict[str, ...], str, int, float, None.
  • Jsonable — anything the stdlib json.dumps accepts: JsonLoaded plus tuple, and dicts with int keys.
  • CustomJsonable — anything custom_dumps accepts: Jsonable plus set, 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

MIT

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

wireform-1.0.0.tar.gz (7.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

wireform-1.0.0-py3-none-any.whl (7.3 kB view details)

Uploaded Python 3

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

Hashes for wireform-1.0.0.tar.gz
Algorithm Hash digest
SHA256 5eac44abb4f17f7c4ea7432d9f305513f3c1ac902912d578a00146e1f74f86fc
MD5 bc7a09a8e2d1557625bac13329a32aa8
BLAKE2b-256 9a6c78b44336d959b4bd0adf729aa121ce9fb46b6774df10b138a07653281bb7

See more details on using hashes here.

Provenance

The following attestation bundles were made for wireform-1.0.0.tar.gz:

Publisher: publish-pypi.yml on miriada-io/wireform

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for wireform-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 668d0f9416fe0e242dea994e6bbb62c486e497e107507f3a1ea61c2e3389114a
MD5 7bd7d5011a9445e92ef6e58acc020d4f
BLAKE2b-256 8c281c47b387253f2ea1395676a340505b46ac1f922044c09b006960a77ecd0b

See more details on using hashes here.

Provenance

The following attestation bundles were made for wireform-1.0.0-py3-none-any.whl:

Publisher: publish-pypi.yml on miriada-io/wireform

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page