Node Class
Node Class¶
The Node class provides a borrowed view into a YamlDocument's AST, enabling tree traversal, query, and mutation operations. A node is created via doc.node(), doc.find("$.path"), or doc.walk().
Overview¶
class Node:
"""A node in the YAML AST, backed by a YamlDocument and a path."""
Each Node stores a reference to its parent YamlDocument and a path tuple that navigates to the target node within the document's AST. Nodes become stale when the document is modified or released.
Constructor¶
Node.__init__()¶
Node.__init__(document: YamlDocument, path: tuple = ()) -> None
Parameters:
document— The parentYamlDocumentpath— A tuple of path segments (keys/indexes) navigating to the target node
Properties¶
value¶
Get the scalar value of this node.
value -> Any | None
Returns None for non-scalar nodes (mappings, sequences).
root_type¶
Get the type of this node.
root_type -> str
Returns one of "scalar", "mapping", "sequence", "null".
_path¶
The path tuple that navigates to this node within the document's AST.
_path -> tuple
path¶
Get this node's path segments (tuple of keys and indices). Unlike _path,
this is the documented public accessor — useful for logging, error messages,
and re-finding a node after it becomes stale.
path -> tuple
children¶
Get the child nodes of this node.
children -> list[Node]
Returns an empty list for scalar/null nodes.
parent¶
Get the parent Node, or None if this is the root.
parent -> Node | None
comment¶
Get this node's comment text, or None if it has no comment.
comment -> str | None
anchor¶
Get this node's anchor name, or None if it has no anchor.
anchor -> str | None
tag¶
Get this node's YAML tag string (e.g. !!str), or None if it has no tag.
tag -> str | None
scalar_style¶
Get the scalar style ("plain", "single_quoted", "double_quoted", "literal", "folded"), or None for non-scalar nodes.
scalar_style -> str | None
flow_style¶
Get the flow style (True = flow {}/[], False = block), or None for non-container nodes.
flow_style -> bool | None
chomping¶
Get the chomping indicator ("strip", "clip", "keep"), or None for non-scalar nodes.
chomping -> str | None
Methods¶
find()¶
Find a node by JSONPath-like path.
find(path: str) -> Node | list[Node]
Supported path syntax:
| Pattern | Description |
|---|---|
$.key |
Root key |
$.key.subkey |
Nested key |
$.arr[0] |
Index into sequence |
$.arr[*] |
All items in sequence |
$..key |
Deep search for key at any depth |
$..* |
All descendant nodes |
Returns: A single Node for exact paths, or a list[Node] for wildcard/deep-scan queries.
find_first()¶
Find the first matching node by JSONPath-like path. Always returns a single
Node or None (never a list) — for wildcard paths ([*]), returns the first
element.
find_first(path: str) -> Node | None
walk()¶
Walk all descendant nodes (depth-first pre-order).
walk() -> Iterator[Node]
Yields: The node itself, then all descendants recursively.
filter()¶
Filter descendant nodes by a predicate function.
filter(predicate: Callable[[Node], bool]) -> list[Node]
Parameters:
predicate— A function taking aNodeand returningbool
Example:
scalars = root.filter(lambda n: n.root_type == "scalar")
set_value()¶
Replace this node's value, preserving its metadata (comment, anchor, tag, style).
set_value(value: Any, create_missing: bool = False) -> None
With create_missing=True, missing intermediate mapping keys along the path are created as nested mappings. Index segments that miss are still an error.
append()¶
Append a value to a sequence node.
append(value: Any) -> None
insert()¶
Insert into a sequence node at an index.
insert(index: int, value: Any) -> None
delete()¶
Remove this node and its comments. The node becomes stale afterwards.
delete() -> None
rename()¶
Rename this node's mapping key. The node must be a mapping value.
rename(new_key: str) -> None
set_comment()¶
Set (or replace) this node's comment. With standalone=True (default) the
comment is emitted on its own line above the node; with standalone=False
it is emitted inline after the node.
set_comment(text: str, standalone: bool = True) -> None
remove_comment()¶
Remove this node's comment.
remove_comment() -> None
set_anchor()¶
Set (or replace) this node's anchor.
set_anchor(name: str) -> None
remove_anchor()¶
Remove this node's anchor.
remove_anchor() -> None
set_tag()¶
Set (or replace) this node's YAML tag. "!custom" produces a local tag,
"!!int" produces a primary (!!) tag, and "!<tag:yaml.org,2002:str>"
produces a verbatim tag.
set_tag(tag: str) -> None
remove_tag()¶
Remove this node's YAML tag.
remove_tag() -> None
set_scalar_style()¶
Set (or replace) this node's scalar style. No-op on non-scalar nodes. Recognized values: "plain", "single_quoted", "double_quoted", "literal", "folded".
set_scalar_style(style: str) -> None
set_flow_style()¶
Set (or replace) this node's flow style. True emits flow ({}/[]), False emits block. No-op on non-container nodes.
set_flow_style(flow: bool) -> None
set_chomping()¶
Set (or replace) this node's chomping indicator. Recognized values: "strip" (-), "clip" (default), "keep" (+). No-op on non-scalar nodes.
set_chomping(chomp: str) -> None
sort_keys()¶
Sort the keys of this mapping node in place. No-op on non-mapping nodes.
sort_keys() -> None
move()¶
Move this subtree to a new path in the same document (copies the subtree to
new_path, then removes the source). new_path is an absolute JSONPath.
move(new_path: str) -> None
to_yaml()¶
Serialize this subtree to a YAML string.
to_yaml() -> str
copy()¶
Deep-copy this subtree as a standalone Python value (dict/list/scalar),
detached from the document. Useful for duplicating a subtree to paste
elsewhere via set_value().
copy() -> Any
is_valid()¶
Check if the parent document is still alive and unmodified.
is_valid() -> bool
release()¶
Release the reference to the parent document, marking this node as stale.
release() -> None
After calling release(), any access to this node will emit a RuntimeWarning and raise YamlDocumentError.
Dunder Methods¶
__repr__()¶
__repr__() -> str
Returns Node(root_type=<type>, path=<path>) for valid nodes, Node(released) for released nodes, or Node(invalid) for stale nodes.
__eq__()¶
__eq__(other: object) -> bool
Two Node instances are equal if they share the same document, path, and alive state.
value_eq()¶
Compare this node's resolved value with another node or Python value. Unlike
__eq__ (reference identity), this compares the actual YAML value.
value_eq(other: object) -> bool
Stale Node Behavior¶
Stale nodes
A Node is tied to the document's revision at creation time. Any document
edit bumps the revision, so previously obtained nodes become stale. Always
re-find a node after editing the document.
A node becomes stale when:
- The parent
YamlDocumentis garbage collected release()is called explicitly- The document is modified after the node was created
Accessing a stale node emits a RuntimeWarning and raises YamlDocumentError:
>>> node = doc.node()
>>> doc.set("$.key", "new_value")
>>> node.value # (1)!
RuntimeWarning: Node is stale: the document was modified after this node was created
YamlDocumentError: document has been modified; re-find the node # (2)!
- Accessing a stale node triggers a
RuntimeWarningfirst. - Then raises
YamlDocumentError. Re-acquire the node viadoc.node().find(path).
Example¶
import pyrs_yaml
doc = pyrs_yaml.parse("""
a:
b: 1
c: [2, 3, 4]
d: hello
""")
# Get root node
root = doc.node()
print(root.root_type) # "mapping"
# Navigate
node = root.find("$.a.c[1]")
print(node.value) # 3
# Walk
for n in root.walk():
print(n._path, n.root_type)
# Filter
numbers = root.filter(lambda n: n.root_type == "scalar" and isinstance(n.value, int))
for n in numbers:
print(n._path, n.value) # ('a', 'b') 1, ('a', 'c', 0) 2, ...
# Mutate
root.find("$.a.b").set_value(42)
root.find("$.a.c").append(5)
root.find("$.d").rename("greeting")
root.find("$.a.c[0]").delete()
print(doc.to_yaml())
# a:
# b: 42
# c: [3, 4, 5]
# greeting: hello