feat: add Document.get() and Editor.get() for non-raising value access#27
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a dict.get()-style accessor to Document and Editor so callers can fetch parsed values without handling QueryError for missing paths or empty documents. The existing strict .root / __getitem__ semantics are preserved, and the design rationale (avoid conflating "no value" with explicit YAML null) is documented in a new spec file.
Changes:
- New
Document.get(*keys, default=None)usingquery_exists+parse_value, and a thinEditor.get()delegate. - Tests for empty / comment-only /
---docs, nested paths, missing keys, custom defaults, YAML null, and a regression test confirming.rootstill raises. - Ruff config tweak to ignore
B018in tests (allowing bare attribute access for side-effect assertions likedoc.root), plus a design spec doc.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/yamltrip/document.py | Adds Document.get() returning default when the path does not exist. |
| src/yamltrip/editor.py | Adds Editor.get() delegating to the underlying document. |
| tests/test_document.py | New TestDocumentGet covering empty docs, nested paths, defaults, null values, .root regression. |
| tests/test_editor.py | New TestEditorGet mirroring document behavior through the editor context. |
| ruff.toml | Enables B018 ignore under tests/** to permit property-access-for-side-effects assertions. |
| doc/specs/2026-05-19-get-method-design.md | New design spec describing the rationale, semantics, and scope of .get(). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Switch from query_exists+parse_value to try/except KeyError pattern - Remove redundant ternary; always call _normalize_keys(keys)
nathanjmcdougall
left a comment
There was a problem hiding this comment.
Addressed all three review comments in 476b503:
Comment 1 (double traversal): Agreed — switched to try/except KeyError pattern. ValueError from malformed nodes still propagates correctly since we only catch KeyError.
Comment 2 (type validation ternary): The premise was slightly off — _normalize_keys was already being called for non-empty keys (e.g. doc.get(1.5) → keys=(1.5,) → truthy → validation fires). However, agreed on simplifying: removed the redundant ternary since _normalize_keys(()) handles the empty case correctly.
Comment 3 (test for invalid key types): The type checker ( y) already catches these at static analysis time, making runtime tests redundant. No additional tests needed.
Closes #23
Summary
Adds a
.get(*keys, default=None)method toDocumentandEditorthat returns the parsed Python value at a path, ordefaultif the path doesn't exist.This provides a
dict.get()-style alternative to bracket access without changing the existing strict behavior of.rootand__getitem__.Design decision
The issue requested making
.rootreturnNoneon empty documents. We chose.get()instead because:Nonefrom.rootconflates "no value" with "explicit YAML null" — two distinct states.get(default=...)gives callers control over the fallbackExamples
Changes
src/yamltrip/document.py—Document.get()methodsrc/yamltrip/editor.py—Editor.get()methodruff.toml— disable B018 in tests (property access for side-effects is legitimate in tests)