PyTOON is a Python port of the excellent toon project by Johann Schopplich. It converts arbitrary Python data structures into the same concise, human-readable text representation produced by the original TypeScript encoder.
The goal of this repository is feature parity with the upstream project while providing a first-class experience for Python applications and tooling.
Warning
This repository is deprecated. Once https://github.com/toon-format/toon-python is released, use that package instead of this repository. No further updates will be made here.
- Features
- Installation
- Quick Start
- Normalization Reference
- Encoding Behaviour
- Options
- Advanced Usage
- Testing
- Project Structure
- Versioning & Compatibility
- Contributing
- License
- Normalises native Python types (dicts, lists, dataclasses, sets,
datetime, numeric edge cases, etc.) to a JSON-like value space before encoding. - Produces the familiar Toon text format with support for inline arrays, tabular object arrays, and deeply nested structures.
- Configurable indentation, row delimiter (
',','|','\t'), and optional length marker flag ([#N]style headers). - Safe string quoting and escaping rules that match the upstream implementation.
- Pure-Python, dependency-free package targeting Python 3.8+.
pip install pytoon-encoderWorking from a clone of this repository? Install in editable mode:
pip install -e .from pytoon import encode
payload = {
"user": {
"id": 123,
"name": "Ada",
"tags": ["reading", "gaming"],
"active": True,
"prefs": [],
}
}
print(encode(payload))Output:
user:
id: 123
name: Ada
tags[2]: reading,gaming
active: true
prefs[0]:
PyTOON first normalises values into a JSON-compatible shape. The table below mirrors the upstream Toon rules:
| Python value | Normalised result | Notes |
|---|---|---|
None |
None |
Encodes as null. |
bool, int, float |
Same numeric/boolean value | Floats that are NaN, ±inf become None; -0.0 becomes 0. |
str |
Same string | Subject to quoting/escaping rules during encoding. |
datetime, date |
ISO-8601 string | Uses .isoformat(). |
set, frozenset |
List of normalised elements | Order is the iteration order of the set. |
list, tuple, other sequences (not bytes) |
List of normalised elements | Preserves order. |
dict, Mapping |
Dict with stringified keys | Keys are coerced with str(key); values normalised recursively. |
dataclass instances |
Dict via dataclasses.asdict |
Deep conversion, then recurses. |
| Unsupported objects, functions, generators | None |
Matches behaviour of upstream encoder. |
PyTOON selects the most legible format while preserving structure:
- Objects render as
key: valuelines in insertion order. Empty objects emit justkey:. - Arrays of primitives appear inline:
tags[3]: a,b,c. - Arrays of objects become tabular if every object shares identical primitive keys. Otherwise, items are rendered as
-list entries. - Arrays of arrays with primitive inner arrays become nested lists; mixed or non-primitive inner arrays fall back to expanded list items.
- Mixed arrays (primitives, objects, arrays combined) always degrade to list entries.
- Strings remain unquoted only if they contain no structural characters, delimiters, leading/trailing whitespace, or ambiguous literals (
"true","42", etc.). Otherwise, they are quoted and escaped.
See examples/basic_usage.py for ready-to-run scenarios.
encode accepts an optional EncodeOptions dictionary that mirrors the TypeScript API:
from pytoon import encode, DELIMITERS
encode(
{"items": [{"id": 1, "name": "Ada"}, {"id": 2, "name": "Bob"}]},
{
"indent": 4,
"delimiter": DELIMITERS["pipe"], # or ',' / '\t'
"length_marker": "#", # renders headers as [#N]
},
)Output:
items[#2|]{id|name}:
1|Ada
2|Bob
Option reference:
| Option | Type | Default | Description |
|---|---|---|---|
indent |
int |
2 |
Spaces per indentation level for nested structures. |
delimiter |
',', `' |
', '\t'` |
',' |
length_marker |
'#' or False |
False |
When '#', emits headers like [#3]; helpful when the consumer expects marked lengths. |
- Pre-normalisation: Import
normalize_valuefrompytoon.normalizeto convert data once, then reuse the normalised structure across multiple encodings or transport layers. - Direct value encoding:
pytoon.encoders.encode_valueaccepts a pre-normalised JSON value plus resolved options, allowing integration with custom line writers or alternative indentation strategies. - Custom delimiters: The
DELIMITERSconstant exposes the supported delimiter characters. Pass alternate values (e.g.DELIMITERS["tab"]) to match TSV-style pipelines. - Examples: The
examples/directory contains scripts that highlight default behaviour and option combinations. Extend these for domain-specific onboarding material. - Automation:
.github/workflows/ci.ymlruns unit tests on every push and publishes tagged releases (v*) to PyPI once tests pass.
Tests use Python's built-in unittest framework:
python3 -m unittest discover -s testsWhen porting new behaviour from toon, add corresponding tests here to keep parity strong.
pytoon/– Core encoder modules (constants,normalize,primitives,encoders,writer).tests/– Unit tests covering primitives, objects, arrays, options, and edge cases.examples/– Runnable scripts that demonstrate practical usage patterns.
PyTOON targets Python 3.8+ and strives to remain aligned with the latest upstream toon behaviour. Breaking format changes follow upstream and will be clearly documented in release notes.
Contributions are welcome! To get started:
- Fork and clone the repository.
- Create a virtual environment for Python 3.8 or newer.
- Install in editable mode with dev extras:
pip install -e .[dev]. - Run
python3 -m unittest discover -s testsbefore submitting a pull request.
PyTOON retains the licensing of the original toon project. Refer to LICENSE for details.