Guidance for AI coding agents (and humans) working in this repository. This file follows the agents.md convention.
YAMLRocks is a fast, correct YAML library for Python, implemented in Rust with PyO3
and built by maturin. The Rust crate lives in src/; the Python package and
type stubs live in pysrc/yamlrocks/.
| Path | Purpose |
|---|---|
src/scanner/ |
Byte reader, tokenizer, comment extraction |
src/parser/ |
Tokens to events |
src/resolver/ |
Scalar typing (YAML 1.1 and 1.2 schemas) |
src/decode/ |
Fast path: events to a Value tree (+ merge keys) |
src/encode/ |
Fast path: Value tree to YAML bytes |
src/roundtrip/ |
Rich AST, composer, emitter, YAMLRocksDocument, upgrade |
src/include/ |
!include/!secret/!env_var/!input resolution |
src/schema/ |
JSON Schema validation against the AST |
src/ffi/ |
PyO3 module: functions, types, conversions |
pysrc/yamlrocks/ |
Python package, stubs, PyYAML compat shim |
tests/ |
pytest suite, grouped by capability (see tests/README.md); data under tests/data/ |
docs/ |
Astro Starlight documentation site |
adr/ |
Architecture decision records (the why behind major choices) |
This project uses uv for the Python side and
just as the task runner. just is the primary
interface: every common workflow (build, test, lint, type-check, docs) has a
recipe, and the recipes wrap the exact uv/cargo commands CI runs. Run
just --list to see them all, or just on its own for the same list.
Run a recipe either by activating the venv once (uv sync && source .venv/bin/activate, then just <recipe>) or without activating by prefixing
uv run --no-sync just <recipe>. Recipes assume just setup (or just develop)
has run at least once.
| Recipe | What it does |
|---|---|
just setup |
Install all dev dependencies into the uv-managed venv |
just develop |
Build + install the extension in release mode (rerun after Rust changes) |
just develop-debug |
Faster debug build for tight iteration loops |
just test [args] |
Run the Python suite (pytest); args pass through, e.g. just test -k anchors |
just test-rust [args] |
Run the Rust unit tests (cargo test --lib) |
just lint |
Lint and format-check Python (read-only) |
just fmt |
Auto-format Python and Rust in place |
just typecheck |
Type-check with both mypy and ty |
just clippy |
Lint the Rust crate (-D warnings) |
just spellcheck |
Spell-check with codespell |
just precommit [hook] |
Run every pre-commit hook via prek (the exact set CI runs) |
just check |
Full local gate: build, all hooks, Rust + Python suites, doc examples |
just examples |
Run every documented Python example and verify its output comments |
just docs / just docs-dev |
Build the docs site / serve it with live reload |
just bench [args] |
Comparison benchmarks vs PyYAML, ruamel.yaml, and yamlium |
just audit |
Audit every dependency ecosystem for advisories |
just licenses |
Regenerate THIRD_PARTY_LICENSES.md from the crate graph |
just coverage |
Rust line coverage measured through the Python suite |
just fuzz [seconds] |
Fuzz the parser, optionally time-boxed |
just clean |
Remove build and fuzzing artifacts |
The sections below give the raw commands behind the most important recipes, for when you need to run a variant by hand.
uv sync --no-install-project # create the venv, install dev deps (just setup)
uv run --no-sync maturin develop # build + install the extension, debug (just develop-debug)
# `just develop` builds the same thing with --release.Prefix any tool with uv run --no-sync to use the project venv without
re-syncing, or activate it once with source .venv/bin/activate.
just test runs the Python suite and just test-rust runs the Rust one. When
invoking pytest directly, always run it under a memory and time guard: a parser
bug can otherwise exhaust memory and take down the host. tests/conftest.py caps
RLIMIT_AS, but add a shell guard too:
timeout 200 bash -c 'ulimit -v 4000000; uv run --no-sync pytest -q' # raw form of: just test
cargo test --lib # raw form of: just test-rustWhen working on the round-trip emitter, regenerate snapshots intentionally:
UPDATE_SNAPSHOTS=1 uv run --no-sync pytest tests/compliance/test_snapshots.pyThe YAML test suite harness (tests/compliance/test_yaml_test_suite.py) asserts
full compliance directly, with no baseline to regenerate: a valid case must load,
round-trip, and match its canonical JSON; an invalid one must be rejected. If a
parser change ever regresses a case, the matching assertion fails by name.
just check runs the complete local gate, and just precommit runs the exact
hook set CI runs. The individual gates, for running one at a time:
cargo fmt --check
cargo clippy --all-targets -- -D warnings # just clippy
uv run --no-sync ruff check . && uv run --no-sync ruff format --check . # just lint
uv run --no-sync mypy pysrc/yamlrocks # \
uv run --no-sync ty check pysrc/yamlrocks # > just typecheck
uv run --no-sync codespell # just spellcheck- Keep public Rust items documented with
///rustdoc. - Every Python test has a one-line docstring describing what it verifies.
- Round-trip fidelity is sacred: an unmodified document must re-emit byte-for-byte. Do not regress this (the YAML test suite enforces it).
- Match the surrounding code style; prefer small, well-named functions.
- External test corpora (the YAML test suite and the real-world config corpus)
live as git submodules under
tests/data/(ADR-017); they auto-skip when the submodule is absent, so the default suite stays green without them.
Date.now()/ randomness are fine in tests but seed any generators.- The fast path (
loads/dumps) must stay allocation-light; the round-trip path may be heavier. - PyO3 (Rust) cannot natively subclass
str/int/float, so the annotated string and number types are thin pure-Python subclasses while annotateddict/listare Rust pyclasses (seeadr/010-...).
adr/(start atadr/README.md): architecture decision records.docs/: the full user-facing documentation (Astro Starlight).CONTRIBUTING.md: contributor workflow.