Skip to content

Initial draft#2

Merged
nathanjmcdougall merged 89 commits into
mainfrom
vibe
May 14, 2026
Merged

Initial draft#2
nathanjmcdougall merged 89 commits into
mainfrom
vibe

Conversation

@nathanjmcdougall
Copy link
Copy Markdown
Collaborator

No description provided.

…Replace assert-based guards in Editor.original/document with\n explicit RuntimeError raises (survives python -O)\n- Remove useless return None in Editor.__exit__\n- Fix Union[str, int] -> str | int (UP007)\n- Remove empty TYPE_CHECKING blocks\n- Fix unused imports in test files\n- Fix ambiguous variable names and list-comprehension-as-index patterns\n- Narrow pytest.raises(Exception) to pytest.raises(KeyError)\n- Combine nested with statements (SIM117)\n- Add todo tickets for 20 identified codebase issues
…_(\"typing\").TYPE_CHECKING hack with standard\n pattern in editor.py\n- Remove empty TYPE_CHECKING block in __init__.py\n- Delete resolved todo tickets (006, 018, 019)\n- Add todo ticket 021 (README)
…ublic methods in Document and Editor\n- Move test-local imports to top-level (PLC0415)\n- Fix multi-statement pytest.raises blocks (PT012)\n- Fix string literal exceptions (EM101)\n- Delete resolved todo tickets (007, 008)
…valid_raises was a pass stub. Now tests that\n_core.Document raises ValueError on malformed YAML input.
…al in (None, {}, [])` with explicit\n`parent_val is None or parent_val in ({}, [])` to separate\nidentity check from equality checks.
…same _core.Route across existence check and operation\nin __getitem__, query, and replace instead of building it twice.
…hould support equality comparison\nand hashing based on source text. Adds tests for equality,\ninequality, cross-type comparison, and set deduplication.
…fusing PyO3 downcast error with a clear TypeError:\n\"updates must be a dict, got <type>\".
… converted to YAML sequences, matching\nthe existing list behavior. Previously raised TypeError.
…e awkward (\"name\",) in doc with \"name\" in doc.\nBoth forms work, but bare strings are more Pythonic and\nestablish the canonical usage pattern.
…utes since they are class-level,\nnot instance-level. Add __eq__, __int__, __repr__ method\nsignatures that PyO3 provides at runtime.
…Component, Route, and Feature are now\nhashable and support equality comparison. This allows them to\nbe used in sets and as dict keys.
…lls now go through _apply() which\nconverts RuntimeError to PatchError. query_exact KeyError is\nwrapped as QueryError, and parse_value ValueError/KeyError\nis wrapped as QueryError. Users never see raw Rust exceptions\nthrough the public Python API.
… build all patches upfront and apply them in a\nsingle apply_patches call instead of looping with one patch per\niteration. This avoids O(n) full reparses for n items.
…s now a method on PyDocument that reuses the\nalready-parsed tree-sitter tree. The standalone function\nremains as a convenience wrapper. Document.__getitem__ now\ncalls self._core_doc.parse_value(route) instead of the\nstandalone _core.parse_value(source, route).
… installation, quick start, full API overview (Document,\nEditor, error hierarchy), known limitations (multi-document,\ntags, anchors), and development setup.
Construct the Document before assigning any instance fields so that
a ParseError leaves the Editor in a clean state for potential reuse.
All yamltrip exceptions now suppress the Rust RuntimeError cause chain
with 'from None' for clean user-facing tracebacks. The _apply_patches
method was the only inconsistent site using 'from e'.
Assertions are stripped with python -O. Use if/raise TypeError instead
for the str type narrowing after _check_no_int_keys_for_creation.
Deduplicate the ancestor-based and root-level creation paths by
extracting the shared nested value construction and patching logic
into a _create_at method. Reduces upsert from 40+ lines to ~15.
Files with non-UTF-8 content now raise ParseError instead of a bare
UnicodeDecodeError, so callers can catch all yamltrip errors uniformly
via YAMLTripError.
Expose the operation type as a string ('replace', 'add', 'remove',
'append', 'merge_into') so Op instances can be inspected from Python
without parsing repr strings.
A tuple like (3.14,) passed _normalize_keys unchecked, then crashed
with an opaque Rust TypeError. Now each element is validated to be
str or int, giving a clear Python-side TypeError.
The previous contract had only one layer (errors) and ignored both
document and editor, checking nothing. Define proper layers matching
the actual dependency direction. Ignore the unavoidable document ->
yamltrip __init__ transitive import (Python loads parent __init__
for any submodule import).
Test Location, FeatureKind, Component, and Route conversions to
yamlpath types. These are pure Rust tests that don't require the
Python GIL.
The old formula (span.0 - nl - 1) calculated bytes from the newline
character, not from the line start. Use (nl + 1) as the line start
position for a correct column offset. Add comments documenting why
serde re-parsing is architecturally necessary.
Add TODO comment noting the dependency is deprecated and should be
migrated to serde_yml once yamlpatch drops its serde_yaml dependency.
…emove double-parse

- Add cross-reference comments between duplicated patch-application sites (#2)
- Add 9 tests for parse_value dedent logic on block scalars (#3)
- Change Any -> object on __getitem__/__contains__/__setitem__ signatures (#4)
- Document empty-tuple root access behavior on Document (#5)
- Document TOCTOU best-effort nature of Editor.__exit__ (#6)
- Remove redundant double-parse in Editor.__enter__ (#7)
…s eq/hash

- Extract yaml_str_repr helper to avoid wrapping strings in Value::String (#11)
- Replace wildcard catch-all arms with explicit handling of all 8 Op variants (#12)
- Use pyclass(frozen, eq, hash) instead of manual __eq__/__hash__ impls (#13)
- load -> mutate -> dump -> reload round-trips
- Editor context manager end-to-end (success, exception, multi-op)
- Use __new__ instead of __init__ for PyO3 constructors
- Add @Final to non-subclassable classes
- Make __eq__ parameters positional-only
- Add __all__, Component.Key/Index nested classes with properties
- Add stubtest as a pytest test (with mypy in test deps)
Use source.get(start..end) instead of source[start..end] to avoid
a Rust panic when byte offsets land inside a multi-byte UTF-8 codepoint.
Returns a clean ValueError instead of crashing the process.

Includes a regression test with a cross-document Feature on emoji YAML.
parse_value() byte spans come from tree-sitter on the same document,
so mid-codepoint slicing is not exploitable via the public API. Keep
idiomatic Rust panicking slices (the contract is that tree-sitter
produces char-aligned spans). Add tests for multi-byte UTF-8 keys,
values, nested paths, and multiline dedenting.
Add undocumented constraints: UTF-8 only, non-finite float rejection,
integer key creation restriction, i64 integer range, no negative
indices, non-atomic Editor write-back, and line ending preservation.
- py_to_yaml_value: fall back to u64 extraction when i64 overflows,
  so large unsigned integers (i64::MAX+1 .. u64::MAX) round-trip correctly
- Route::new: detect negative integers explicitly and raise ValueError
  instead of a misleading TypeError claiming the value is not an int
- python-test.yml: pytest across Python 3.10-3.14 on ubuntu/macos/windows
- rust-test.yml: cargo test on stable + beta (beta is informational)
- static-checks.yml: prek, cargo fmt, cargo clippy
- release.yml: maturin build + PyPI trusted publishing
- zizmor.yml: GitHub Actions security scanning
@github-advanced-security
Copy link
Copy Markdown

You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool.

What Enabling Code Scanning Means:

  • The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
  • Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
  • You will be able to see the analysis results for the pull request's branch on this overview once the scans have completed and the checks have passed.

For more information about GitHub Code Scanning, check out the documentation.

…est compat

- rust-test.yml, static-checks.yml: add actions/setup-python so PyO3 can
  link against libpython during cargo test and clippy
- src/*.rs: cargo fmt, replace deprecated PyObject with Py<PyAny>,
  replace downcast() with cast()
- tests/test_stubs.py: conditionally use --ignore-disjoint-bases only
  when the installed mypy version supports it (fixes min-deps matrix)
PyO3's extension-module feature skips linking against libpython, which
is correct for building the .so/.pyd extension but breaks cargo test.

Move extension-module behind a Cargo feature flag (enabled by default
so maturin builds are unaffected) and use --no-default-features in CI
for cargo test and clippy.
@nathanjmcdougall nathanjmcdougall merged commit f7b893e into main May 14, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants