Metadata-Version: 2.4
Name: ouroboros-jinja
Version: 0.1.0
Summary: Expand Jinja2 templates that back-reference values in the same document
Project-URL: Homepage, https://github.com/talamus/python-ouroboros-jinja
Project-URL: Repository, https://github.com/talamus/python-ouroboros-jinja
Project-URL: Issues, https://github.com/talamus/python-ouroboros-jinja/issues
Author-email: Tero Niemi <talamus@gmail.com>
License-Expression: MIT
License-File: LICENSE
Keywords: configuration,jinja2,templating,toml,yaml
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: jinja2>=3.1.6
Requires-Dist: pyyaml>=6.0.3
Requires-Dist: tomli>=2.0; python_version < '3.11'
Description-Content-Type: text/markdown

# Ouroboros Jinja

Expand Jinja2 templates that back-reference other values in the same document
— the snake eats its own tail.

```yaml
# defaults.yaml
slug: ~
domain: "{{ slug }}.nimbus.fi"
volume-name: "{{ slug }}-volume"
permanent-ip-name: "{{ domain }}"
```

```toml
# server.toml
name = "Prominence 2: Hasturian Era"
slug = "hasturian"
```

```python
import ouroboros_jinja

config = ouroboros_jinja.load("defaults.yaml", "server.toml")
# {
#   "slug": "hasturian",
#   "domain": "hasturian.nimbus.fi",
#   "volume-name": "hasturian-volume",
#   "permanent-ip-name": "hasturian.nimbus.fi",
#   "name": "Prominence 2: Hasturian Era"
# }
```

## API

- `expand(*mappings)` — shallow-merge mappings (later ones win) and expand
  every string value as a Jinja2 template.
- `loads(*yaml_texts)` — parse YAML texts, merge and expand.
- `load(*paths)` — read files (`.toml` via `tomllib`, everything else as
  YAML), merge and expand.

Templates may reference any top-level key, including nested content
(`{{ an_object.description }}`) and lists (`{% for port in ports %}`).
Values are expanded lazily and recursively, in dependency order.

## Semantics

- Reference cycles raise `CircularReferenceError` with the chain spelled
  out: `Circular reference detected: item_1 -> item_2 -> item_3 -> item_1`.
- Unknown variables raise `ExpansionError` (Jinja2 `StrictUndefined`).
- A top-level key with a null value (`slug: ~`) is a *required override*:
  referencing it before it has been overridden raises `ExpansionError`
  instead of silently interpolating `"None"`.
- Only strings containing `{{` or `{%` are touched; other values
  (ints, lists, …) pass through unchanged.

## Testing

```sh
uv run pytest
```
