Skip to content

Releases: 759401524/pyrs-yaml

v0.14.1

Choose a tag to compare

@759401524 759401524 released this 15 Aug 04:27

[v0.14.1] — 2026-08-15

Fixed

  • Single-quoted scalars with backslash + control/noncharacter — a quoted
    value containing a backslash was routed to single-quoting, but single quotes
    cannot escape control characters or Unicode noncharacters, so the emitted
    YAML was unparseable. Such values now use double-quoting (direct_dump and
    the shared write_plain_scalar single-quote branch).
  • Noncharacters and BOM quotedneeds_quotes / needs_double_quoted
    now treat Unicode noncharacters (U+FFFE/U+FFFF and the plane-end twins) and
    U+FEFF (BOM) as requiring quoting: granit drops a plain U+FEFF as a
    document-start BOM and rejects raw noncharacters even inside quoted scalars.
  • Double-quoted escape widthwrite_double_quoted_scalar escapes
    noncharacters and control chars; for code points above U+FFFF it now emits
    the 8-digit \Uxxxxxxxx form (the 4-digit \u form is only valid for the
    BMP).
  • Folded plain-scalar continuation indentwrap_plain_scalar
    continuation indent is no longer a fixed 2 spaces; it is derived from the
    value's start column on the current line, so folded plain scalars inside
    nested sequence/mapping items stay indented past the parent block indent
    (granit otherwise reports "simple key expected ':'").
  • Multi-byte wrap boundarywrap_plain_scalar now floors the wrap slice
    to a char boundary instead of panicking when a 4-byte UTF-8 character
    straddles the wrap column.
  • hypothesis in publish test requirements.ci/requirements-test.txt
    now pins hypothesis>=6.113.0 so the publish workflow (which does not
    install the dev dependency group) can run the property test suite.

Added

  • scripts/fuzz_panics.py — high-volume local Hypothesis fuzz harness that
    bypasses pytest @settings caps with a hostile strategy (control chars,
    NBSP, backslashes, long multibyte runs) across dump/parse/edit/idempotency.

v0.14.0 — Core Engine Rebuild

Choose a tag to compare

@759401524 759401524 released this 14 Aug 07:36
f450169

[v0.14.0] — 2026-08-14

Added

  • YAML Schema Language — define custom schemas as YAML files with a
    rules list mapping regex patterns to YAML types (null/bool/int/
    float/str), plus an optional extends base schema. Registered via
    register_schema(name, schema_yaml) and used as YAML(schema=name).
  • Inline dict schema — the schema parameter of YAML(), parse(),
    parse_file(), parse_all_docs(), safe_load(), and safe_loads()
    accepts an inline dict, serialized and registered automatically.
  • Community PluginsCustomType base class with
    can_parse/from_yaml/to_yaml/validate methods; register via
    register_type() (imperative or decorator). Custom types handle tagged
    scalars on load and Python objects on dump.
  • Built-in plugins!timestamp (maps to datetime) and !set
    registered by default in pyrs_yaml/plugins/.

Changed

  • Schema resolution is pluggableYamlSchema enum refactored into a
    SchemaResolver trait + Schema enum with a global SchemaRegistry
    pre-loaded with the four built-in schemas (failsafe, json, core,
    yaml1.1). Custom schemas register via the registry; built-in Core keeps
    its zero-cost match dispatch.
  • node_to_pyobject and direct_dump check registered CustomTypes
    tagged scalars convert via from_yaml() on load; matching Python objects
    serialize via to_yaml() on dump.

Fixed

  • Quoted scalars always load as strings — implicit type resolution now
    applies only to plain scalars (YAML 1.2): safe_load('"true"') returns the
    string "true", not True. The serializer keeps negative numbers
    round-tripping through the document (to_yaml) path.
  • Lone-quote keys round-trip — mapping keys that are a single ' or "
    are emitted as quoted scalars instead of unparseable YAML.
  • Empty collections emit {}/[] — dumping empty mappings/sequences no
    longer yields an empty document that re-parses as None.

Changed

  • get() is literal-key onlyYamlDocument.get() no longer guesses
    JSONPath for keys containing . or [; every key is treated as a
    top-level mapping key, consistent with __getitem__/__setitem__.
    Path access stays available via find()/node().

v0.13.0 — Core Engine Rebuild

Choose a tag to compare

@759401524 759401524 released this 11 Aug 02:05

[v0.13.0] — 2026-08-10

Changed

  • Rust MSRV raised to 1.96 and edition bumped to 2024 - both crates now
    declare rust-version = "1.96" and edition = "2024"; CI pins the
    build/test-freethreaded jobs to Rust 1.96 for deterministic wheel builds
    and adds an msrv-check job running cargo check/cargo test at the MSRV
    to prevent silent MSRV drift (the rust-lint job stays on stable).
    The floor is set above PyO3 0.29's own baseline (rustc 1.83) for std API
    headroom (e.g. assert_matches!, stabilized 1.96) with no code migration
    needed. TAG_REGISTRY (tag handler storage) refactored to
    std::sync::LazyLock, dropping the Mutex<Option<...>> indirection.

Performance

  • safe_dump / from_dict / dump_file / dump_iterable: direct writer
    — Python→YAML serialization without intermediate CustomNode AST.
    Single-pass direct_dump replaces the old two-pass pyobject_to_node +
    to_yaml. 7x faster on safe_dump (28ns→4ns), 6x faster on from_dict
    (35ns→6ns). (#60)
  • safe_load / safe_loads / to_dict: fast-path skip anchor tracking
    — when input has no & characters, skip collect_anchors + anchor
    resolution and use the simpler node_to_pyobject_simple path. (#59)
  • resolve_core_type: first-byte dispatch whitelist — non-numeric/
    non-boolean first bytes return Str immediately, avoiding schema
    resolution overhead for the common case. (#59)
  • granit-parser migration — saphyr-parser replaced with granit-parser
    1.0.1 for native Event::Comment emission, eliminating the full-text
    scan_yaml() pre-scan. parse_small -18%, parse_large -21%,
    roundtrip_large -18%.

Fixed

  • float_to_yaml_string round-trip fix — appends .0 when Rust
    Display drops the decimal (4242.0) so floats round-trip as
    floats instead of becoming ints.
  • Reverted count_nodes pre-allocation — the full AST traversal cost
    more than the reallocations it avoided (serialize_10mb was ~14% slower);
    buffer growth is left to the Vec.

Added

  • max_depth on stream & frontmatter APIsparse_stream(yaml, on_event, max_depth),
    read_markdown(path, schema, max_depth), read_markdown_str(content, schema, max_depth)
    accept max_depth (default 1000). Stream parsing now enforces the nesting-depth limit
    via core parse_stream_with_options (previously stream events had no depth limit).
  • Pydantic integrationdump_pydantic() serializes a Pydantic model
    to YAML string via model_dump(mode='json') + safe_dump; parse_as()
    parses YAML string into a Pydantic model instance. Both use lazy imports,
    no hard dependency on pydantic. (#61)

Internal

  • Split py/mod.rs — monolithic 1786-line module broken into
    document.rs (YamlDocument), yaml_instance.rs (YAML class),
    functions.rs (module-level functions), stream_iterator.rs,
    walk_helpers.rs. mod.rs reduced to 128 lines. (#61)
  • needs_quotes() guard + double_quoted_scalar() constructor
    strings like 'true' / '42' / 'null' now emit as double-quoted
    scalars under the core schema instead of being misread on re-parse
    (pyobject_to_node + json_value_to_node).
  • CodSpeed benchmarks unified on codspeed-divan-compat
    exclude-allocations removes allocator noise; cross-library benchmarks
    consolidated into tests/test_benchmark_crosslib.py with shared
    tests/data/yaml_samples.py fixtures and streaming coverage.

v0.11.0 — Surgical Serialization

Choose a tag to compare

@759401524 759401524 released this 02 Aug 12:15

[0.11.0] - 2026-08-02

Added

  • Surgical Serialization — byte-level source span tracking on every AST node; segment-based splice — edits regenerate only the touched region, untouched text is byte-copied
  • proptest fidelity property tests (new dev-dependency)
  • 10MB edit-flush benchmarks (divan)

Changed

  • lush_source now splices segments; falls back to full serialization for flow-style regions, non-default layout documents, merged keys, CRLF/BOM documents, and after materialization (single-burst model)
  • Splice edits preserve ---/.../directive marker lines as untouched bytes (full serialization previously dropped them — deliberate behavior difference)

v0.9.0 — Ecosystem Ready

Choose a tag to compare

@759401524 759401524 released this 01 Aug 04:06
d023f77
feat: v0.9.0 Ecosystem Ready

Python 3.13-3.15 + free-threaded CPython support, allow_duplicate_keys, SerializeOptions expansion (width / indent_mapping / indent_sequence / indent_offset), tag handler registry with chaining + remove_tag, Pydantic integration via parse_as(), committed .pyi stubs, and a CI optimization (single rust-lint job, wheel install instead of maturin develop).

Review follow-up fixes: wired the previously dead serializer indent options, added a width=1 hang guard, i18n'd duplicate-key errors across 4 locales, and made non-string tag handler returns raise YamlTagError.

v0.8.0 — Unlock the AST

Choose a tag to compare

@759401524 759401524 released this 31 Jul 05:05
02eec70

v0.8.0 — Unlock the AST

Breaking Changes

  • YAML() instance API: New YAML() class replaces module-level parsing for configuration. pyrs_yaml.parse() still works as shorthand.
  • Python Node API: New Node(doc) class exposes AST with ind(), walk(), ilter(), parent, children, o_yaml().
  • MergedView: doc.merged() returns a read-only dict-like view with merge keys resolved.
  • Lifecycle warnings: Node.release() marks stale nodes; accessing released nodes emits RuntimeWarning and raises YamlDocumentError.
  • Document metadata: doc.version() returns the YAML spec version.

✨ Features

  • Python Node API with JSONPath-like query language in Node.find()
  • MergedView — read-only anchor merge view
  • Node.release() + RuntimeWarning lifecycle warnings
  • doc.version() metadata
  • pytest parametrize refactoring across 5 test files (498→322 lines)

🔧 Bug Fixes

  • Node.to_yaml(): rom_dict() returns str, not YamlDocument — fixed branching logic
  • MergedView: handle sequence-root documents
  • _parse_jsonpath: deep scan (..) and wildcard (\$..*) detection order
  • Removed duplicate pytest_collection_modifyitems in conftest.py

🧪 Testing

  • 3 new test files: test_yaml_instance.py, test_node_api.py, test_merged_view.py
  • 52 new test cases across all v0.8.0 features
  • 460 Python tests + 106 Rust tests all passing