LEPTRIS

Teptris — the TOML package v0.2.23

A native C-extension binding (no ctypes, no fallback) over libteptris v0.1.27. The API shape mirrors tomllib/tomli — loads/load raising TOMLDecodeError — plus a dumps writer in the tomli_w spirit. Datetimes follow the tomllib mapping.

Installation

bash
pip install teptris

pip resolves a wheel for your platform — manylinux/musllinux x86_64, aarch64 and armv7l (32-bit ARM, built under qemu), macOS arm64 and x86_64, Windows AMD64 and ARM64. CPython 3.10–3.14 gets a version-specific wheel (PGO-built, with the datetime C-API fast paths); everything else resolves thecp39-abi3 wheel, which covers every CPython ≥ 3.9 including future minors. The extension statically links the engine, so each wheel is a single self-contained module. Every other platform installs the sdist, which vendors the engine and compiles it with the installing interpreter’s compiler — a C compiler is required, and the build fails loudly without one.

Usage

python
import teptris

doc = teptris.loads('title = "teptris"\n[owner]\nname = "t"\n')
# => {'title': 'teptris', 'owner': {'name': 't'}}

teptris.dumps(doc)   # canonical TOML — the engine emitter is the
                     # single formatting source (3.5-14.2x tomli_w)

try:
    teptris.loads("a = [1,")
except teptris.TOMLDecodeError as e:
    e.lineno, e.colno   # the tomllib error attributes

Both directions are native. End to end (parse + materialize into Python objects) teptris is 4.8–6× rtoml (the rust-backed incumbent), 19–42× the stdlib’s tomllib, and hundreds of times tomlkit per shape.

The value contract

TOMLPython
offset datetimedatetime.datetime (aware)
local datetimedatetime.datetime (naive)
local datedatetime.date
local timedatetime.time
integer / float / string / bool / array / tablethe obvious Python types

On the version-specific wheels (CPython 3.10–3.14, and the free-threaded cp314t wheels) datetimes materialize through the C datetime API directly — no generic-call machinery — with one cached timezone per offset; measured −56% on datetime-heavy loads and−72% on dumps against the abi3 path.

Many documents — loads_batch

python
docs = teptris.loads_batch([toml_a, toml_b, toml_c])
# => [dict, dict, dict] — same datetime contract as loads()

# the first failing document raises TOMLDecodeError
# carrying its line/column

Lazy loading — loads_lazy

When most of the parsed tree is going to be ignored,loads_lazy returns a LazyNode wrapper that materializes host objects only along the paths actually accessed:

python
doc = teptris.loads_lazy(big_toml)
# one parse, no dicts/lists materialized yet
sku = doc["items"][0]["sku"].value()   # scalars materialize at .value()

for item in doc["items"]:              # arrays yield LazyNodes
    print(item["sku"].value())

flat = doc.to_dict()                   # flatten eagerly, loads()-shaped

LazyNode exposes kind(),value() (scalars only), len(), iteration (tables yield (key, LazyNode) pairs), subscripting, andto_dict()/to_list() for eager flattening. Datetimes materialize under the same contract as loads. The eager path is the floor for flatten-everything workloads; the lazy path wins for parse-once-touch-a-subset.

Planned-key materialization — Descriptor

teptris.Descriptor compiles a plan tree once, then materializes a whole document against it in one native pass — unplanned keys are never materialized (the twin of teptris-ruby's Teptris::Descriptor):

python
desc = teptris.Descriptor.build({"children": [
    {"name": "name", "kind": "scalar"},
    {"name": "port", "kind": "scalar"},
    {"name": "hosts", "kind": "collection"},          # array of scalars
    {"name": "items", "kind": "nested", "plan": {     # recurses; spans
        "children": [{"name": "id", "kind": "scalar"}]},  # arrays of tables
    {"name": "everything_else", "kind": "raw"},       # untouched subtree
]})
doc = desc.walk(toml_string)   # rows absent from the doc read as None

Keys not in the plan never appear;walk raises TOMLDecodeError withline/column like every other public path.

Typed from 0.2.26

The package ships py.typed (PEP 561) with a_native.pyi stub for the C surface — strict mypy/pyright consumers type-check out of the box, and a mypy gate runs in CI. teptris.engine_version() reports the linked libteptris version; teptris.loads_lazy_batch([...])is the lazy twin of loads_batch. The Ruby gem carries the matching contract as RBS signatures with a steep gate.

This is the curated guide. The repositories are canonical: when this page and the repo disagree, the repo wins. Full documentation lives with the source.