Skip to main content

CHRON: Configurable Human-Readable Object Notation

Project description

CHRON

CHRON stands for Configurable Human-Readable Object Notation.

CHRON is a Python library for round-tripping structured objects through review-friendly Markdown text with explicit marker metadata.

Why CHRON

  • Human-readable output for review and diff.
  • Machine-loadable with deterministic reconstruction.
  • Typed records supported through pydantic model metadata.
  • Configurable marker namespace prefix.
  • No regex parsing in core codec paths.
  • Marker-level grammar parsed via lark.
  • JSON path resolution and matching via jsonpath_ng.

Install

pip install chronml

Quick Start

from chronml import Chron

chronml = Chron()

records = [
    {"title": "hello", "body": "line1\nline2"}
]

md = chronml.dumps(records, template="# {$.title}\n\n{$.body:block}")
print(md)

loaded = chronml.loads(md)
assert loaded[0]["title"] == "hello"
assert loaded[0]["body"] == "line1\nline2"

Markdown Output Examples

1) Inline + Block

Python:

from chronml import Chron

chronml = Chron()
md = chronml.dumps(
    [{"title": "hello", "body": "line1\nline2"}],
    template="# {$.title}\n\n{$.body:block}",
)
print(md)

Output:

<!-- chronml:begin type=json skeleton={} -->

# <!-- chronml:value-begin json-path=$.title --> hello <!-- chronml:value-end -->

<!-- chronml:multi-line-value-begin json-path=$.body -->
line1
line2
<!-- chronml:multi-line-value-end -->
<!-- chronml:end -->

2) Code Block

Python:

from chronml import Chron

chronml = Chron()
md = chronml.dumps(
    [{"code": "print(1)\nprint(2)"}],
    template="{$.code:code-block-python}",
)
print(md)

Output:

<!-- chronml:begin type=json skeleton={} -->

<!-- chronml:code-block-begin lang=python json-path=$.code -->
```python
print(1)
print(2)
```
<!-- chronml:code-block-end -->
<!-- chronml:end -->

3) Wildcard Multi-Match ([*])

Python:

from chronml import Chron

chronml = Chron()
md = chronml.dumps(
    [{"items": [{"name": "A"}, {"name": "B"}]}],
    template="items: {$.items[*].name}",
)
print(md)

Output (flattened with concrete paths):

<!-- chronml:begin type=json skeleton={"items": [{}, {}]} -->

items: <!-- chronml:value-begin json-path=$.items[0].name --> A <!-- chronml:value-end --> <!-- chronml:value-begin json-path=$.items[1].name --> B <!-- chronml:value-end -->
<!-- chronml:end -->

4) Nested Model Template Expansion

Python:

from pydantic import BaseModel
from chronml import Chron

class Child(BaseModel):
    x: int
    y: str
    __chronml_template__ = "X={$.x}\nY={$.y}"

class Parent(BaseModel):
    name: str
    child: Child
    __chronml_template__ = "N={$.name}\n{$.child}"

chronml = Chron()
md = chronml.dumps([Parent(name="p", child=Child(x=5, y="yy"))], template="")
print(md)

Output (nested paths rewritten to global paths):

<!-- chronml:begin type=pydantic:__main__.Parent skeleton={"child": {}} -->

N=<!-- chronml:value-begin json-path=$.name --> p <!-- chronml:value-end -->
X=<!-- chronml:value-begin type=int json-path=$.child.x --> 5 <!-- chronml:value-end -->
Y=<!-- chronml:value-begin json-path=$.child.y --> yy <!-- chronml:value-end -->
<!-- chronml:end -->

Core API

  • Chron.loads(text) -> list[record]
  • Chron.load(path) -> list[record]
  • Chron.dumps(records, template=..., template_func=...) -> str
  • Chron.dump(records, path, template=..., template_func=...) -> None

Module-level helpers with the same names are also exported: loads, load, dumps, dump.

Template Syntax

Placeholder forms:

  • {$.path} inline value
  • {$.path:inline} inline value
  • {$.path:block} multi-line value block
  • {$.path:code-block} fenced code block
  • {$.path:code-block-python} fenced code block with language
  • {$.items[*].name} multi-match flattening

For wildcard and other multi-match expressions, CHRON expands all matches in result order. Emitted marker paths are concrete ($.items[0].name, ...).

Marker Prefix

Default prefix is chronml:.

chronml = Chron(marker_prefix="custom:")

Then markers are emitted as:

  • <!-- custom:begin ... -->
  • <!-- custom:value-begin ... -->
  • <!-- custom:end -->

Pydantic Support

If a record is a pydantic.BaseModel, CHRON stores record type metadata as type=pydantic:<module>.<ClassName> and reconstructs model instances on load.

Template resolution order per record:

  1. obj.__chronml_template__ (string only)
  2. template_func(obj)
  3. template argument

Nested model templates are expanded in place for inline/default placeholders, and nested paths are rewritten to global paths.

Escaping and Round-Trip Guarantees

CHRON escapes marker-like payload text, including existing escaped marker literals, with reversible encoding. This preserves:

  • leading/trailing spaces in inline text
  • multi-line content and trailing spaces in block mode
  • marker-like text inside payload content

Protocol and AST

  • Marker AST spec: docs/ast-spec.md
  • Release checklist: docs/release.md

Development

Run tests:

uv run --with pytest pytest tests -q

Build artifacts:

uv run --with build python -m build

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

chronml-0.1.0.tar.gz (61.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

chronml-0.1.0-py3-none-any.whl (13.2 kB view details)

Uploaded Python 3

File details

Details for the file chronml-0.1.0.tar.gz.

File metadata

  • Download URL: chronml-0.1.0.tar.gz
  • Upload date:
  • Size: 61.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.12

File hashes

Hashes for chronml-0.1.0.tar.gz
Algorithm Hash digest
SHA256 6c64529bea1054c36a506a0eaaac22a35ecbab71eebb76ab5604d70bd4d08a35
MD5 522f14c4799cd6575fe32d476acda4ca
BLAKE2b-256 c4f988eee7ebad8c352e4896ef9fe1acdcf752630c82f1fd8a09bfaae1a96318

See more details on using hashes here.

File details

Details for the file chronml-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: chronml-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 13.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.12

File hashes

Hashes for chronml-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ba1cd30a1f41cc43d370d17e3e9b5f4a792785dc1e547eee9246fd43fc8a7f63
MD5 146316b72c69332d6e5cb6457ca15a5e
BLAKE2b-256 2e4b3f317d9e4ab7892b3cb473699b54fadd060cc346383083717c75eb6631f2

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page