Skip to main content

isojson

Fast JSON for CPython that works in per-interpreter-GIL sub-interpreters.

isojson is a Rust extension with an orjson-compatible API (dumps, loads, the same OPT_* flags, and byte-identical output for the types it supports). The difference is that it loads and runs in own-GIL sub-interpreters (PEP 684) with no compatibility override. orjson does not:

>>> from concurrent import interpreters
>>> interpreters.create().exec("import orjson")
ImportError: module orjson.orjson does not support loading in subinterpreters

>>> interpreters.create().exec("import isojson")   # works, strict mode, no override
pip install isojson

Wheels are published for CPython 3.14 on Linux (x86_64, aarch64), macOS (arm64, x86_64) and Windows (x86_64).

import isojson

isojson.dumps({"a": [1, 2.5, None]})          # b'{"a":[1,2.5,null]}'
isojson.loads(b'{"a":[1,2.5,null]}')           # {'a': [1, 2.5, None]}
isojson.dumps(obj, default=str, option=isojson.OPT_SORT_KEYS | isojson.OPT_INDENT_2)

Switching from orjson is usually a one-line change: import isojson as orjson. Check the differences first.


Pure Rust, and no worrying about threads or sub-interpreters

isojson is pure Rust. There is no C or C++ source anywhere in the dependency tree, and the build never runs a C compiler. The only native library it links is libpython itself. The runtime dependencies are pyo3-ffi (declarations of the CPython C API, no code), simd-json (with value-trait, halfbrown and simdutf8), zmij and itoa. The SIMD code (SSE2 on x86_64, NEON on aarch64) uses Rust's core::arch intrinsics.

Things you do not have to do with isojson:

  • set _override_multi_interp_extensions_check or any other escape hatch;
  • ship or load a physical copy of the extension per worker;
  • import it in the main interpreter first, or in any particular order;
  • keep calls on one thread, or add locks around them.

Import it in as many own-GIL sub-interpreters as you like, in strict mode, and call it from any thread.

Why that is safe: the only state shared across interpreters or threads is plain data, never a Python object:

shared state what it is
per-thread scratch buffers and size hints bytes and sizes only (thread_local!)
simd-json's CPU-feature detection an atomic set once per process
the str fast-path switch an atomic, set by an import-time self-check that gives the same result in every interpreter

Every Python object isojson keeps longer than one call lives in per-interpreter module state. That is the JSONDecodeError type, the dict-key cache, and the type cache (the datetime types, looked up in that interpreter's own sys.modules, never imported). CPython creates it for each interpreter and frees it with that interpreter.

How this is tested: strict import in 6 own-GIL sub-interpreters, with no override; 4 and 8 sub-interpreters running concurrently; 8 threads in one interpreter; threads and sub-interpreters running together; 200 create/use/destroy cycles. The whole suite also passes under PYTHONMALLOC=debug, on macOS (arm64) and Linux (x86_64). Every concurrent worker checks its own seeded data against its own expected answer, so a leak between threads or interpreters shows up as a wrong result, not just as a crash that may or may not happen.

Free-threaded CPython (3.14t): isojson works there, but it is not free-threading-ready yet. The module declares that it needs the GIL, so CPython re-enables the GIL when it is imported and prints a RuntimeWarning. We checked this on 3.14t. The str fast path is also off on those builds.


Why this exists

Python 3.12 added a separate GIL per sub-interpreter, and Python 3.14 made it usable from Python (concurrent.interpreters). N sub-interpreters in one process can run Python code on N cores, which lets a single process scale across cores without multiprocessing.

For that to work, every C extension a sub-interpreter imports has to be safe there. In practice this means:

  1. multi-phase initialization (PEP 489), so each interpreter gets its own module object;
  2. no Python objects stored in process-global state, since an object belongs to exactly one interpreter and touching it from another corrupts reference counts and allocator arenas;
  3. declaring Py_MOD_PER_INTERPRETER_GIL_SUPPORTED, which is only honest once 1 and 2 hold.

orjson is the fastest JSON library for CPython, but it does not meet these requirements and refuses to import in a sub-interpreter. That left people running sub-interpreters two options:

  • stdlib json: safe, but 2.7–13× slower than orjson depending on the payload (see the tables below);
  • a physical copy of orjson's shared library per worker, loaded under an override. This works, but you pay one copy's memory, disk, and load time per worker, plus a separate mechanism to maintain.

isojson is a JSON library written from the start to meet all three requirements.

How it works

  • Multi-phase init with per-interpreter state. The module declares Py_MOD_PER_INTERPRETER_GIL_SUPPORTED, which is honest only because nothing process-global holds a Python object (see above). The only process-global pointers isojson touches are CPython's static builtin types, _datetime's static types and C-API struct, and the immortal singletons None/True/False, which every interpreter shares by design.
  • The key cache is per interpreter. Like orjson, loads caches recently seen dict keys, so repeated keys reuse one str with its hash already computed. orjson keeps that cache process-wide. isojson keeps one per interpreter, because a shared cache would hand one interpreter's objects to another.
  • dumps borrows; only types are kept. It only borrows the objects it writes, for the length of the call, on the calling thread, under the caller's GIL; across calls it keeps only the per-interpreter type cache. When Python code could run during the walk (a default= callback, or a datetime's utcoffset()) and mutate a container, items are also held by a reference for that span. Output is written straight into the result bytes object, so there is no final copy. String escaping scans 16 bytes at a time: SSE2 on x86_64 and NEON on aarch64, both part of those architectures' baseline, so no runtime detection is needed.
  • str contents are read from the object header, after a self-check. Where CPython 3.14 already holds a string's UTF-8 (compact ASCII, or a cached UTF-8 copy), isojson reads it directly instead of calling PyUnicode_AsUTF8AndSize. That layout is not public API. So at import, isojson compares its reading with the API on probe strings and turns the fast path on only if every probe agrees. If a future CPython changes the layout, isojson gets slower but never wrong.
  • loads parses with simd-json and uses no recursion. simd-json turns the document into a flat tape where every array and object carries its length. isojson builds Python objects from the tape with an explicit stack, so lists are created at their exact size. A document nested 1024 deep is safe even on threads with small C stacks; deeper documents are rejected, as in orjson. simd-json never touches a Python object.
  • Exceptions belong to the interpreter. isojson.JSONDecodeError subclasses the calling interpreter's own json.JSONDecodeError. Its messages are plain sentences, never the parser's internal error names.
  • No PyO3. The module is written against the raw C API (pyo3-ffi). PyO3's high-level layer caches type objects and modules in process-global statics and rejects a second interpreter, which is exactly what this package exists to avoid.

Why simd-json, not sonic-rs

Both are fast, pure-Rust JSON parsers. We compared them directly on JSONTestSuite (318 cases, each run in its own child process so a crash is recorded), on edge cases that matter for a Python library, and on raw parse speed:

simd-json 0.18.1 sonic-rs 0.5.10
JSONTestSuite: must-accept rejected / must-reject accepted 0 / 0 0 / 0
JSONTestSuite: crashes 0 2 (stack overflow: n_structure_100000_opening_arrays, n_structure_open_array_object)
Deeply nested input depth limit 1024, then a clean error (same as orjson) no limit: 100,000 levels abort the process, which cannot be caught
-0 integer 0 (same as orjson) float 0.0
Integers beyond 64 bits, 1e400 float / error (same as orjson) same
Lone surrogate "\ud800" decoded as "\x00": bug, fixed by us (see below) error
API for building Python objects a tape where every container carries its length serde visitor (recursive) or its own DOM

Parse only, no Python objects, µs, Apple M5 Pro:

payload simd-json (tape) sonic-rs (Value) serde_json (Value)
floats ×10k 142 152 255
records ×100 18.6 18.9 100
records ×2000 360 336 1906
unicode/escapes ×200 28.0 19.8 96.5

Speed is a draw. The deciding factor is robustness. A JSON library inside a server must not let one request take the process down, and sonic-rs does exactly that on deeply nested input. simd-json's tape is also the shape isojson needs.

simd-json had one real bug for us: a lone high surrogate ("\ud800") was decoded as U+0000 instead of being rejected, so invalid input silently became different data. The cause was an old "0 means failure" sentinel that kept its value but lost its meaning in a 2023 refactor. We reported it (simd-lite/simd-json#481) and sent the fix with a regression test (#482). Until a release contains it, isojson pins our fork at that commit (see Cargo.toml), and isojson's own tests cover the case.

Feature comparison

isojson orjson json (stdlib)
Loads in own-GIL sub-interpreters (strict, no override) ✅ ❌ ImportError ✅
dict, list, tuple, str, int, float, bool, None ✅ ✅ ✅
Subclasses of str / int / dict / list ✅ ✅ ✅
default= callable ✅ ✅ ✅
OPT_INDENT_2, OPT_SORT_KEYS, OPT_APPEND_NEWLINE ✅ ✅ ~ (indent=, sort_keys=)
OPT_STRICT_INTEGER, OPT_PASSTHROUGH_SUBCLASS ✅ ✅ —
loads from bytes / bytearray / memoryview / str ✅ ✅ str/bytes only
Output bytes identical to orjson for the types above ✅ — ❌
datetime / date / time natively, with OPT_NAIVE_UTC, OPT_UTC_Z, OPT_OMIT_MICROSECONDS, OPT_PASSTHROUGH_DATETIME ✅ ✅ ❌
numpy arrays and scalars (OPT_SERIALIZE_NUMPY), incl. datetime64 ✅ (numpy ≥ 2) ✅ ❌
uuid.UUID, enum.Enum, dataclasses natively ❌ → default= ✅ ❌
Non-str dict keys (OPT_NON_STR_KEYS) ❌ raises ✅ ~ (coerced)
orjson.Fragment ❌ ✅ ❌
Integers beyond 64 bits in dumps ❌ (like orjson) ❌ ✅
NaN / ±Infinity in dumps null (like orjson) null NaN / Infinity
Python versions CPython 3.14 CPython 3.10+ all

Differences from orjson

These are all the known differences. Anything not listed produces the same result as orjson 3.12.0 (the test suite checks this, see Testing).

Where orjson crashes or writes wrong data

isojson writes what Python's own API says the value is: isoformat() for datetime / date / time (after the options are applied), and for datetime64 the meaning numpy's API defines (v × mult units since 1970, sub-µs floored to µs). What has no answer in that format is declined (next section). Each row has a regression test that proves both halves.

# Input orjson 3.12.0 isojson
DV-1 UTC offset with seconds or microseconds (+05:59:30, -00:00:01, New York before 1883 = -04:56:02) rounds to the minute without carrying: +05:60, -00:00 (sign lost), -04:56 isoformat()'s offset, exactly
DV-3 pytz datetime after arithmetic, not normalized the normalized offset on the un-normalized wall time (a different instant) dt.utcoffset()
DV-4a a tzinfo whose utcoffset() returns None an invented offset (+00:00 on macOS / x86_64 Linux, garbage such as +18:12 on aarch64 Linux) no offset: naive to Python
DV-4b utcoffset() raises (datetime) / is invalid (time) datetime: crashes (SIGSEGV) TypeError("datetime.utcoffset() raised …"), the exception as __cause__
DV-5 datetime64 NaT in ns "1677-09-21T00:12:43.145224" null
DV-6 NaT in W D h m "1970-01-01T00:00:00" null
DV-7 NaT in Y M s ms us, and generic NaT TypeError null
DV-8 multiplied units (M8[10ms], M8[2D]) crashes (unreachable!()) value × multiplier
DV-9 M8[M] before 1970 crashes / TypeError floor division (1969-12-01T00:00:00)
DV-10 M8[D…us] from 9999-12-30T22:00 to 9999-12-31 TypeError written
DV-11 values whose seconds overflow i64 (M8[m] 307445734561825861) a wrapped, wrong value declined
DV-12 datetime.time with tzinfo TypeError t.isoformat(): "01:00:00+00:00"
DV-13 generic-unit M8 holding a value TypeError: … unit: NaT (misnames it) declined
DV-14 ≥2-D M8 arrays with an element orjson's 1-D writer rejects malformed JSON, no error, even with default ([[,[…]]) per the rows above
DV-15 utcoffset() returns a non-timedelta or ≥ 24 h an invented or garbage offset TypeError, as dt.utcoffset() raises
DV-16 datetime64 in ps, fs, as TypeError, even with default floored to µs
DV-17 a time whose microsecond has five digits (time(0, 0, 1, 75652)) drops the leading zero: "00:00:01.75652" "00:00:01.075652"

Declined numpy objects

A numpy object isojson can't write — a non-C-contiguous or non-native-endian array, a 0-d array, an unsupported dtype, a generic-unit value, an unrepresentable datetime64 — goes whole to default= when one is given. A decline found mid-array rolls the output back first, so default gets the array, not a half-written one. Without default, dumps raises orjson's message for the reason, with a note (add_note) carrying the raw evidence (dtype.str, flags, shape, value). orjson sends some of these to default too, but raises for non-native-endian arrays and out-of-range datetime64 even with a default. Unrecognized numpy scalars (complex128, longdouble) take the ordinary default path. Unaligned C-contiguous arrays (np.frombuffer(…, offset=1)) are read correctly (orjson's typed-slice read is undefined behaviour there).

Kept from 0.1

  • uuid.UUID, enum.Enum, dataclasses and orjson.Fragment go to default= (or raise Type is not JSON serializable).
  • OPT_NON_STR_KEYS raises TypeError: isojson does not support OPT_NON_STR_KEYS, rather than being silently ignored. OPT_PASSTHROUGH_DATACLASS is accepted and does nothing (isojson never serializes dataclasses); OPT_SERIALIZE_DATACLASS and OPT_SERIALIZE_UUID are 0, as in orjson.
  • default=None means "no default". orjson calls None and raises Type is not JSON serializable with a 'NoneType' object is not callable cause.
  • Argument errors (dumps() with no object, unknown keywords) have different wording.
  • loads error messages differ. The exception type (JSONDecodeError, a subclass of json.JSONDecodeError and ValueError) and .pos / .lineno / .colno match. The wording of .msg does not always match orjson's.

Limitations

  • CPython 3.14 only. Per-interpreter GIL arrived in 3.12, but 3.12's and 3.13's own _datetime isn't usable from concurrent strict sub-interpreters, so isojson targets 3.14.
  • Free-threaded builds (3.14t): the module declares that it needs the GIL, so CPython re-enables it on import (see above).
  • numpy: OPT_SERIALIZE_NUMPY is tested with numpy ≥ 2. isojson reads arrays through __array_struct__ and never imports numpy; it uses the numpy the calling interpreter already has in sys.modules.

Performance

Reproduce with python bench/bench.py. Each cell is the median of repeated runs. Parallel cells time only the work loop: interpreter or process creation and imports happen before timing starts. The single-interpreter and datetime/numpy tables are isojson 0.2.0; the parallel tables were measured with 0.1.0 (0.2's per-call dumps is within 2–7% of 0.1's).

macOS arm64: Apple M5 Pro, 18 cores, CPython 3.14.7, orjson 3.12.0

Parallel, the case isojson is built for. Each worker does 3,000 round trips (loads(dumps(doc))) of a 100-record document. Throughput is in round trips per second; higher is better.

setup N=1 N=2 N=4 N=8 scaling 1→8
isojson, N own-GIL sub-interpreters, one process 15,162 31,731 60,705 114,405 7.55×
json (stdlib), N own-GIL sub-interpreters 4,019 8,277 14,230 26,679 6.64×
orjson, N own-GIL sub-interpreters ✗ ✗ ✗ ✗ —
orjson, N threads, one interpreter (shared GIL) 16,277 20,206 18,185 21,703 1.33×
isojson, N threads, one interpreter (shared GIL) 18,160 17,932 18,270 19,076 1.05×
orjson, N processes (multiprocessing) 22,773 42,917 80,230 149,056 6.55×
isojson, N processes (multiprocessing) 19,286 36,307 64,255 123,112 6.38×

✗ ImportError: module orjson.orjson does not support loading in subinterpreters

How to read this:

  • In one process, isojson on 8 sub-interpreters does about 5× the best orjson can do (114k vs 22k round trips/s here; 4.9–5.3× across our runs). Adding threads to orjson gains almost nothing, because every thread shares one GIL.
  • isojson on sub-interpreters does 4.3× stdlib json on sub-interpreters, and stdlib json was the only other option that works there.
  • Sub-interpreters get close to process-level scaling in one process: isojson reaches 114k round trips/s on 8 sub-interpreters vs 123k on 8 processes.
  • orjson on 8 processes is still faster (149k) because orjson is faster per call. If you already run multiprocessing and never use sub-interpreters, orjson remains the faster choice.

Single interpreter, per-call cost (lower is better):

dumps isojson orjson json (stdlib) isojson / orjson
small (27 B) 49 ns 43 ns 567 ns 1.14×
records ×100 18.04 µs 15.83 µs 145.72 µs 1.14×
records ×2000 342.46 µs 279.09 µs 2.73 ms 1.23×
floats ×10k 95.87 µs 188.74 µs 2.21 ms 0.51×
unicode/escapes ×200 12.43 µs 9.97 µs 107.87 µs 1.25×
loads isojson orjson json (stdlib) isojson / orjson
small (27 B) 101 ns 70 ns 553 ns 1.44×
records ×100 49.48 µs 38.35 µs 101.08 µs 1.29×
records ×2000 1.03 ms 777.99 µs 2.02 ms 1.32×
floats ×10k 195.54 µs 162.60 µs 1.06 ms 1.20×
unicode/escapes ×200 63.99 µs 42.94 µs 118.75 µs 1.49×

datetime and numpy (dumps; python bench/bench.py types):

payload isojson orjson isojson / orjson
2,000 records × 3 datetimes (naive, +08:00, UTC) 182.90 µs 236.75 µs 0.77×
numpy float64 × 1M 14.89 ms 22.45 ms 0.66×
numpy float64 1000 × 1000 14.93 ms 21.76 ms 0.69×
numpy int64 × 1M 7.30 ms 6.77 ms 1.08×
10k numpy float64 scalars 204.75 µs 288.92 µs 0.71×
10k numpy int64 scalars 115.96 µs 155.99 µs 0.74×

Aware datetimes are faster than orjson's because the offset comes from one tzinfo.utcoffset(dt) call, checked the way CPython checks it, while orjson probes the tzinfo's attributes first. float64 arrays and scalars share the float writer that makes float-heavy documents fast.

In summary, on this machine: dumps is 1.1–1.25× orjson's time on plain JSON, about 2× faster on float-heavy documents, and faster on datetimes and numpy. loads is 1.2–1.5× slower than orjson. Both are 1.5–23× faster than stdlib json.

The remaining loads gap is mostly fixed per-call cost around simd-json: the input is copied (simd-json unescapes in place, and bytes are immutable), and a fresh tape is allocated on every call. Both are next on the list.

Linux x86_64: AMD Ryzen 7 7840HS, 8 cores / 16 threads, CPython 3.14.7, orjson 3.12.0

Run on a shared box with light background load (1-minute load average 6.0 at the start, 3.9 at the end).

setup N=1 N=2 N=4 N=8 scaling 1→8
isojson, N own-GIL sub-interpreters, one process 14,129 27,887 54,893 78,242 5.54×
json (stdlib), N own-GIL sub-interpreters 3,862 7,677 15,154 19,973 5.17×
orjson, N own-GIL sub-interpreters ✗ ✗ ✗ ✗ —
orjson, N threads, one interpreter (shared GIL) 19,934 18,492 17,668 17,537 0.88×
isojson, N threads, one interpreter (shared GIL) 13,890 12,388 13,034 12,301 0.89×
orjson, N processes (multiprocessing) 19,651 38,657 74,130 98,140 4.99×
isojson, N processes (multiprocessing) 14,122 27,388 54,055 75,194 5.32×

Here isojson on 8 sub-interpreters does 3.9× the best orjson manages in one process, and 3.9× stdlib json on the same sub-interpreters. It matches isojson on 8 processes (78k vs 75k). orjson on 8 processes is again the fastest row (98k).

dumps isojson orjson json (stdlib) isojson / orjson
small (27 B) 79 ns 70 ns 793 ns 1.14×
records ×100 24.73 µs 17.19 µs 148.74 µs 1.44×
records ×2000 660.35 µs 336.80 µs 3.37 ms 1.96×
floats ×10k 164.61 µs 163.90 µs 2.81 ms 1.00×
unicode/escapes ×200 14.44 µs 11.82 µs 92.46 µs 1.22×
loads isojson orjson json (stdlib) isojson / orjson
small (27 B) 130 ns 114 ns 848 ns 1.14×
records ×100 69.21 µs 47.23 µs 130.90 µs 1.47×
records ×2000 1.42 ms 989.92 µs 2.72 ms 1.43×
floats ×10k 262.10 µs 166.52 µs 1.40 ms 1.57×
unicode/escapes ×200 78.41 µs 49.52 µs 147.87 µs 1.58×

datetime and numpy (dumps):

payload isojson orjson isojson / orjson
2,000 records × 3 datetimes (naive, +08:00, UTC) 286.39 µs 277.75 µs 1.03×
numpy float64 × 1M 18.04 ms 17.84 ms 1.01×
numpy float64 1000 × 1000 17.95 ms 17.66 ms 1.02×
numpy int64 × 1M 9.53 ms 9.30 ms 1.02×
10k numpy float64 scalars 281.20 µs 274.20 µs 1.03×
10k numpy int64 scalars 176.61 µs 218.76 µs 0.81×

Per call, x86_64 is harder on isojson than arm64. dumps of large record documents is 2.0× slower than orjson here, against 1.2× on the Mac, and loads is 1.1–1.6× slower; datetimes and numpy are at parity. Both are still 1.9–19× faster than stdlib json. Floats are at parity since 0.2, which writes them in place in the output: 0.1 formatted each into a stack buffer and copied it out, which x86_64 can't store-forward (floats in [0, 1) or integral floats took up to twice orjson's time).

Across both machines

On 8 own-GIL sub-interpreters in one process, isojson does 4–5× the best orjson can do in a single process (3.9× on Linux x86_64, 5.3× on macOS arm64), and about 4× stdlib json. orjson on multiprocessing stays faster per core on both.

Testing

pip install maturin
pip install -e ".[test]"      # pytest, orjson==3.12.0, numpy>=2, pytz, tzdata
maturin develop --release
pytest tests
cargo test --lib              # the pure Rust core
  • Parity with orjson (tests/test_parity.py):
    • randomized documents (control characters, non-BMP text, 64-bit edges, random float bit patterns) compared byte-for-byte against orjson under every supported option combination;
    • 250,000 floats compared bit-exactly;
    • error types and messages for every dumps failure mode;
    • the recursion and default depth limits;
    • loads accept/reject behaviour on edge-case documents.
  • JSON conformance (tests/test_conformance.py): all 318 cases of JSONTestSuite. Every must-accept document parses, every must-reject document raises JSONDecodeError, and nothing crashes. The suite runs in a child process, so a crash is reported as a failure.
  • Multi-interpreter safety (tests/test_subinterp.py):
    • strict import in 6 own-GIL sub-interpreters with no override;
    • per-interpreter module state and exception types;
    • 4 and 8 interpreters running concurrently, each on its own seeded data with its own expected answer, with a Python default= callback running inside the serializer on every call. A leak between interpreters would show up as a wrong answer, not just a possible crash;
    • 200 create/use/destroy cycles in a child process, so a crash is reported instead of swallowed.
  • Threads (tests/test_threads.py): 8 threads in one interpreter, and 4 threads plus 4 sub-interpreters at the same time, each worker on its own seeded data with its own expected answer.
  • datetime and numpy (0.2):
    • byte parity with orjson for every datetime option combination, numpy dtypes × shapes × default × OPT_INDENT_2, and random documents (tests/test_parity_types.py);
    • one regression per difference, proving orjson's failure, the Python-API reference, and isojson's match; the crash rows run in child processes (tests/test_divergence.py);
    • the Python API as the oracle: isoformat() / fromisoformat() round trips, and datetime64 in every unit × multiplier across the i64 range against the exact integer meaning (tests/test_stdlib_oracle.py);
    • every decline rule, rollback, notes, and unaligned arrays (tests/test_declines.py); all 65,536 float16 values bit-exactly (tests/test_f16.py); a replaced sys.modules["numpy"] (tests/test_numpy_swap.py);
    • reentrancy: utcoffset() mutating the container being written, under PYTHONMALLOC=debug (tests/test_reentrancy.py); concurrent sub-interpreters writing datetimes (tests/test_concurrency_dt.py);
    • no process-global Python state: every static reviewed, banned symbols, cached types traversed by the GC (tests/test_no_global_pyobject.py).
  • Per-worker numpy in Pyronova: Pyronova's tests/test_isojson_numpy_workers.py runs 4 workers, each with its own numpy copy, against orjson's bytes.

The suite passes on macOS arm64 and Linux x86_64, both normally and under PYTHONMALLOC=debug. The -X dev / PYTHONDEVMODE=1 run is on Linux.

Status

Version 0.2.0: native datetime / date / time and numpy. The API is stable (it is orjson's). UUID, Enum and dataclass support is next.

License

Apache-2.0.

Third-party: simd-json (Apache-2.0 OR MIT); the float16 → float32 conversion is half-rs's f16_to_f32_fallback as shipped in orjson (Apache-2.0 OR MIT, attribution in src/float.rs); tests/data/JSONTestSuite is from nst/JSONTestSuite (MIT, license included in that directory).

Release files for isojson 0.2.0

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

Source distribution (sdist)

Source distribution for isojson 0.2.0
File Size Uploaded
isojson-0.2.0.tar.gz 167.2 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for isojson 0.2.0
File
isojson-0.2.0-cp314-cp314-win_amd64.whl CPython 3.14 CPython 3.14 Windows x86-64 Details
isojson-0.2.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.14 CPython 3.14 Linux glibc 2.17+ x86-64 Details
isojson-0.2.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.14 CPython 3.14 Linux glibc 2.17+ ARM64 Details
isojson-0.2.0-cp314-cp314-macosx_11_0_arm64.whl CPython 3.14 CPython 3.14 macOS 11.0+ ARM64 Details
isojson-0.2.0-cp314-cp314-macosx_10_12_x86_64.whl CPython 3.14 CPython 3.14 macOS 10.12+ x86-64 Details

Total release size: 1.4 MB

Release files / isojson-0.2.0.tar.gz

Download URL isojson-0.2.0.tar.gz
Size 167.2 kB
Tags Source
SHA-256 checksum
How to use checksums
2d3c7b598fe004a498d81fa047de0505c3d5f15b0c57f624667e720b191a8c9b
BLAKE2b-256 checksum
How to use checksums
a5d0e1d399625c745538e80cbb4fc5a775daf8a810c69fc654dd5d2636d8f04c
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 25, 2026.

Transparency log

Release files / isojson-0.2.0-cp314-cp314-win_amd64.whl

Download URL isojson-0.2.0-cp314-cp314-win_amd64.whl
Size 170.8 kB
Tags CPython 3.14 Windows x86-64
SHA-256 checksum
How to use checksums
facb9916764a7cabb90bbad4a7c93511f7dc53e99a8327914afe5f33543d209d
BLAKE2b-256 checksum
How to use checksums
c7c0d3b5b58bd38a5bcfdcaef4c1ab310bc9c446fc6d19aa05120e9a152cb268
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 25, 2026.

Transparency log

Release files / isojson-0.2.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL isojson-0.2.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 279.7 kB
Tags CPython 3.14 Linux glibc 2.17+ x86-64
SHA-256 checksum
How to use checksums
f8e8dede554528a323953f4c792a63b3d3c7c479c478cdc05442a26a129412ae
BLAKE2b-256 checksum
How to use checksums
70607d2a0d275f8e6e62e2013bbcb68c1e15f71eb41da2b31d4aa44ec2c445e3
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 25, 2026.

Transparency log

Release files / isojson-0.2.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL isojson-0.2.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 268.6 kB
Tags CPython 3.14 Linux glibc 2.17+ ARM64
SHA-256 checksum
How to use checksums
ee8fd5ca19f364c04f22d1f111d5952e3c33b1e8289f1c80feead91ace1e8439
BLAKE2b-256 checksum
How to use checksums
a1e36559323b51af97d4fff5a2c5404c0ca12956ef7decd644342d5e01cc4604
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 25, 2026.

Transparency log

Release files / isojson-0.2.0-cp314-cp314-macosx_11_0_arm64.whl

Download URL isojson-0.2.0-cp314-cp314-macosx_11_0_arm64.whl
Size 249.8 kB
Tags CPython 3.14 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
8a6cd35d3629f5c997f91ba848a99b27890257b3e6822cf45f0911fd535d6798
BLAKE2b-256 checksum
How to use checksums
34682d7e4ddf989856bca3d0bb751be88191a9be001117f82381f6bfac316305
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 25, 2026.

Transparency log

Release files / isojson-0.2.0-cp314-cp314-macosx_10_12_x86_64.whl

Download URL isojson-0.2.0-cp314-cp314-macosx_10_12_x86_64.whl
Size 271.9 kB
Tags CPython 3.14 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
fd524f9a68cf34075d4264823351ca6f4e67afe8ba33b995e0eb860e3ac7345d
BLAKE2b-256 checksum
How to use checksums
705a30db6a31120a26edb27b77224e7623dc4c1f938342ab753712f16e953abe
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 25, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.0 This release

6 release files

0.1.0

16 release files

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