Skip to content

ConfigNode

The core data structure: a single line of configuration in the tree.

ConfigNode

ConfigNode(
    text: str, indent: int, parent: ConfigNode | None = None
)

Bases: _Queryable

One line of configuration and its place in the hierarchy.

Every node knows its own (stripped) text, its parent, and its children. Top-level lines (those at column 0) have no parent; deeper lines point at the line they are indented beneath.

Create a node from its text, indentation depth, and parent.

Source code in networkconfparse/node.py
def __init__(
    self,
    text: str,
    indent: int,
    parent: ConfigNode | None = None,
) -> None:
    """Create a node from its text, indentation depth, and parent."""
    self.text = text
    self.indent = indent
    self.parent = parent
    self.children: list[ConfigNode] = []

text instance-attribute

text = text

indent instance-attribute

indent = indent

parent instance-attribute

parent = parent

children instance-attribute

children: list[ConfigNode] = []

ancestors property

ancestors: Iterator[ConfigNode]

Yield this node's ancestors, nearest parent first up to the top.

descendants property

descendants: Iterator[ConfigNode]

Yield every descendant, depth-first - equivalent to walk.

root property

root: ConfigNode

Return the top-level line this node belongs to (itself if top-level).

path property

path: list[str]

The chain of ancestor lines down to this one, top-most first.

For ip address ... nested under an interface this returns ["interface Gi0/0", "ip address ..."]. A top-level line's path is just its own text.

matches

matches(pattern: str | Pattern[str]) -> bool

Return whether this node's text matches pattern.

pattern may be a string (treated as a regular expression) or a pre-compiled pattern. Matching uses re.search, so the pattern need not be anchored to match anywhere in the line.

Source code in networkconfparse/node.py
def matches(self, pattern: str | re.Pattern[str]) -> bool:
    """Return whether this node's text matches ``pattern``.

    ``pattern`` may be a string (treated as a regular expression) or a
    pre-compiled pattern. Matching uses `re.search`, so the pattern
    need not be anchored to match anywhere in the line.
    """
    return re.search(pattern, self.text) is not None

walk

walk() -> Iterator[ConfigNode]

Yield every descendant node, depth-first (pre-order).

The object itself is not yielded; iteration descends into the immediate child nodes in configuration order.

Source code in networkconfparse/node.py
def walk(self) -> Iterator[ConfigNode]:
    """Yield every descendant node, depth-first (pre-order).

    The object itself is not yielded; iteration descends into the immediate
    child nodes in configuration order.
    """
    for node in self._query_nodes:
        yield node
        yield from node.walk()

find

find(
    pattern: str | Pattern[str] | None = None,
    *,
    where: Predicate | None = None,
) -> list[ConfigNode]

Return all descendant nodes matching pattern and/or where.

Searches the entire subtree, not just immediate children.

Source code in networkconfparse/node.py
def find(
    self,
    pattern: str | re.Pattern[str] | None = None,
    *,
    where: Predicate | None = None,
) -> list[ConfigNode]:
    """Return all descendant nodes matching ``pattern`` and/or ``where``.

    Searches the entire subtree, not just immediate children.
    """
    test = self._matcher(pattern, where)
    return [node for node in self.walk() if test(node)]

find_one

find_one(
    pattern: str | Pattern[str] | None = None,
    *,
    where: Predicate | None = None,
) -> ConfigNode | None

Return the first descendant matching pattern/where, or None.

Searches the whole subtree in pre-order.

Source code in networkconfparse/node.py
def find_one(
    self,
    pattern: str | re.Pattern[str] | None = None,
    *,
    where: Predicate | None = None,
) -> ConfigNode | None:
    """Return the first descendant matching ``pattern``/``where``, or ``None``.

    Searches the whole subtree in pre-order.
    """
    test = self._matcher(pattern, where)
    for node in self.walk():
        if test(node):
            return node
    return None

find_child

find_child(
    pattern: str | Pattern[str] | None = None,
    *,
    where: Predicate | None = None,
) -> ConfigNode | None

Return the first immediate child matching pattern/where.

Only immediate children are considered, in configuration order; returns None if none match.

Source code in networkconfparse/node.py
def find_child(
    self,
    pattern: str | re.Pattern[str] | None = None,
    *,
    where: Predicate | None = None,
) -> ConfigNode | None:
    """Return the first immediate child matching ``pattern``/``where``.

    Only immediate children are considered, in configuration order; returns
    ``None`` if none match.
    """
    test = self._matcher(pattern, where)
    for node in self._query_nodes:
        if test(node):
            return node
    return None

has_child

has_child(
    pattern: str | Pattern[str] | None = None,
    *,
    where: Predicate | None = None,
) -> bool

Return whether any immediate child matches pattern/where.

Source code in networkconfparse/node.py
def has_child(
    self,
    pattern: str | re.Pattern[str] | None = None,
    *,
    where: Predicate | None = None,
) -> bool:
    """Return whether any immediate child matches ``pattern``/``where``."""
    return self.find_child(pattern, where=where) is not None

find_with_child

find_with_child(
    pattern: str | Pattern[str] | None, child: Patterns
) -> list[ConfigNode]

Return nodes matching pattern that have child as direct children.

child is one regex or an iterable of them; every pattern must match some direct child (logical AND). Implemented with find and a where predicate.

Source code in networkconfparse/node.py
def find_with_child(
    self,
    pattern: str | re.Pattern[str] | None,
    child: Patterns,
) -> list[ConfigNode]:
    """Return nodes matching ``pattern`` that have ``child`` as direct children.

    ``child`` is one regex or an iterable of them; every pattern must match
    some direct child (logical AND). Implemented with `find` and a
    ``where`` predicate.
    """
    children = _as_list(child)
    return self.find(
        pattern,
        where=lambda node: all(node.has_child(c) for c in children),
    )

find_with_descendant

find_with_descendant(
    pattern: str | Pattern[str] | None, descendant: Patterns
) -> list[ConfigNode]

Return nodes matching pattern with descendant anywhere below.

descendant is one regex or an iterable of them; every pattern must match some descendant at any depth (logical AND). Implemented with find and a where predicate.

Source code in networkconfparse/node.py
def find_with_descendant(
    self,
    pattern: str | re.Pattern[str] | None,
    descendant: Patterns,
) -> list[ConfigNode]:
    """Return nodes matching ``pattern`` with ``descendant`` anywhere below.

    ``descendant`` is one regex or an iterable of them; every pattern must
    match some descendant at any depth (logical AND). Implemented with
    `find` and a ``where`` predicate.
    """
    descendants = _as_list(descendant)
    return self.find(
        pattern,
        where=lambda node: all(node.find_one(d) is not None for d in descendants),
    )

find_with_parent

find_with_parent(
    pattern: str | Pattern[str] | None, parent: Patterns
) -> list[ConfigNode]

Return nodes matching pattern whose direct parent matches parent.

parent is one regex or an iterable of them; every pattern must match the single parent line (logical AND). Implemented with find and a where predicate.

Source code in networkconfparse/node.py
def find_with_parent(
    self,
    pattern: str | re.Pattern[str] | None,
    parent: Patterns,
) -> list[ConfigNode]:
    """Return nodes matching ``pattern`` whose direct parent matches ``parent``.

    ``parent`` is one regex or an iterable of them; every pattern must match
    the single parent line (logical AND). Implemented with `find` and
    a ``where`` predicate.
    """
    parents = _as_list(parent)
    return self.find(
        pattern,
        where=lambda node: _parent_matches_all(node, parents),
    )

find_with_ancestor

find_with_ancestor(
    pattern: str | Pattern[str] | None,
    ancestor: Patterns,
    *,
    adjacent: bool = False,
) -> list[ConfigNode]

Return nodes matching pattern with ancestor above them.

ancestor is one regex or an iterable of them, all required (AND). With adjacent=False (the default) each pattern must match some ancestor, in any order. With adjacent=True the patterns must form a consecutive chain matched nearest-first: the first against the direct parent, the next against its parent, and so on. Implemented with find and a where predicate.

Source code in networkconfparse/node.py
def find_with_ancestor(
    self,
    pattern: str | re.Pattern[str] | None,
    ancestor: Patterns,
    *,
    adjacent: bool = False,
) -> list[ConfigNode]:
    """Return nodes matching ``pattern`` with ``ancestor`` above them.

    ``ancestor`` is one regex or an iterable of them, all required (AND).
    With ``adjacent=False`` (the default) each pattern must match some
    ancestor, in any order. With ``adjacent=True`` the patterns must form a
    consecutive chain matched nearest-first: the first against the direct
    parent, the next against its parent, and so on. Implemented with
    `find` and a ``where`` predicate.
    """
    ancestors = _as_list(ancestor)
    if adjacent:
        return self.find(
            pattern,
            where=lambda node: _adjacent_ancestors(node, ancestors),
        )
    return self.find(
        pattern,
        where=lambda node: _any_ancestor_each(node, ancestors),
    )

find_without_child

find_without_child(
    pattern: str | Pattern[str] | None, child: Patterns
) -> list[ConfigNode]

Return nodes matching pattern that lack every child pattern.

The negative counterpart of find_with_child. child is one regex or an iterable of them; a node matches when none of them match any direct child (the "none present" / NOR rule). For [a, b] a node qualifies only if it has no direct child matching a and none matching b - so a node carrying just one of them is excluded. Implemented with find and a where predicate.

Source code in networkconfparse/node.py
def find_without_child(
    self,
    pattern: str | re.Pattern[str] | None,
    child: Patterns,
) -> list[ConfigNode]:
    """Return nodes matching ``pattern`` that lack every ``child`` pattern.

    The negative counterpart of `find_with_child`. ``child`` is one regex
    or an iterable of them; a node matches when **none** of them match any
    direct child (the "none present" / NOR rule). For ``[a, b]`` a node
    qualifies only if it has no direct child matching ``a`` and none
    matching ``b`` - so a node carrying just one of them is excluded.
    Implemented with `find` and a ``where`` predicate.
    """
    children = _as_list(child)
    return self.find(
        pattern,
        where=lambda node: not any(node.has_child(c) for c in children),
    )

find_without_descendant

find_without_descendant(
    pattern: str | Pattern[str] | None, descendant: Patterns
) -> list[ConfigNode]

Return nodes matching pattern with none of descendant below.

The negative counterpart of find_with_descendant. descendant is one regex or an iterable of them; a node matches when none of them match any descendant at any depth (the "none present" / NOR rule). For [a, b] a node carrying just one of the two below it is excluded. Implemented with find and a where predicate.

Source code in networkconfparse/node.py
def find_without_descendant(
    self,
    pattern: str | re.Pattern[str] | None,
    descendant: Patterns,
) -> list[ConfigNode]:
    """Return nodes matching ``pattern`` with none of ``descendant`` below.

    The negative counterpart of `find_with_descendant`. ``descendant`` is
    one regex or an iterable of them; a node matches when **none** of them
    match any descendant at any depth (the "none present" / NOR rule). For
    ``[a, b]`` a node carrying just one of the two below it is excluded.
    Implemented with `find` and a ``where`` predicate.
    """
    descendants = _as_list(descendant)
    return self.find(
        pattern,
        where=lambda node: (
            not any(node.find_one(d) is not None for d in descendants)
        ),
    )

find_without_parent

find_without_parent(
    pattern: str | Pattern[str] | None, parent: Patterns
) -> list[ConfigNode]

Return nodes matching pattern whose parent matches no parent.

The negative counterpart of find_with_parent. parent is one regex or an iterable of them; a node matches when its direct parent matches none of them (the "none present" / NOR rule). A top-level node has no parent, so it can never sit beneath a matching one and always qualifies. For [a, b] a node whose parent matches just one of the two is excluded. Implemented with find and a where predicate.

Source code in networkconfparse/node.py
def find_without_parent(
    self,
    pattern: str | re.Pattern[str] | None,
    parent: Patterns,
) -> list[ConfigNode]:
    """Return nodes matching ``pattern`` whose parent matches no ``parent``.

    The negative counterpart of `find_with_parent`. ``parent`` is one regex
    or an iterable of them; a node matches when its direct parent matches
    **none** of them (the "none present" / NOR rule). A top-level node has
    no parent, so it can never sit beneath a matching one and always
    qualifies. For ``[a, b]`` a node whose parent matches just one of the
    two is excluded. Implemented with `find` and a ``where`` predicate.
    """
    parents = _as_list(parent)
    return self.find(
        pattern,
        where=lambda node: _parent_matches_none(node, parents),
    )

find_without_ancestor

find_without_ancestor(
    pattern: str | Pattern[str] | None, ancestor: Patterns
) -> list[ConfigNode]

Return nodes matching pattern with none of ancestor above.

The negative counterpart of find_with_ancestor. ancestor is one regex or an iterable of them; a node matches when none of them match any ancestor at any depth (the "none present" / NOR rule). A top-level node has no ancestors and always qualifies. For [a, b] a node with just one of the two anywhere above it is excluded.

Unlike find_with_ancestor, this takes no adjacent flag: negating an adjacent-chain match is rarely meaningful, so the only negative offered is "none of these appear anywhere in the ancestry." Implemented with find and a where predicate.

Source code in networkconfparse/node.py
def find_without_ancestor(
    self,
    pattern: str | re.Pattern[str] | None,
    ancestor: Patterns,
) -> list[ConfigNode]:
    """Return nodes matching ``pattern`` with none of ``ancestor`` above.

    The negative counterpart of `find_with_ancestor`. ``ancestor`` is one
    regex or an iterable of them; a node matches when **none** of them
    match any ancestor at any depth (the "none present" / NOR rule). A
    top-level node has no ancestors and always qualifies. For ``[a, b]`` a
    node with just one of the two anywhere above it is excluded.

    Unlike `find_with_ancestor`, this takes no ``adjacent`` flag: negating
    an adjacent-chain match is rarely meaningful, so the only negative
    offered is "none of these appear anywhere in the ancestry." Implemented
    with `find` and a ``where`` predicate.
    """
    ancestors = _as_list(ancestor)
    return self.find(
        pattern,
        where=lambda node: _no_ancestor_matches(node, ancestors),
    )