Skip to main content

ktav (Python)

PyPI CI License: MIT OR Apache-2.0 Playground

Python bindings for Ktav — a plain configuration format. JSON-shape, no required quotes, no commas, dotted keys. Powered by Rust under the hood.

Languages: English · Русский · 简体中文

Playground: convert JSON / YAML / TOML / INI ⇄ Ktav in your browser at ktav-lang.github.io.

Specification: this package implements Ktav. The format is versioned and maintained independently of this package — see ktav-lang/spec for the formal document.


Install

pip install ktav

The default backend is native and includes our bundled Rust core in a matching platform wheel. No Python runtime dependency is required. Wheels target Python 3.9+ on supported platforms.

If no wheel matches, install from source: the native extension is compiled with Rust through maturin, so a Rust toolchain and Python development headers are needed. Source installation does not switch to pure mode.

Pure Python

For a universal wheel, install the existing py3-none-any artifact explicitly:

pip install ./ktav-0.8.1-py3-none-any.whl

Then select the pure API directly:

from ktav import pure




doc = pure.loads("server.port: 8080")
text = pure.dumps({"server": {"port": 8080}})
formatted = pure.format(text)

The pure backend provides the full parser, writer, formatter, and Ktav errors using only the Python standard library. It does not require third-party packages and does not load _core, WASM, or invoke network or subprocesses. It is explicitly selected; the native default never silently falls back to it.

tomli may appear in test or development extras for tooling on older Python versions; it is not a runtime dependency.

from ktav import pure

config = pure.loads(open("config.ktav").read())
text = pure.dumps({"port": 8080})
formatted = pure.format(open("config.ktav").read())

Pure mode implements the full public operation set — loads, loads_strict, dumps, emit_canonical, dumps_force_strings, format, canonical_from_source (plus load/dump) — with the same ten-field error envelope, against spec 0.8.1. Prefer it when you have no Rust toolchain, when no wheel exists for your platform, for embedding, or when you want an implementation you can audit in pure Python. The honest tradeoff: pure mode runs at Python speed, not the native extension's — no speed claims are made either way. There is no global backend flag and no silent fallback: you choose a backend by choosing which module you import.

Since 0.8.1 the pure backend also ships as a real install artifact: the universal pure wheel ktav-0.8.1-py3-none-any.whl (same distribution name, same version) is published on PyPI alongside the native platform wheels. To install it, download/select the pure wheel (py3-none-any) from the release or PyPI files; pip prefers a matching platform wheel automatically when one exists. The pure wheel installs with no Rust toolchain, no compiler and no maturin, and provides exactly the ktav.pure backend — the native top-level functions then raise a clear ImportError pointing at from ktav import pure.

Quick start

Parse — read typed fields straight off the dict

import ktav

src = """
service: web
port: 8080
ratio: 0.75
tls: true
tags: [
    prod
    eu-west-1
]
db.host: primary.internal
db.timeout: 30
"""

cfg = ktav.loads(src)

service: str = cfg["service"]
port: int = cfg["port"]
ratio: float = cfg["ratio"]
tls: bool = cfg["tls"]
tags: list[str] = cfg["tags"]
db_host: str = cfg["db"]["host"]
db_timeout: int = cfg["db"]["timeout"]

Walk — dispatch on the runtime type

for k, v in cfg.items():
    if v is None:
        kind = "null"
    elif isinstance(v, bool):
        kind = f"bool={v}"  # bool first — True is also an int!
    elif isinstance(v, int):
        kind = f"int={v}"
    elif isinstance(v, float):
        kind = f"float={v}"
    elif isinstance(v, str):
        kind = f"str={v!r}"
    elif isinstance(v, list):
        kind = f"array({len(v)})"
    elif isinstance(v, dict):
        kind = f"object({len(v)})"
    print(f"{k} -> {kind}")

Build & render — construct a document in code

doc = {
    "name": "frontend",
    "port": 8443,
    "tls": True,
    "ratio": 0.95,
    "upstreams": [
        {"host": "a.example", "port": 1080},
        {"host": "b.example", "port": 1080},
    ],
    "notes": None,
}
text = ktav.dumps(doc)
# name: frontend
# port: 8443
# tls: true
# ratio: 0.95
# upstreams: [
#     { host: a.example  port: 1080 }
#     { host: b.example  port: 1080 }
# ]
# notes: null

A complete runnable version lives in examples/basic.py.

Format — normalise a file, keep the comments

import ktav

print(ktav.format(open("config.ktav").read()))

ktav.format rewrites a document in canonical form (spec § 5.9) while preserving every comment verbatim. Blank-line runs collapse to one and blank padding inside brackets is dropped, so formatting is a fixed point — safe to run in a pre-commit hook. Key order is never changed.

Three functions produce canonical output and they differ in what they accept and what they keep:

input comments blank lines
ktav.format(src) source text kept kept (runs collapse to one)
ktav.canonical_from_source(src) source text dropped dropped
ktav.emit_canonical(obj) a Python object none to keep none to keep

Pick by what you are holding: emit_canonical when you have an object, canonical_from_source when you have text and want no Python value built in between, format when you want the document's own comments and grouping to survive.

Unlike the JavaScript bindings, canonical_from_source buys you no extra numeric fidelity here — Python distinguishes int from float, so the Ktav Integer/Float distinction survives loads, and emit_canonical(loads(src)) produces the same bytes as canonical_from_source(src). Both spell a float by the canonical rule (§ 5.9.8) rather than by copying the source, so 1.23456789012345678901 becomes 1.2345678901234567 down either path.

ktav.dumps_force_strings(obj) renders like dumps but coerces every leaf scalar — integer, float, bool, null — to a String via the raw :: marker. Compounds keep their structure.

Four entry points mirror the standard library json module:

Function Purpose
ktav.loads(s) Parse a Ktav string (or UTF-8 bytes).
ktav.dumps(obj) Serialise a native Python value.
ktav.load(fp) Parse from a file-like object.
ktav.dump(obj, fp) Serialise to a file-like object.

load / dump accept both text-mode and binary-mode files.

For validation at trust boundaries, ktav.loads_strict(s) applies the specification's canonical-scalar rules and raises KtavDecodeError for a lossy scalar spelling. Canonical writer forms such as 1e-3 and 1e10 are accepted and produce the same native values as loads.

Type mapping

Ktav Python
null None
true / false bool
bare integer (fits signed 64-bit) int
integer beyond 64-bit str
bare decimal float
other scalar str
[ ... ] list
{ ... } dict

Ktav types numbers by lexical form — a bare port: 8080 is an int, ratio: 0.5 a float, and anything that isn't a bare number stays a str. Force a numeric-looking value to stay a string with :: (zip:: 01007).

The Integer domain matches the native extension's (signed 64-bit): an integer literal beyond i64 parses as a str, re-emitted via the raw :: marker so it stays a string — the same behaviour in both backends.

dict preserves insertion order (Python 3.7+ guarantee), matching the ordered-object semantics of Ktav.

Serialisation is the inverse:

  • Python int → bare integer (including arbitrary-precision bigints; reading such a literal back yields a str — see above).
  • Python float → bare decimal (decimal point always present; NaN / ±Infinity are rejected — Ktav does not represent them).
  • Python tuple is accepted as an array, for symmetry with list.
  • Non-str keys in a dict raise KtavEncodeError.

Key escaping

Since spec 0.6.4 a literal . or : inside a key segment is written with a backslash:

a\.b: v
a\:b: v
x.y\.z: v
a."b.c".d: v
"\u0041": v

A literal backslash in a key is \\. Since spec 0.7, a whole segment may instead use double quotes, single quotes or backticks. Quotes only delimit key segments, not values; \uXXXX decodes a Unicode code point.

Errors

import ktav

try:
    ktav.loads("x: [")
except ktav.KtavDecodeError as e:
    print("decode:", e)

try:
    ktav.dumps({"v": float("nan")})
except ktav.KtavEncodeError as e:
    print("encode:", e)

# Catching the base class catches either.
try:
    ktav.loads("a: 1\na: 2")
except ktav.KtavError:
    ...
Exception Raised by Base
KtavError (base) Exception
KtavDecodeError loads / load KtavError
KtavEncodeError dumps / dump KtavError

Both backends raise the same exceptions carrying the identical ten-field envelope. One deliberate delta: for a str input containing literal lone surrogates, pure mode raises KtavDecodeError with the error category InvalidUtf8 and a nonempty span over the malformed bytes, where the native extension surfaces a UnicodeEncodeError.

Since 0.7.1 every raised instance also carries the structured error envelope as attributes: error, reason, line, line_text, span, path, body, canonical, spec_section. Absent information is None, never a missing attribute. span is {"start": …, "end": …} byte offsets into the UTF-8 source; path is the list of exact decoded key segments (a key literally named a.b is one segment, never split). str(e) stays a human-readable message — the raw envelope is never substituted for it.

e.message carries that same text as an attribute, so str(e) == e.message. It is redundant here by design: this binding has always used the core's own rendering for str(e), and the attribute exists so the envelope has the same ten-field shape in Python that it has in every other language — where message is the only way to reach the text. Use it rather than rebuilding a sentence from error, line and body: a reassembled message differs between bindings, and this one does not.

Philosophy

Ktav is intentionally small. Its five design principles (from spec/CONTRIBUTING.md):

  1. Locality — a line's meaning does not depend on another line.
  2. One sentence — any new rule fits in one sentence of the spec.
  3. No whitespace sensitivity (line breaks aside).
  4. No magic types — the format never decides "8080" means a number.
  5. Explicit over clever — :: is verbose on purpose.

The Python bindings honour this: they add no schema inference, no auto-casting, no defaulting. If you want typing, you do it at the boundary with your own tool — pydantic, dataclasses, attrs — against the native Python structures this library returns.

Other Ktav implementations

  • spec — specification + conformance suite
  • rust — reference Rust crate (cargo add ktav); these Python bindings are a thin PyO3 wrapper around it
  • csharp — C# / .NET (dotnet add package Ktav)
  • golang — Go (go get github.com/ktav-lang/golang)
  • java — Java / JVM (io.github.ktav-lang:ktav on Maven Central)
  • js — JS / TS (npm install @ktav-lang/ktav)
  • php — PHP (composer require ktav-lang/ktav)

Two backends ship in this package. The native backend is the default: the top-level ktav API is a thin PyO3 wrapper around the Rust core (0.8.1). The pure backend, ktav.pure, is a complete parser and generator in pure Python — no compiled extension required. There is no global backend flag and no silent fallback: you choose a backend by importing it, and CI asserts byte-identical canonical output across both. The pure backend also ships as the universal pure wheel (ktav-0.8.1-py3-none-any.whl), published alongside the native platform wheels.

Versioning

This package follows Semantic Versioning with the pre-1.0 convention that a MINOR bump is breaking. The package version and the ktav crate version move together. ktav.__spec_version__ reports the Ktav format version this binding supports.

Development

See CONTRIBUTING.md for the dev setup, test layout, and the contribution workflow.

Support the project

The author has many ideas that could be broadly useful to IT worldwide — not limited to Ktav. Realizing them requires funding. If you'd like to help, please reach out at phpcraftdream@gmail.com.

License

MIT OR Apache-2.0. See LICENSE-MIT and LICENSE-APACHE.

Metadata

Release files for ktav 0.8.1

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

Source distribution (sdist)

Source distribution for ktav 0.8.1
File Size Uploaded Provenance
ktav-0.8.1.tar.gz 109.5 kB With provenance Details

Built distributions (wheels)

Table of built distributions (wheels) for ktav 0.8.1
File
ktav-0.8.1-py3-none-any.whl Python 3 none any Details
ktav-0.8.1-cp39-abi3-win_arm64.whl CPython 3.9 abi3 Windows ARM64 Details
ktav-0.8.1-cp39-abi3-win_amd64.whl CPython 3.9 abi3 Windows x86-64 Details
ktav-0.8.1-cp39-abi3-musllinux_1_2_x86_64.whl CPython 3.9 abi3 Linux musl 1.2+ x86-64 Details
ktav-0.8.1-cp39-abi3-musllinux_1_2_aarch64.whl CPython 3.9 abi3 Linux musl 1.2+ ARM64 Details
ktav-0.8.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.9 abi3 Linux glibc 2.17+ x86-64 Details
ktav-0.8.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.9 abi3 Linux glibc 2.17+ ARM64 Details
ktav-0.8.1-cp39-abi3-macosx_11_0_arm64.whl CPython 3.9 abi3 macOS 11.0+ ARM64 Details
ktav-0.8.1-cp39-abi3-macosx_10_12_x86_64.whl CPython 3.9 abi3 macOS 10.12+ x86-64 Details

Total release size: 3.5 MB

Release files / ktav-0.8.1.tar.gz

Download URL ktav-0.8.1.tar.gz
Size 109.5 kB
Tags Source
SHA-256 checksum
How to use checksums
d5f500c44877244c02a588a67df2e746fb6655915ce9fadec4001a717a8619dc
BLAKE2b-256 checksum
How to use checksums
a6446a271c1f1396aaac3267a35bfa245bf484e5db04ef76a83a7e9e0b89d9ed
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Oct 22, 2026.

Transparency log

Release files / ktav-0.8.1-py3-none-any.whl

Download URL ktav-0.8.1-py3-none-any.whl
Size 49.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8353dad79c233c15621f036336e154c21aa8d0cf2a64a9e299674b67f1984809
BLAKE2b-256 checksum
How to use checksums
6d097803866ef7d255f1a0299789b6226292f3fd806ef2d3c89b273171c26fa8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Oct 22, 2026.

Transparency log

Release files / ktav-0.8.1-cp39-abi3-win_arm64.whl

Download URL ktav-0.8.1-cp39-abi3-win_arm64.whl
Size 310.7 kB
Tags CPython 3.9 Windows ARM64 abi3
SHA-256 checksum
How to use checksums
9229323071b6af4e832a864bb23c2772a2113ceeaa940e338206915a522eaace
BLAKE2b-256 checksum
How to use checksums
785aa03f392b079aaaaa6be0ffe0747a76add03d0ab002ae255781ea71ca35e8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Oct 22, 2026.

Transparency log

Release files / ktav-0.8.1-cp39-abi3-win_amd64.whl

Download URL ktav-0.8.1-cp39-abi3-win_amd64.whl
Size 319.0 kB
Tags CPython 3.9 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
330eae8e1682ee58ebb9621d8906a2bf4f0b01dcdc0459c2c67f8fc3644ab206
BLAKE2b-256 checksum
How to use checksums
b46f5b07edd806dc03f073c8235b366560a14cf4918be782dba5e79a0b9f0ed2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Oct 22, 2026.

Transparency log

Release files / ktav-0.8.1-cp39-abi3-musllinux_1_2_x86_64.whl

Download URL ktav-0.8.1-cp39-abi3-musllinux_1_2_x86_64.whl
Size 607.8 kB
Tags CPython 3.9 Linux musl 1.2+ x86-64 abi3
SHA-256 checksum
How to use checksums
70f946254576c4e7dd84b34a6c5b3e3dcfdff66bb00318ff1566da30a1af2802
BLAKE2b-256 checksum
How to use checksums
6cab4e90fd74b37f4e7073243d4a4a4b2d79bb5004d7bb9bb76fb1074ea0dc3a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Oct 22, 2026.

Transparency log

Release files / ktav-0.8.1-cp39-abi3-musllinux_1_2_aarch64.whl

Download URL ktav-0.8.1-cp39-abi3-musllinux_1_2_aarch64.whl
Size 559.4 kB
Tags CPython 3.9 Linux musl 1.2+ ARM64 abi3
SHA-256 checksum
How to use checksums
ccdd70311c872bbf6ba2fa810afcc5d548aa12cd953a71b2920962923458ff1a
BLAKE2b-256 checksum
How to use checksums
b057e7a673e63ebe29cc58496f166333b862b453c7620abda7280f57701716c0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Oct 22, 2026.

Transparency log

Release files / ktav-0.8.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL ktav-0.8.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 395.9 kB
Tags CPython 3.9 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
87cab469e872dc7ebecea8d16adba20ae62833fc74ca853f2740dd66dacdb1d3
BLAKE2b-256 checksum
How to use checksums
ab990cead3299a3746d5de9cf579dcd365c0addf5ee10bcdbb221e49ca423d1f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Oct 22, 2026.

Transparency log

Release files / ktav-0.8.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL ktav-0.8.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 380.9 kB
Tags CPython 3.9 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
98a908319fe362fdfa6c57f6bdfeb98d8ca2ee4ffad42971b15f19b4ac589dc7
BLAKE2b-256 checksum
How to use checksums
b3cb33f0ec493f2ec80b66182adf01faf785a1794afb4bfe37c60d74dd663fad
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Oct 22, 2026.

Transparency log

Release files / ktav-0.8.1-cp39-abi3-macosx_11_0_arm64.whl

Download URL ktav-0.8.1-cp39-abi3-macosx_11_0_arm64.whl
Size 372.7 kB
Tags CPython 3.9 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
6d1c1190df3d1c9d6ae08c84fb44155c73086095c486bc3cf1eda3cbc424df0b
BLAKE2b-256 checksum
How to use checksums
023f84a785290f70b65a670283c8c0ecf217ad0f65933c7ac3c01e1c695ace8d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Oct 22, 2026.

Transparency log

Release files / ktav-0.8.1-cp39-abi3-macosx_10_12_x86_64.whl

Download URL ktav-0.8.1-cp39-abi3-macosx_10_12_x86_64.whl
Size 387.9 kB
Tags CPython 3.9 abi3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
b48d3abfff2158ae278dd901202c311b7c1174dc76ed2306049d0caa6867c78d
BLAKE2b-256 checksum
How to use checksums
26b977314f3591c8d5ba798ef0f5413e6713a880da2829c730793966077f6bff
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Oct 22, 2026.

Transparency log

Security for ktav 0.8.1

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.

Full provenance

All 10 files in this release have been uploaded with provenance metadata.

Select from 10 files

Attestation for ktav-0.8.1.tar.gz

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 Oct 22, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

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