pytoon-encoder


Namepytoon-encoder JSON
Version 0.1.1 PyPI version JSON
download
home_pageNone
SummaryPython port of the TOON encoder, producing a concise, human-readable data format.
upload_time2025-10-27 08:37:29
maintainerNone
docs_urlNone
authorBintang Pradana Erlangga Putra
requires_python>=3.8
licenseMIT
keywords toon serialization data-format text encoding
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            PyTOON
======

PyTOON is a Python port of the excellent [`toon`](https://github.com/johannschopplich/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.

Contents
--------
- [Features](#features)
- [Installation](#installation)
- [Quick Start](#quick-start)
- [Normalization Reference](#normalization-reference)
- [Encoding Behaviour](#encoding-behaviour)
- [Options](#options)
- [Advanced Usage](#advanced-usage)
- [Testing](#testing)
- [Project Structure](#project-structure)
- [Versioning & Compatibility](#versioning--compatibility)
- [Contributing](#contributing)
- [License](#license)

Features
--------
- 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+.

Installation
------------

```bash
pip install pytoon-encoder
```

Working from a clone of this repository? Install in editable mode:

```bash
pip install -e .
```

Quick Start
-----------

```python
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]:
```

Normalization Reference
-----------------------

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.                                |

Encoding Behaviour
------------------

PyTOON selects the most legible format while preserving structure:

- **Objects** render as `key: value` lines in insertion order. Empty objects emit just `key:`.
- **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`](examples/basic_usage.py) for ready-to-run scenarios.

Options
-------

`encode` accepts an optional `EncodeOptions` dictionary that mirrors the TypeScript API:

```python
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'` | `','`   | Active delimiter for inline arrays and tabular rows.                                        |
| `length_marker` | `'#'` or `False`     | `False` | When `'#'`, emits headers like `[#3]`; helpful when the consumer expects marked lengths.    |

Advanced Usage
--------------

- **Pre-normalisation**: Import `normalize_value` from `pytoon.normalize` to convert data once, then reuse the normalised structure across multiple encodings or transport layers.
- **Direct value encoding**: `pytoon.encoders.encode_value` accepts a pre-normalised JSON value plus resolved options, allowing integration with custom line writers or alternative indentation strategies.
- **Custom delimiters**: The `DELIMITERS` constant exposes the supported delimiter characters. Pass alternate values (e.g. `DELIMITERS["tab"]`) to match TSV-style pipelines.
- **Examples**: The [`examples/`](examples/README.md) directory contains scripts that highlight default behaviour and option combinations. Extend these for domain-specific onboarding material.
- **Automation**: `.github/workflows/ci.yml` runs unit tests on every push and publishes tagged releases (`v*`) to PyPI once tests pass.

Testing
-------

Tests use Python's built-in `unittest` framework:

```bash
python3 -m unittest discover -s tests
```

When porting new behaviour from `toon`, add corresponding tests here to keep parity strong.

Project Structure
-----------------

- `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.

Versioning & Compatibility
--------------------------

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.

Contributing
------------

Contributions are welcome! To get started:

1. Fork and clone the repository.
2. Create a virtual environment for Python 3.8 or newer.
3. Install in editable mode with dev extras: `pip install -e .[dev]`.
4. Run `python3 -m unittest discover -s tests` before submitting a pull request.

License
-------

PyTOON retains the licensing of the original [`toon`](https://github.com/johannschopplich/toon) project. Refer to [`LICENSE`](LICENSE) for details.

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "pytoon-encoder",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": null,
    "keywords": "toon, serialization, data-format, text, encoding",
    "author": "Bintang Pradana Erlangga Putra",
    "author_email": null,
    "download_url": "https://files.pythonhosted.org/packages/1b/7e/7d6ff1f0450a687d0c22f8539c319f100bd6bbb0cace33ef9f64fa0b6ed1/pytoon_encoder-0.1.1.tar.gz",
    "platform": null,
    "description": "PyTOON\n======\n\nPyTOON is a Python port of the excellent [`toon`](https://github.com/johannschopplich/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.\n\nThe goal of this repository is feature parity with the upstream project while providing a first-class experience for Python applications and tooling.\n\nContents\n--------\n- [Features](#features)\n- [Installation](#installation)\n- [Quick Start](#quick-start)\n- [Normalization Reference](#normalization-reference)\n- [Encoding Behaviour](#encoding-behaviour)\n- [Options](#options)\n- [Advanced Usage](#advanced-usage)\n- [Testing](#testing)\n- [Project Structure](#project-structure)\n- [Versioning & Compatibility](#versioning--compatibility)\n- [Contributing](#contributing)\n- [License](#license)\n\nFeatures\n--------\n- Normalises native Python types (dicts, lists, dataclasses, sets, `datetime`, numeric edge cases, etc.) to a JSON-like value space before encoding.\n- Produces the familiar Toon text format with support for inline arrays, tabular object arrays, and deeply nested structures.\n- Configurable indentation, row delimiter (`','`, `'|'`, `'\\t'`), and optional length marker flag (`[#N]` style headers).\n- Safe string quoting and escaping rules that match the upstream implementation.\n- Pure-Python, dependency-free package targeting Python 3.8+.\n\nInstallation\n------------\n\n```bash\npip install pytoon-encoder\n```\n\nWorking from a clone of this repository? Install in editable mode:\n\n```bash\npip install -e .\n```\n\nQuick Start\n-----------\n\n```python\nfrom pytoon import encode\n\npayload = {\n    \"user\": {\n        \"id\": 123,\n        \"name\": \"Ada\",\n        \"tags\": [\"reading\", \"gaming\"],\n        \"active\": True,\n        \"prefs\": [],\n    }\n}\n\nprint(encode(payload))\n```\n\nOutput:\n\n```\nuser:\n  id: 123\n  name: Ada\n  tags[2]: reading,gaming\n  active: true\n  prefs[0]:\n```\n\nNormalization Reference\n-----------------------\n\nPyTOON first normalises values into a JSON-compatible shape. The table below mirrors the upstream Toon rules:\n\n| Python value                                  | Normalised result                         | Notes                                                                 |\n|-----------------------------------------------|-------------------------------------------|-----------------------------------------------------------------------|\n| `None`                                        | `None`                                    | Encodes as `null`.                                                    |\n| `bool`, `int`, `float`                        | Same numeric/boolean value                | Floats that are `NaN`, `\u00b1inf` become `None`; `-0.0` becomes `0`.      |\n| `str`                                         | Same string                               | Subject to quoting/escaping rules during encoding.                    |\n| `datetime`, `date`                            | ISO-8601 string                           | Uses `.isoformat()`.                                                  |\n| `set`, `frozenset`                            | List of normalised elements               | Order is the iteration order of the set.                              |\n| `list`, `tuple`, other sequences (not bytes)  | List of normalised elements               | Preserves order.                                                      |\n| `dict`, `Mapping`                             | Dict with stringified keys                | Keys are coerced with `str(key)`; values normalised recursively.      |\n| `dataclass` instances                         | Dict via `dataclasses.asdict`             | Deep conversion, then recurses.                                       |\n| Unsupported objects, functions, generators    | `None`                                    | Matches behaviour of upstream encoder.                                |\n\nEncoding Behaviour\n------------------\n\nPyTOON selects the most legible format while preserving structure:\n\n- **Objects** render as `key: value` lines in insertion order. Empty objects emit just `key:`.\n- **Arrays of primitives** appear inline: `tags[3]: a,b,c`.\n- **Arrays of objects** become tabular if every object shares identical primitive keys. Otherwise, items are rendered as `-` list entries.\n- **Arrays of arrays** with primitive inner arrays become nested lists; mixed or non-primitive inner arrays fall back to expanded list items.\n- **Mixed arrays** (primitives, objects, arrays combined) always degrade to list entries.\n- **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.\n\nSee [`examples/basic_usage.py`](examples/basic_usage.py) for ready-to-run scenarios.\n\nOptions\n-------\n\n`encode` accepts an optional `EncodeOptions` dictionary that mirrors the TypeScript API:\n\n```python\nfrom pytoon import encode, DELIMITERS\n\nencode(\n    {\"items\": [{\"id\": 1, \"name\": \"Ada\"}, {\"id\": 2, \"name\": \"Bob\"}]},\n    {\n        \"indent\": 4,\n        \"delimiter\": DELIMITERS[\"pipe\"],  # or ',' / '\\t'\n        \"length_marker\": \"#\",             # renders headers as [#N]\n    },\n)\n```\n\nOutput:\n\n```\nitems[#2|]{id|name}:\n  1|Ada\n  2|Bob\n```\n\nOption reference:\n\n| Option          | Type                 | Default | Description                                                                                 |\n|-----------------|----------------------|---------|---------------------------------------------------------------------------------------------|\n| `indent`        | `int`                | `2`     | Spaces per indentation level for nested structures.                                         |\n| `delimiter`     | `','`, `'|'`, `'\\t'` | `','`   | Active delimiter for inline arrays and tabular rows.                                        |\n| `length_marker` | `'#'` or `False`     | `False` | When `'#'`, emits headers like `[#3]`; helpful when the consumer expects marked lengths.    |\n\nAdvanced Usage\n--------------\n\n- **Pre-normalisation**: Import `normalize_value` from `pytoon.normalize` to convert data once, then reuse the normalised structure across multiple encodings or transport layers.\n- **Direct value encoding**: `pytoon.encoders.encode_value` accepts a pre-normalised JSON value plus resolved options, allowing integration with custom line writers or alternative indentation strategies.\n- **Custom delimiters**: The `DELIMITERS` constant exposes the supported delimiter characters. Pass alternate values (e.g. `DELIMITERS[\"tab\"]`) to match TSV-style pipelines.\n- **Examples**: The [`examples/`](examples/README.md) directory contains scripts that highlight default behaviour and option combinations. Extend these for domain-specific onboarding material.\n- **Automation**: `.github/workflows/ci.yml` runs unit tests on every push and publishes tagged releases (`v*`) to PyPI once tests pass.\n\nTesting\n-------\n\nTests use Python's built-in `unittest` framework:\n\n```bash\npython3 -m unittest discover -s tests\n```\n\nWhen porting new behaviour from `toon`, add corresponding tests here to keep parity strong.\n\nProject Structure\n-----------------\n\n- `pytoon/` \u2013 Core encoder modules (`constants`, `normalize`, `primitives`, `encoders`, `writer`).\n- `tests/` \u2013 Unit tests covering primitives, objects, arrays, options, and edge cases.\n- `examples/` \u2013 Runnable scripts that demonstrate practical usage patterns.\n\nVersioning & Compatibility\n--------------------------\n\nPyTOON 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.\n\nContributing\n------------\n\nContributions are welcome! To get started:\n\n1. Fork and clone the repository.\n2. Create a virtual environment for Python 3.8 or newer.\n3. Install in editable mode with dev extras: `pip install -e .[dev]`.\n4. Run `python3 -m unittest discover -s tests` before submitting a pull request.\n\nLicense\n-------\n\nPyTOON retains the licensing of the original [`toon`](https://github.com/johannschopplich/toon) project. Refer to [`LICENSE`](LICENSE) for details.\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Python port of the TOON encoder, producing a concise, human-readable data format.",
    "version": "0.1.1",
    "project_urls": {
        "Homepage": "https://github.com/bpradana/pytoon",
        "Issues": "https://github.com/bpradana/pytoon/issues",
        "Repository": "https://github.com/bpradana/pytoon",
        "Upstream": "https://github.com/johannschopplich/toon"
    },
    "split_keywords": [
        "toon",
        " serialization",
        " data-format",
        " text",
        " encoding"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "60401afc7e86b2d7afba2271c60721d3d259a54c4434ecccc3c6ad3f071c3604",
                "md5": "02803ffb2d2d96581b50e891fe4d57fe",
                "sha256": "2720349f4cf342c6126451a329da2f3a6f3fa27574e24f50d987da1e12d877f6"
            },
            "downloads": -1,
            "filename": "pytoon_encoder-0.1.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "02803ffb2d2d96581b50e891fe4d57fe",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 11643,
            "upload_time": "2025-10-27T08:37:28",
            "upload_time_iso_8601": "2025-10-27T08:37:28.668120Z",
            "url": "https://files.pythonhosted.org/packages/60/40/1afc7e86b2d7afba2271c60721d3d259a54c4434ecccc3c6ad3f071c3604/pytoon_encoder-0.1.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "1b7e7d6ff1f0450a687d0c22f8539c319f100bd6bbb0cace33ef9f64fa0b6ed1",
                "md5": "19ada66df77b1c72b5fef82065bd2499",
                "sha256": "5ee3ad6984efe553953c3c282020386eeeade59658dc0cb5ead1853499a742de"
            },
            "downloads": -1,
            "filename": "pytoon_encoder-0.1.1.tar.gz",
            "has_sig": false,
            "md5_digest": "19ada66df77b1c72b5fef82065bd2499",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 15014,
            "upload_time": "2025-10-27T08:37:29",
            "upload_time_iso_8601": "2025-10-27T08:37:29.386372Z",
            "url": "https://files.pythonhosted.org/packages/1b/7e/7d6ff1f0450a687d0c22f8539c319f100bd6bbb0cace33ef9f64fa0b6ed1/pytoon_encoder-0.1.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-10-27 08:37:29",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "bpradana",
    "github_project": "pytoon",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "pytoon-encoder"
}
        
Elapsed time: 0.87499s