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
pydanticmodel 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=...) -> strChron.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:
obj.__chronml_template__(string only)template_func(obj)templateargument
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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6c64529bea1054c36a506a0eaaac22a35ecbab71eebb76ab5604d70bd4d08a35
|
|
| MD5 |
522f14c4799cd6575fe32d476acda4ca
|
|
| BLAKE2b-256 |
c4f988eee7ebad8c352e4896ef9fe1acdcf752630c82f1fd8a09bfaae1a96318
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ba1cd30a1f41cc43d370d17e3e9b5f4a792785dc1e547eee9246fd43fc8a7f63
|
|
| MD5 |
146316b72c69332d6e5cb6457ca15a5e
|
|
| BLAKE2b-256 |
2e4b3f317d9e4ab7892b3cb473699b54fadd060cc346383083717c75eb6631f2
|