API Reference#

nutree.tree module#

Declare the Tree class.

MIN_PYTHON_VERSION_INFO = (3, 8)#

Minimal Python version that is supported by WsgiDAV

class Tree(name: str | None = None, *, factory: Type[Node] | None = None, calc_data_id: Callable[[Tree, Any], str | int] | None = None, shadow_attrs: bool = False)[source]#

Bases: object

A Tree object is a shallow wrapper around a single, invisible system root node. All visible toplevel nodes are direct children of this root node. Trees expose methods to iterate, search, copy, filter, serialize, etc.

A name string can be passed for enhanced printing.

If a factory is passed, it must be a class that is derived from Node and will be used to instantiate node instances.

calc_data_id can be a callback function that calculates data IDs from data objects (by default hash(data) is used).

Set shadow_attrs to true, to enable aliasing of node attributes, i.e. make node.data.NAME accessible as node.NAME.
See Shadow Attributes (Attribute Aliasing).

DEFAULT_CONNECTOR_STYLE = 'round43'#

Default connector prefixes format(style=...) argument.

DEFAULT_KEY_MAP = {'data_id': 'i', 'str': 's'}#

Default value for save(..., key_map=...) argument.

DEFAULT_VALUE_MAP = {}#

Default value for save(..., value_map=...) argument.

__contains__(data)[source]#

Implement data in tree syntax to check for node existence.

__delitem__(data)[source]#

Implement del tree[data] syntax to remove nodes.

__enter__()[source]#

Implement with tree: ... syntax to acquire an RLock.

__getitem__(data: object) Node[source]#

Implement tree[data] syntax to lookup a node.

data may be a plain string, data object, data_id, or node_id.

Note: This is a flexible and concise way to access tree nodes. However, find_all() or find_first() may be faster.

AmbiguousMatchError is raised if multiple matches are found. Use find_all() or find_first() instead to resolve this.

__iter__(method: IterMethod = IterMethod.PRE_ORDER) Iterator[Node]#

Implement for node in tree: ... syntax to iterate nodes depth-first.

__len__()[source]#

Make len(tree) return the number of nodes (also makes empty trees falsy).

add(child: Node | Tree | Any, *, before: Node | bool | int | None = None, deep: bool = None, data_id=None, node_id=None) Node#

Alias for add_child()

add_child(child: Node | Tree | Any, *, before: Node | bool | int | None = None, deep: bool = None, data_id=None, node_id=None) Node[source]#

Add a toplevel node.

See Node’s add_child() method for details.

calc_data_id(data) str | int[source]#

Called internally to calculate data_id for a data object.

This value is used to lookup nodes by data, identify clones, and for (de)serialization. It defaults to hash(data) but may be overloaded when the data objects have meaningful keys that should be used instead.

Note: By default, the __hash__() values of str and bytes objects are ‘salted’ with an unpredictable random value. Although they remain constant within an individual Python process, they are not predictable between repeated invocations of Python.

calc_height() int[source]#

Return the maximum depth of all nodes.

property children: list[Node]#

Return list of direct child nodes, i.e. toplevel nodes (list may be empty).

clear() None[source]#

Remove all nodes from this tree.

copy(*, name: str = None, predicate: Callable[[Node], None | bool | IterationControl] = None) Tree[source]#

Return a copy of this tree.

New Tree and Node instances are created. The new nodes reference the original data objects.

predicate may be passed to filter the result, which is equivalent to calling filtered()

See Node’s copy_to() and Iteration Callbacks method for details.

copy_to(target: Node | Tree, *, deep=True) None[source]#

Copy this tree’s nodes to another target.

See Node’s copy_to() method for details.

property count: int#

Return the total number of nodes.

property count_unique: int#

Return the total number of unique nodes.

Multiple references to the same data object (‘clones’) are only counted once. This is different from count(), which returns the number of all nodes.

static deserialize_mapper(parent: Node, data: dict) str | object | None[source]#

Used as default mapper argument for load().

diff(other: Tree, *, ordered=False, reduce=False) Tree[source]#

Compare this tree against other and return a merged, annotated copy.

The resulting tree contains a union of all nodes from this and the other tree. Additional metadata is added to the resulting nodes to classify changes from the perspective of this tree. For example a node that only exists in other, will have node.get_meta("dc") == DiffClassification.ADDED defined.

If ordered is true, changes in the child order are also considered a change.
If reduce is true, unchanged nodes are removed, leaving a compact tree with only the modifications.

See Diff and Merge for details.

filter(predicate: Callable[[Node], None | bool | IterationControl]) None[source]#

In-place removal of unmatching nodes.

See also Iteration Callbacks.

filtered(predicate: Callable[[Node], None | bool | IterationControl]) Tree[source]#

Return a filtered copy of this tree.

See also Iteration Callbacks.

find(data=None, *, match=None, data_id=None, node_id=None) Node | None#

Alias for find_first()

find_all(data=None, *, match=None, data_id=None, max_results: int = None) list[Node][source]#

Return a list of matching nodes (list may be empty).

See also Node’s find_all() method and Iteration Callbacks.

find_first(data=None, *, match=None, data_id=None, node_id=None) Node | None[source]#

Return the one matching node or None.

Note that ‘first’ sometimes means ‘one arbitrary’ matching node, which is not neccessarily the first of a specific iteration method. See also Node’s find_first() method and Iteration Callbacks.

first_child() Node | None[source]#

Return the first toplevel node.

format(*, repr=None, style=None, title=None, join='\n') str[source]#

Return a pretty string representation of the tree hierarchy.

See Node’s format() method for details.

format_iter(*, repr=None, style=None, title=None) Iterator[str][source]#

This variant of format() returns a line generator.

classmethod from_dict(obj: list[dict], *, mapper=None) Tree[source]#

Return a new Tree instance from a list of dicts.

See also to_dict_list() and Node’s find_first() methods, and Iteration Callbacks.

get_random_node() Node[source]#

Return a random node.

Note that there is also IterMethod.RANDOM_ORDER.

get_toplevel_nodes() list[Node][source]#

Return list of direct child nodes, i.e. toplevel nodes (may be empty, alias for children()).

iterator(method: IterMethod = IterMethod.PRE_ORDER) Iterator[Node][source]#

Traverse tree structure and yield nodes.

See Node’s iterator() method for details.

last_child() Node | None[source]#

Return the last toplevel node.

classmethod load(target: IO[str] | str | Path, *, mapper: Callable[[Node, dict], str | object] | None = None, file_meta: dict = None, auto_uncompress: bool = True) Tree[source]#

Create a new Tree instance from a file path or JSON file stream.

If file_meta is a dict, it receives the content if the file’s meta header.

See also save().

name#

Tree name used for logging

print(*, repr=None, style=None, title=None, join: str = '\n', file: IO[str] | None = None) None[source]#

Convenience method that simply runs print(self. format()).

save(target: IO[str] | str | Path, *, compression: bool | int = False, mapper: Callable[[Node, dict], None | dict] | None = None, meta: dict | None = None, key_map: Dict[str, str] | bool = True, value_map: Dict[str, List[str]] | bool = True) None[source]#

Store tree in a compact JSON file stream.

If target is a string, it is interpreted as a file path. Otherwise it must be a file object.

If compression is true, the file is compressed using gzip (zipfile.ZIP_DEFLATED). Other values are: zipfile.ZIP_STORED, zipfile.ZIP_BZIP2, zipfile.ZIP_LZMA. Pass False to disable compression and store as plain json.

See also (De)Serialize and to_list_iter() and load() methods.

serialize_mapper(node: Node, data: dict) dict | None[source]#

Used as default mapper argument for save().

sort(*, key=None, reverse=False, deep=True) None[source]#

Sort toplevel nodes (optionally recursively).

key defaults to attrgetter("name"), so children are sorted by their string representation.

property system_root: _SystemRootNode#
to_dict_list(*, mapper: Callable[[Node, dict], None | dict] | None = None) list[dict][source]#

Call Node’s to_dict() method for all child nodes and return list of results.

to_dot(*, add_root=True, unique_nodes=True, graph_attrs=None, node_attrs=None, edge_attrs=None, node_mapper=None, edge_mapper=None) Iterator[str][source]#

Generate a DOT formatted graph representation.

See Node’s to_dot() method for details.

to_dotfile(target: IO[str] | str | Path, *, format=None, add_root=True, unique_nodes=True, graph_attrs=None, node_attrs=None, edge_attrs=None, node_mapper=None, edge_mapper=None)[source]#

Serialize a DOT formatted graph representation.

Optionally convert to a Graphviz display formats. See Graphs for details.

to_list_iter(*, mapper: Callable[[Node, dict], None | dict] | None = None, key_map: Dict[str, str] | None = None, value_map: Dict[str, List[str]] | None = None) Iterator[dict][source]#

Yield a parent-referencing list of child nodes.

to_mermaid_flowchart(target: IO[str] | str | Path, *, as_markdown: bool = True, direction: Literal['LR', 'RL', 'TB', 'TD', 'BT'] = 'TD', title: str | bool | None = True, format: Literal['svg', 'pdf', 'png'] | None = None, mmdc_options: dict | None = None, add_root: bool = True, unique_nodes: bool = True, headers: Iterable[str] | None = None, node_mapper: Callable[[Node], str] | None = None, edge_mapper: Callable[[int, Node, int, Node], str] | None = None) Iterator[str][source]#

Serialize a Mermaid flowchart representation.

Optionally convert to a Graphviz display formats. See Graphs for details.

to_rdf_graph()[source]#

Return an instance of rdflib.Graph.

See Graphs for details.

visit(callback: TraversalCallbackType, *, method=IterMethod.PRE_ORDER, memo=None) Any | None[source]#

Call callback(node, memo) for all nodes.

See Node’s visit() method and Iteration Callbacks for details.

nutree.node module#

Declare the Node class.

class Node(data, *, parent: Node, data_id: str | int | None = None, node_id: int | None = None, meta: dict = None)[source]#

Bases: object

A Node represents a single element in the tree. It is a shallow wrapper around a user data instance, that adds navigation, modification, and other functionality.

Node objects are rarely created using this contructor, but indirectly by invoking helper methods like add_child(), etc.

data

is the arbitrary object that this node will hold.

parent

is the parent Node() instance. This node will also inherit the tree reference from it.

data_id

is an optional integer or string that will be used as ID (instead of calculating hash(data)). A tree may contain more than one node with the same data and data_id. In this case we call the nodes ‘clones’.

node_id

is an optional integer, that is used as unique ID for this node. Even ‘clones’ must have unique node IDs. The default is calculated as id(self).

meta

is an optional dictionary. See also get_meta(), set_meta(), update_meta(), and clear_meta().

DEFAULT_RENDER_REPR = '{node.data!r}'#

Default value for repr argument when formatting data for print/display.

__eq__(other) bool[source]#

Implement node == other syntax to compare embedded data.

If other is a Node instance, self.data == other.data is evaluated. Otherwise self.data == other is compared.

Use node is other syntax instead to check if two nodes are truly identical.

__getattr__(name: str) Any[source]#

Implement node.NAME aliasing to node.data.NAME.

See Shadow Attributes (Attribute Aliasing).

__iter__(method=IterMethod.PRE_ORDER, *, add_self=False) Iterator[Node]#

Implement for subnode in node: ... syntax to iterate descendant nodes.

add(child: Node | Tree | Any, *, before: Node | bool | int | None = None, deep: bool = None, data_id=None, node_id=None) Node#

Alias for add_child()

add_child(child: Node | Tree | Any, *, before: Node | bool | int | None = None, deep: bool = None, data_id=None, node_id=None) Node[source]#

Append or insert a new subnode or branch as child.

If child is an existing Node instance, a copy of this node will be created, that references the same child.data object.
If deep is true, the children are copied recursively.

If child is a Tree, all of its topnodes are added recursively (unless deep is false).

If child is neither a Node nor a Tree, child itself will become the data object of a new node that is added.

The source node may come from the same or from a foreign tree.
Note that adding the same data below one parent is not allowed.

If this node has no children yet, the new node is created as first child. Otherwise, it will be appended to the existing children by default.
The before option may be used to specifiy the position:

  • False, None: append the new node as last child

  • True, 0: prepend the new node as first child

  • <int>: prepend the new node before the existing child with this index

  • <Node>: prepend the new node before this child node

Parameters:
  • child (Node|Tree|Any) – Either an existing Node or a data object.

  • before (bool|int|Node|None) – Optional position.

  • deep (bool) – Copy descendants if any. This argument defaults to false when child is a Node. It defaults to true when child is a Tree.

  • data_id (str|int|None) – Allow to override the new node’s data_id. This argument is only allowed for single nodes, but not for deep copies. Default None will to calculate from hash(node.data).

  • node_id (str|int|None) – Optional custom unique node key (defaults to id(node)) This argument is only allowed for single nodes, but not for deep copies.

Returns:

the new Node instance

append_child(child: Node | Tree | Any, *, deep=None, data_id=None, node_id=None)[source]#

Append a new subnode.

This is a shortcut for add_child() with before=None.

append_sibling(child: Node | Tree | Any, *, deep=None, data_id=None, node_id=None) Node[source]#

Add a new node after self.

This method calls add_child() on self.parent.

calc_depth() int[source]#

Return the distance to the root node (1 for toplevel nodes).

calc_height() int[source]#

Return the maximum depth of all descendants (0 for leaves).

property children: list[Node]#

Return list of direct child nodes (list may be empty).

clear_meta(key: str = None) None[source]#

Reset all metadata or a distinct entry.

copy(*, add_self=True, predicate: PredicateCallbackType = None) Tree[source]#

Return a new Tree instance from this branch.

See also Iteration Callbacks.

copy_to(target: Node | Tree, *, add_self=True, before: Node | bool | int | None = None, deep: bool = False) Node[source]#

Copy this node to another parent and return the new node.

If add_self is set, a copy of this node becomes a child of target. Otherwise copies of all children of this node are created below target.

By default new nodes are appended to existing children. The before argument defines an alternative positioning. It is only available when add_self is true. See add_child() for a description of before.

If deep is set, all descendants are copied recursively.

count_descendants(*, leaves_only=False) int[source]#

Return number of descendant nodes, not counting self.

property data: Any#

Return the wrapped data instance (use set_data() to modify).

property data_id: str | int#

Return the wrapped data instance’s id (use set_data() to modify).

depth() int[source]#

Return the distance to the root node (1 for toplevel nodes).

filter(predicate: Callable[[Node], None | bool | IterationControl]) None[source]#

In-place removal of mismatching nodes.

See also Iteration Callbacks.

filtered(predicate: PredicateCallbackType) Tree[source]#

Return a filtered copy of this node and descendants as tree.

See also Iteration Callbacks.

find(data=None, *, match=None, data_id=None) Node | None#

Alias for find_first()

find_all(data=None, *, match=None, data_id=None, add_self=False, max_results=None) list[Node][source]#

Return a list of matching nodes (list may be empty).

See also Iteration Callbacks.

find_first(data=None, *, match=None, data_id=None) Node | None[source]#

Return the first matching node or None.

See also Iteration Callbacks.

first_child() Node | None[source]#

First direct child node or None if no children exist.

first_sibling() Node[source]#

Return first sibling (may be self).

format(*, repr=None, style=None, add_self=True, join='\n') str[source]#

Return a pretty string representation of the node hierarchy.

more

Parameters:
  • repr (str) – A string or callback that defines how the node is rendered after the connector prefixes are generated.

  • style (str) – A string that defines the connector type, e.g. “round42” will produce “│ “, “╰─”, “├─”.

  • add_self (bool) – If false, only the descendants of this node are formatted.

  • join (str) – A string that is used to terminate the single lines. Defaults to “n”, but may be set to “, “ together with style=”list” to create a single line output.

Example

print(node.format(repr=”{node.name}”, style=”round42”))

format_iter(*, repr=None, style=None, add_self=True) Iterator[str][source]#

This variant of format() returns a line generator.

from_dict(obj: list[dict], *, mapper: Callable[[Node, dict], str | object] | None = None) None[source]#

Append copies of all source children to self.

get_children() list[Node][source]#

Return list of direct child nodes (list may be empty).

get_clones(*, add_self=False) list[Node][source]#

Return a list of all nodes that reference the same data if any.

get_common_ancestor(other: Node) Node | None[source]#

Return the nearest node that contains self and other (may be None).

get_index() int[source]#

Return index in sibling list.

get_meta(key: str, default=None) Any[source]#

Return metadata value.

get_parent_list(*, add_self=False, bottom_up=False) list[Node][source]#

Return ordered list of all parent nodes.

get_path(*, add_self=True, separator='/', repr='{node.name}') str[source]#

Return a breadcrumb string, e.g. ‘/A/a1/a12’.

get_siblings(*, add_self=False) list[Node][source]#

Return a list of all sibling entries of self (excluding self) if any.

get_top() Node[source]#

Return toplevel ancestor (may be self).

has_children() bool[source]#

Return true if this node has one or more children.

is_ancestor_of(other: Node) bool[source]#

Return true if this node is a parent, grandparent, … of other.

is_clone() bool[source]#

Return true if this node’s data is referenced at least one more time.

is_descendant_of(other: Node) bool[source]#

Return true if this node is direct or indirect child of other.

is_first_sibling() bool[source]#

Return true if this node is the first sibling, i.e. the first child of its parent.

is_last_sibling() bool[source]#

Return true if this node is the last sibling, i.e. the last child of its parent.

is_leaf() bool[source]#

Return true if this node is an end node, i.e. has no children.

is_system_root() bool[source]#

Return true if this node is the invisible system root _SystemRootNode.

is_top() bool[source]#

Return true if this node has no parent.

iterator(method=IterMethod.PRE_ORDER, *, add_self=False) Iterator[Node][source]#

Generator that walks the hierarchy.

last_child() Node | None[source]#

Last direct child node or None if no children exist.

last_sibling() Node[source]#

Return last node, that share own parent (may be self).

property meta: dict | None#

Return the node’s metadata dictionary or None if empty.

See also get_meta(), set_meta(), update_meta(), clear_meta().

move_to(new_parent: Node | Tree, *, before: Node | bool | int | None = None)[source]#

Move this node to another parent.

By default, the node is appended to existing children. See add_child() for a description of before.

property name: str#

String representation of the embedded data object.

next_sibling() Node | None[source]#

Return successor or None, if node is last sibling.

property node_id: int#

Return the node’s unique key.

property parent: Node#

Return parent node or None for toplevel nodes.

property path: str#

All ancestor names including self, starting with and separated by ‘/’.

prepend_child(child: Node | Tree | Any, *, deep=None, data_id=None, node_id=None)[source]#

Prepend a new subnode.

This is a shortcut for add_child() with before=True.

prepend_sibling(child: Node | Tree | Any, *, deep=None, data_id=None, node_id=None) Node[source]#

Add a new node before self.

This method calls add_child() on self.parent.

prev_sibling() Node | None[source]#

Predecessor or None, if node is first sibling.

remove(*, keep_children=False, with_clones=False) None[source]#

Remove this node.

If keep_children is true, all children will be moved one level up, so they become siblings, before this node is removed.

If with_clones is true, all nodes that reference the same data instance are removed as well.

remove_children() None[source]#

Remove all children of this node, making it a leaf node.

rename(new_name: str) None[source]#

Set self.data to a new string (assuming plain string node).

set_data(data, *, data_id=None, with_clones: bool = None) None[source]#

Change node’s data and/or data_id and update bookkeeping.

set_meta(key: str, value) None[source]#

Set metadata value (pass value None to remove).

sort_children(*, key=None, reverse=False, deep=False) None[source]#

Sort child nodes.

key defaults to attrgetter("name"), so children are sorted by their string representation.

to_dict(*, mapper: Callable[[Node, dict], None | dict] | None = None) dict[source]#

Return a nested dict of this node and its children.

to_dot(*, add_self=False, unique_nodes=True, graph_attrs: dict = None, node_attrs: dict = None, edge_attrs: dict = None, node_mapper: Callable[[Node, dict], None | Any] = None, edge_mapper: Callable[[Node, dict], None | Any] = None) Iterator[str][source]#

Generate a DOT formatted graph representation.

See Graphs for details.

to_list_iter(*, mapper: Callable[[Node, dict], None | dict] | None = None, key_map: Dict[str, str] | None = None, value_map: Dict[str, List[str]] | None = None) Iterator[dict][source]#

Yield children as parent-referencing list.

`py [(parent_key, data)] `

to_mermaid_flowchart(target: IO[str] | str | Path, *, as_markdown: bool = True, direction: Literal['LR', 'RL', 'TB', 'TD', 'BT'] = 'TD', title: str | bool | None = True, format: Literal['svg', 'pdf', 'png'] | None = None, mmdc_options: dict | None = None, add_self: bool = True, unique_nodes: bool = True, headers: Iterable[str] | None = None, node_mapper: Callable[[Node], str] | None = None, edge_mapper: Callable[[int, Node, int, Node], str] | None = None) None[source]#

Serialize a Mermaid flowchart representation.

Optionally convert to a Graphviz display formats. See Graphs for details.

to_rdf_graph(*, add_self: bool = True, node_mapper: Callable[[None, None, Node], None | bool] = None)[source]#

Return an instance of rdflib.Graph.

See Graphs for details.

property tree: Tree#

Return container Tree instance.

update_meta(values: dict, *, replace: bool = False) None[source]#

Add values dict to current metadata.

If replace is true, previous metatdata will be cleared.

visit(callback: Callable[[Node, Any], None | bool | StopTraversal | SkipBranch], *, add_self=False, method=IterMethod.PRE_ORDER, memo=None) None | Any[source]#

Call callback(node, memo) for all subnodes.

The callback may return SkipBranch (or an instance thereof) to omit child nodes but continue traversal otherwise. Raising SkipBranch has the same effect.

The callback may return False or StopIteration to immediately interrupt traversal. Raising StopTraversal(value) has the same effect but also allows to specify a return value for the visit method.
See also Iteration Callbacks.

Parameters:
  • callback (function(node, memo)) – Callback function(node, memo)

  • add_self (bool) – If true, this node will also be visited (typically as first call).

  • method (IterMethod) – Traversal method, defaults to pre-order, depth-first search.

  • memo (Any) – This value will be passed to all calls and may be useful to implement caching or collect and return traversal results. If no memo argument is passed, an empty dict is created at start, which has a life-span of the traversal only.

nutree.typed_tree module#

Declare the TypedTree class.

class ANY_KIND[source]#

Bases: object

Special argument value for some methods that access child nodes.

class TypedNode(kind: str, data, *, parent: TypedNode, data_id=None, node_id=None, meta: dict = None)[source]#

Bases: Node

A special node variant, derived from Node and used by TypedTree.

In addition to Node, we have a kind member to define the node’s type.

DEFAULT_RENDER_REPR = '{node.kind} {node.data}'#

Default value for repr argument when formatting data for print/display.

__eq__(other) bool#

Implement node == other syntax to compare embedded data.

If other is a Node instance, self.data == other.data is evaluated. Otherwise self.data == other is compared.

Use node is other syntax instead to check if two nodes are truly identical.

__getattr__(name: str) Any#

Implement node.NAME aliasing to node.data.NAME.

See Shadow Attributes (Attribute Aliasing).

__iter__(method=IterMethod.PRE_ORDER, *, add_self=False) Iterator[Node]#

Implement for subnode in node: ... syntax to iterate descendant nodes.

add(child: TypedNode | TypedTree | Any, *, kind: str = None, before: TypedNode | bool | int | None = None, deep: bool = None, data_id=None, node_id=None) TypedNode#

Alias for add_child()

add_child(child: TypedNode | TypedTree | Any, *, kind: str = None, before: TypedNode | bool | int | None = None, deep: bool = None, data_id=None, node_id=None) TypedNode[source]#

See …

append_child(child: TypedNode | TypedTree | Any, *, kind: str = None, deep=None, data_id=None, node_id=None)[source]#

Append a new subnode.

This is a shortcut for add_child() with before=None.

append_sibling(child: TypedNode | TypedTree | Any, *, deep=None, data_id=None, node_id=None) TypedNode[source]#

Add a new node of same kind after self.

This method calls add_child() on self.parent.

calc_depth() int#

Return the distance to the root node (1 for toplevel nodes).

calc_height() int#

Return the maximum depth of all descendants (0 for leaves).

property children: list[TypedNode]#

Return list of direct child nodes (list may be empty).

Note that this property returns all children, independent of the kind. See also get_children().

clear_meta(key: str = None) None#

Reset all metadata or a distinct entry.

copy(*, add_self=True, predicate=None) TypedTree[source]#

Return a new TypedTree instance from this branch.

See also _add_from() and Iteration Callbacks.

copy_to(target: Node | Tree, *, add_self=True, before: Node | bool | int | None = None, deep: bool = False) Node#

Copy this node to another parent and return the new node.

If add_self is set, a copy of this node becomes a child of target. Otherwise copies of all children of this node are created below target.

By default new nodes are appended to existing children. The before argument defines an alternative positioning. It is only available when add_self is true. See add_child() for a description of before.

If deep is set, all descendants are copied recursively.

count_descendants(*, leaves_only=False) int#

Return number of descendant nodes, not counting self.

property data: Any#

Return the wrapped data instance (use set_data() to modify).

property data_id: str | int#

Return the wrapped data instance’s id (use set_data() to modify).

depth() int#

Return the distance to the root node (1 for toplevel nodes).

filter(predicate: Callable[[Node], None | bool | IterationControl]) None#

In-place removal of mismatching nodes.

See also Iteration Callbacks.

filtered(predicate: Callable[[Node], None | bool | IterationControl]) TypedTree[source]#

Return a filtered copy of this node and descendants as tree.

See also Iteration Callbacks.

find(data=None, *, match=None, data_id=None) Node | None#

Return the first matching node or None.

See also Iteration Callbacks.

find_all(data=None, *, match=None, data_id=None, add_self=False, max_results=None) list[Node]#

Return a list of matching nodes (list may be empty).

See also Iteration Callbacks.

find_first(data=None, *, match=None, data_id=None) Node | None#

Return the first matching node or None.

See also Iteration Callbacks.

first_child(kind: str | ANY_KIND) TypedNode | None[source]#

First direct child node or None if no children exist.

first_sibling(*, any_kind=False) TypedNode[source]#

Return first sibling of the same kind (may be self).

format(*, repr=None, style=None, add_self=True, join='\n') str#

Return a pretty string representation of the node hierarchy.

more

Parameters:
  • repr (str) – A string or callback that defines how the node is rendered after the connector prefixes are generated.

  • style (str) – A string that defines the connector type, e.g. “round42” will produce “│ “, “╰─”, “├─”.

  • add_self (bool) – If false, only the descendants of this node are formatted.

  • join (str) – A string that is used to terminate the single lines. Defaults to “n”, but may be set to “, “ together with style=”list” to create a single line output.

Example

print(node.format(repr=”{node.name}”, style=”round42”))

format_iter(*, repr=None, style=None, add_self=True) Iterator[str]#

This variant of format() returns a line generator.

from_dict(obj: list[dict], *, mapper: Callable[[Node, dict], str | object] | None = None) None#

Append copies of all source children to self.

get_children(kind: str | ANY_KIND) list[TypedNode][source]#

Return list of direct child nodes of a given type (list may be empty).

get_clones(*, add_self=False) list[Node]#

Return a list of all nodes that reference the same data if any.

get_common_ancestor(other: Node) Node | None#

Return the nearest node that contains self and other (may be None).

get_index(*, any_kind=False) int[source]#

Return index in sibling list.

get_meta(key: str, default=None) Any#

Return metadata value.

get_parent_list(*, add_self=False, bottom_up=False) list[Node]#

Return ordered list of all parent nodes.

get_path(*, add_self=True, separator='/', repr='{node.name}') str#

Return a breadcrumb string, e.g. ‘/A/a1/a12’.

get_siblings(*, add_self=False, any_kind=False) list[TypedNode][source]#

Return a list of all sibling entries of self (excluding self) if any.

get_top() Node#

Return toplevel ancestor (may be self).

has_children(kind: str | ANY_KIND) bool[source]#

Return true if this node has one or more children.

is_ancestor_of(other: Node) bool#

Return true if this node is a parent, grandparent, … of other.

is_clone() bool#

Return true if this node’s data is referenced at least one more time.

is_descendant_of(other: Node) bool#

Return true if this node is direct or indirect child of other.

is_first_sibling(*, any_kind=False) bool[source]#

Return true if this node is the first sibling, i.e. the first child of its parent.

is_last_sibling(*, any_kind=False) bool[source]#

Return true if this node is the last sibling, i.e. the last child of this kind of its parent.

is_leaf() bool#

Return true if this node is an end node, i.e. has no children.

is_system_root() bool#

Return true if this node is the invisible system root _SystemRootNode.

is_top() bool#

Return true if this node has no parent.

iterator(method=IterMethod.PRE_ORDER, *, add_self=False) Iterator[Node][source]#

Generator that walks the hierarchy.

property kind: str#
last_child(kind: str | ANY_KIND) TypedNode | None[source]#

Last direct child node or None if no children exist.

last_sibling(*, any_kind=False) TypedNode[source]#

Return last sibling of the same kind (may be self).

property meta: dict | None#

Return the node’s metadata dictionary or None if empty.

See also get_meta(), set_meta(), update_meta(), clear_meta().

move_to(new_parent: TypedNode | TypedTree, *, before: TypedNode | bool | int | None = None)[source]#

Move this node before or after new_parent.

property name: str#

String representation of the embedded data object.

next_sibling(*, any_kind=False) TypedNode | None[source]#

Return successor of the same kind or None if node is last sibling.

property node_id: int#

Return the node’s unique key.

property parent: TypedNode#

Return parent node or None for toplevel nodes.

property path: str#

All ancestor names including self, starting with and separated by ‘/’.

prepend_child(child: TypedNode | TypedTree | Any, *, kind: str = None, deep=None, data_id=None, node_id=None)[source]#

Prepend a new subnode.

This is a shortcut for add_child() with before=True.

prepend_sibling(child: TypedNode | TypedTree | Any, *, deep=None, data_id=None, node_id=None) TypedNode[source]#

Add a new node of same kind before self.

This method calls add_child() on self.parent.

prev_sibling(*, any_kind=False) TypedNode | None[source]#

Return predecessor of the same kind or None if node is first sibling.

remove(*, keep_children=False, with_clones=False) None#

Remove this node.

If keep_children is true, all children will be moved one level up, so they become siblings, before this node is removed.

If with_clones is true, all nodes that reference the same data instance are removed as well.

remove_children() None#

Remove all children of this node, making it a leaf node.

rename(new_name: str) None#

Set self.data to a new string (assuming plain string node).

set_data(data, *, data_id=None, with_clones: bool = None) None#

Change node’s data and/or data_id and update bookkeeping.

set_meta(key: str, value) None#

Set metadata value (pass value None to remove).

sort_children(*, key=None, reverse=False, deep=False) None#

Sort child nodes.

key defaults to attrgetter("name"), so children are sorted by their string representation.

to_dict(*, mapper: Callable[[Node, dict], None | dict] | None = None) dict#

Return a nested dict of this node and its children.

to_dot(*, add_self=False, unique_nodes=True, graph_attrs: dict = None, node_attrs: dict = None, edge_attrs: dict = None, node_mapper: Callable[[Node, dict], None | Any] = None, edge_mapper: Callable[[Node, dict], None | Any] = None) Iterator[str][source]#

Generate a DOT formatted graph representation.

See Graphs for details.

to_list_iter(*, mapper: Callable[[Node, dict], None | dict] | None = None, key_map: Dict[str, str] | None = None, value_map: Dict[str, List[str]] | None = None) Iterator[dict]#

Yield children as parent-referencing list.

`py [(parent_key, data)] `

to_mermaid_flowchart(target: IO[str] | str | Path, *, as_markdown: bool = True, direction: Literal['LR', 'RL', 'TB', 'TD', 'BT'] = 'TD', title: str | bool | None = True, format: Literal['svg', 'pdf', 'png'] | None = None, mmdc_options: dict | None = None, add_self: bool = True, unique_nodes: bool = True, headers: Iterable[str] | None = None, node_mapper: Callable[[Node], str] | None = None, edge_mapper: Callable[[int, Node, int, Node], str] | None = None) None#

Serialize a Mermaid flowchart representation.

Optionally convert to a Graphviz display formats. See Graphs for details.

to_rdf_graph(*, add_self: bool = True, node_mapper: Callable[[None, None, Node], None | bool] = None)#

Return an instance of rdflib.Graph.

See Graphs for details.

property tree: Tree#

Return container Tree instance.

update_meta(values: dict, *, replace: bool = False) None#

Add values dict to current metadata.

If replace is true, previous metatdata will be cleared.

visit(callback: Callable[[Node, Any], None | bool | StopTraversal | SkipBranch], *, add_self=False, method=IterMethod.PRE_ORDER, memo=None) None | Any#

Call callback(node, memo) for all subnodes.

The callback may return SkipBranch (or an instance thereof) to omit child nodes but continue traversal otherwise. Raising SkipBranch has the same effect.

The callback may return False or StopIteration to immediately interrupt traversal. Raising StopTraversal(value) has the same effect but also allows to specify a return value for the visit method.
See also Iteration Callbacks.

Parameters:
  • callback (function(node, memo)) – Callback function(node, memo)

  • add_self (bool) – If true, this node will also be visited (typically as first call).

  • method (IterMethod) – Traversal method, defaults to pre-order, depth-first search.

  • memo (Any) – This value will be passed to all calls and may be useful to implement caching or collect and return traversal results. If no memo argument is passed, an empty dict is created at start, which has a life-span of the traversal only.

class TypedTree(name: str | None = None, *, factory: Type[Node] | None = None, calc_data_id: Callable[[Tree, Any], str | int] | None = None, shadow_attrs: bool = False)[source]#

Bases: Tree

A special tree variant, derived from Tree, that uses TypedNode objects, which maintain an addition kind attribute.

See Typed Tree for details.

DEFAULT_CHILD_TYPE = 'child'#

Default value for add_child when loading.

DEFAULT_CONNECTOR_STYLE = 'round43'#

Default connector prefixes format(style=...) argument.

DEFAULT_KEY_MAP = {'data_id': 'i', 'kind': 'k', 'str': 's'}#

Default value for key_map argument when saving

DEFAULT_VALUE_MAP = {}#

Default value for value_map argument when saving

__contains__(data)#

Implement data in tree syntax to check for node existence.

__delitem__(data)#

Implement del tree[data] syntax to remove nodes.

__enter__()#

Implement with tree: ... syntax to acquire an RLock.

__iter__(method: IterMethod = IterMethod.PRE_ORDER) Iterator[Node]#

Traverse tree structure and yield nodes.

See Node’s iterator() method for details.

__len__()#

Make len(tree) return the number of nodes (also makes empty trees falsy).

add(child: TypedNode | TypedTree | Any, *, kind: str = None, before: TypedNode | bool | int | None = None, deep: bool = None, data_id=None, node_id=None) TypedNode#

Alias for add_child()

add_child(child: TypedNode | TypedTree | Any, *, kind: str = None, before: TypedNode | bool | int | None = None, deep: bool = None, data_id=None, node_id=None) TypedNode[source]#

Add a toplevel node.

See Node’s add_child() method for details.

calc_data_id(data) str | int#

Called internally to calculate data_id for a data object.

This value is used to lookup nodes by data, identify clones, and for (de)serialization. It defaults to hash(data) but may be overloaded when the data objects have meaningful keys that should be used instead.

Note: By default, the __hash__() values of str and bytes objects are ‘salted’ with an unpredictable random value. Although they remain constant within an individual Python process, they are not predictable between repeated invocations of Python.

calc_height() int#

Return the maximum depth of all nodes.

property children: list[Node]#

Return list of direct child nodes, i.e. toplevel nodes (list may be empty).

clear() None#

Remove all nodes from this tree.

copy(*, name: str = None, predicate: Callable[[Node], None | bool | IterationControl] = None) Tree#

Return a copy of this tree.

New Tree and Node instances are created. The new nodes reference the original data objects.

predicate may be passed to filter the result, which is equivalent to calling filtered()

See Node’s copy_to() and Iteration Callbacks method for details.

copy_to(target: Node | Tree, *, deep=True) None#

Copy this tree’s nodes to another target.

See Node’s copy_to() method for details.

property count: int#

Return the total number of nodes.

property count_unique: int#

Return the total number of unique nodes.

Multiple references to the same data object (‘clones’) are only counted once. This is different from count(), which returns the number of all nodes.

static deserialize_mapper(parent: Node, data: dict) str | object | None[source]#

Used as default mapper argument for load().

diff(other: Tree, *, ordered=False, reduce=False) Tree#

Compare this tree against other and return a merged, annotated copy.

The resulting tree contains a union of all nodes from this and the other tree. Additional metadata is added to the resulting nodes to classify changes from the perspective of this tree. For example a node that only exists in other, will have node.get_meta("dc") == DiffClassification.ADDED defined.

If ordered is true, changes in the child order are also considered a change.
If reduce is true, unchanged nodes are removed, leaving a compact tree with only the modifications.

See Diff and Merge for details.

filter(predicate: Callable[[Node], None | bool | IterationControl]) None#

In-place removal of unmatching nodes.

See also Iteration Callbacks.

filtered(predicate: Callable[[Node], None | bool | IterationControl]) Tree#

Return a filtered copy of this tree.

See also Iteration Callbacks.

find(data=None, *, match=None, data_id=None, node_id=None) Node | None#

Return the one matching node or None.

Note that ‘first’ sometimes means ‘one arbitrary’ matching node, which is not neccessarily the first of a specific iteration method. See also Node’s find_first() method and Iteration Callbacks.

find_all(data=None, *, match=None, data_id=None, max_results: int = None) list[Node]#

Return a list of matching nodes (list may be empty).

See also Node’s find_all() method and Iteration Callbacks.

find_first(data=None, *, match=None, data_id=None, node_id=None) Node | None#

Return the one matching node or None.

Note that ‘first’ sometimes means ‘one arbitrary’ matching node, which is not neccessarily the first of a specific iteration method. See also Node’s find_first() method and Iteration Callbacks.

first_child(kind: str | ANY_KIND) TypedNode | None[source]#

Return the first toplevel node.

format(*, repr=None, style=None, title=None, join='\n') str#

Return a pretty string representation of the tree hierarchy.

See Node’s format() method for details.

format_iter(*, repr=None, style=None, title=None) Iterator[str]#

This variant of format() returns a line generator.

classmethod from_dict(obj: list[dict], *, mapper=None) Tree#

Return a new Tree instance from a list of dicts.

See also to_dict_list() and Node’s find_first() methods, and Iteration Callbacks.

get_random_node() Node#

Return a random node.

Note that there is also IterMethod.RANDOM_ORDER.

get_toplevel_nodes() list[Node]#

Return list of direct child nodes, i.e. toplevel nodes (may be empty, alias for children()).

iter_by_type(kind: str | ANY_KIND) Iterator[TypedNode][source]#
iterator(method: IterMethod = IterMethod.PRE_ORDER) Iterator[Node]#

Traverse tree structure and yield nodes.

See Node’s iterator() method for details.

last_child(kind: str | ANY_KIND) TypedNode | None[source]#

Return the last toplevel node.

classmethod load(target: IO[str] | str | Path, *, mapper: Callable[[Node, dict], str | object] | None = None, file_meta: dict = None) TypedTree[source]#

Create a new TypedTree instance from a JSON file stream.

See also Tree’s save() and load() methods.

name#

Tree name used for logging

print(*, repr=None, style=None, title=None, join: str = '\n', file: IO[str] | None = None) None#

Convenience method that simply runs print(self. format()).

save(target: IO[str] | str | Path, *, mapper: Callable[[Node, dict], None | dict] | None = None, meta: dict | None = None, key_map: Dict[str, str] | bool = True, value_map: Dict[str, List[str]] | bool = True) None[source]#

Store tree in a compact JSON file stream.

See also (De)Serialize and to_list_iter() and load() methods.

serialize_mapper(node: Node, data: dict) dict | None#

Used as default mapper argument for save().

sort(*, key=None, reverse=False, deep=True) None#

Sort toplevel nodes (optionally recursively).

key defaults to attrgetter("name"), so children are sorted by their string representation.

property system_root: _SystemRootNode#
to_dict_list(*, mapper: Callable[[Node, dict], None | dict] | None = None) list[dict]#

Call Node’s to_dict() method for all child nodes and return list of results.

to_dot(*, add_root=True, unique_nodes=True, graph_attrs=None, node_attrs=None, edge_attrs=None, node_mapper=None, edge_mapper=None) Iterator[str]#

Generate a DOT formatted graph representation.

See Node’s to_dot() method for details.

to_dotfile(target: IO[str] | str | Path, *, format=None, add_root=True, unique_nodes=True, graph_attrs=None, node_attrs=None, edge_attrs=None, node_mapper=None, edge_mapper=None)#

Serialize a DOT formatted graph representation.

Optionally convert to a Graphviz display formats. See Graphs for details.

to_list_iter(*, mapper: Callable[[Node, dict], None | dict] | None = None, key_map: Dict[str, str] | None = None, value_map: Dict[str, List[str]] | None = None) Iterator[dict]#

Yield a parent-referencing list of child nodes.

to_mermaid_flowchart(target: IO[str] | str | Path, *, as_markdown: bool = True, direction: Literal['LR', 'RL', 'TB', 'TD', 'BT'] = 'TD', title: str | bool | None = True, format: Literal['svg', 'pdf', 'png'] | None = None, mmdc_options: dict | None = None, add_root: bool = True, unique_nodes: bool = True, headers: Iterable[str] | None = None, node_mapper: Callable[[Node], str] | None = None, edge_mapper: Callable[[int, Node, int, Node], str] | None = None) Iterator[str]#

Serialize a Mermaid flowchart representation.

Optionally convert to a Graphviz display formats. See Graphs for details.

to_rdf_graph()#

Return an instance of rdflib.Graph.

See Graphs for details.

visit(callback: TraversalCallbackType, *, method=IterMethod.PRE_ORDER, memo=None) Any | None#

Call callback(node, memo) for all nodes.

See Node’s visit() method and Iteration Callbacks for details.

nutree.common module#

Functions and declarations used by the nutree.tree and nutree.node modules.

exception AmbiguousMatchError[source]#

Bases: TreeError

Thrown when a single-value lookup found multiple matches.

add_note()#

Exception.add_note(note) – add a note to the exception

args#
CONNECTORS = {'ascii11': (' ', '|', '`', '-'), 'ascii21': ('  ', '| ', '` ', '- '), 'ascii22': ('  ', '| ', '`-', '+-'), 'ascii32': ('   ', '|  ', '`- ', '+- '), 'ascii42': ('    ', ' |  ', ' `- ', ' +- '), 'ascii43': ('    ', '|   ', '`-- ', '+-- '), 'lines11': (' ', '│', '└', '├'), 'lines21': ('  ', '│ ', '└ ', '├ '), 'lines22': ('  ', '│ ', '└─', '├─'), 'lines32': ('   ', '│  ', '└─ ', '├─ '), 'lines32c': (' ', '│', '└─ ', '├─ ', '└┬ ', '├┬ '), 'lines42': ('    ', '   ', ' └─ ', ' ├─ '), 'lines43': ('    ', '│   ', '└── ', '├── '), 'lines43c': ('  ', '│ ', '└── ', '├── ', '└─┬ ', '├─┬ '), 'lines43r': ('    ', '   ', ' └──', ' ├──'), 'round11': (' ', '│', '╰', '├'), 'round21': ('  ', '│ ', '╰ ', '├ '), 'round22': ('  ', '│ ', '╰─', '├─'), 'round32': ('   ', '│  ', '╰─ ', '├─ '), 'round32c': (' ', '│', '╰─ ', '├─ ', '╰┬ ', '├┬ '), 'round42': ('    ', '   ', ' ╰─ ', ' ├─ '), 'round43': ('    ', '│   ', '╰── ', '├── '), 'round43c': ('  ', '│ ', '╰── ', '├── ', '╰─┬ ', '├─┬ '), 'round43r': ('    ', '   ', ' ╰──', ' ├──'), 'space1': (' ', ' ', ' ', ' '), 'space2': ('  ', '  ', '  ', '  '), 'space3': ('   ', '   ', '   ', '   '), 'space4': ('    ', ' |  ', '    ', '    ')}#

Node connector prefixes, for use with format(style=...) argument.

CalcIdCallbackType#

Type of Tree(..., calc_data_id)`

alias of Callable[[Tree, Any], Union[str, int]]

DataIdType#

Type of Node.data_id

alias of Union[str, int]

DeserializeMapperType#

Callback for tree.load()

alias of Callable[[Node, dict], Union[str, object]]

FILE_FORMAT_VERSION: str = '1.0'#

File format version used by tree.save() as meta.$format_version

class IterMethod(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)[source]#

Bases: Enum

Traversal order.

LEVEL_ORDER = 'level'#

Breadth-first (aka level-order)

LEVEL_ORDER_RTL = 'level_rtl'#

Breadth-first (aka level-order) right-to-left

POST_ORDER = 'post'#

Depth-first, post-order

PRE_ORDER = 'pre'#

Depth-first, pre-order

RANDOM_ORDER = 'random'#

Random order traversal

UNORDERED = 'unordered'#

Fastest traversal in unpredictable order. It may appear to be the order of node insertion, but do not rely on this!

ZIGZAG = 'zigzag'#

ZigZag order

ZIGZAG_RTL = 'zigzag_rtl'#

ZigZag order

classmethod __contains__(member)#

Return True if member is a member of this enum raises TypeError if member is not an enum member

note: in 3.12 TypeError will no longer be raised, and True will also be returned if member is the value of a member in this enum

classmethod __getitem__(name)#

Return the member matching name.

classmethod __iter__()#

Return members in definition order.

classmethod __len__()#

Return the number of members (no aliases)

exception IterationControl[source]#

Bases: Exception

Common base class for tree iteration controls.

add_note()#

Exception.add_note(note) – add a note to the exception

args#
KeyMapType#

Type of tree.save(..., key_map)

alias of Dict[str, str]

MapperCallbackType#

alias of Callable[[Node, dict], Union[None, Any]]

NodeFactoryType#

Type of Tree(..., factory)`

alias of Type[Node]

PYTHON_VERSION = '3.11.6'#

Currently used Python version as string

PredicateCallbackType#

alias of Callable[[Node], Union[None, bool, IterationControl]]

ROOT_ID: str = '__root__'#

Used as ID for the system root node

exception SelectBranch[source]#

Bases: IterationControl

Raised or returned by traversal callbacks to unconditionally accept all descendants.

add_note()#

Exception.add_note(note) – add a note to the exception

args#
SerializeMapperType#

Callback for tree.save()

alias of Callable[[Node, dict], Union[None, dict]]

exception SkipBranch(*, and_self=None)[source]#

Bases: IterationControl

Raised or returned by traversal callbacks to skip the current node’s descendants.

If and_self is true, some iterators will consider the node itself, but still skip the descendants. For example copy() and find_all(). If and_self is false, some iterators will consider the node’s children only.

add_note()#

Exception.add_note(note) – add a note to the exception

args#
exception StopTraversal(value=None)[source]#

Bases: IterationControl

Raised or returned by traversal callbacks to stop iteration.

Optionally, a return value may be passed. Note that if a callback returns False, this will be converted to an StopTraversal(None) exception.

add_note()#

Exception.add_note(note) – add a note to the exception

args#
exception TreeError[source]#

Bases: RuntimeError

Base class for all nutree errors.

add_note()#

Exception.add_note(note) – add a note to the exception

args#
exception UniqueConstraintError[source]#

Bases: TreeError

Thrown when trying to add the same node_id to the same parent

add_note()#

Exception.add_note(note) – add a note to the exception

args#
ValueMapType#

Type of tree.save(..., value_map)

alias of Dict[str, List[str]]

call_mapper(fn: MapperCallbackType | None, node: Node, data: dict) Any[source]#

Call the function and normalize result and exceptions.

Handles MapperCallbackType: Call fn(node, data) if defined and return the result. If fn is undefined or returns None, return data.

call_predicate(fn: Callable, node: Node) IterationControl | None | Any[source]#

Call the function and normalize result and exceptions.

Handles PredicateCallbackType: Call fn(node) and converts all raised IterationControl responses to a canonical result.

call_traversal_cb(fn: Callable, node: Node, memo: Any) False | None[source]#

Call the function and handle result and exceptions.

This method calls fn(node, memo) and converts all returned or raised IterationControl responses to a canonical result:

Handles TraversalCallbackType

  • Return False if the method returns SkipBranch or an instance of SkipBranch.

  • Raise StopTraversal(value) if the method returns False, StopTraversal, or an instance of StopTraversal.

  • If a form of StopIteration is returned, we treat as StopTraversal, but emit a warning.

  • Other return values are ignored and converted to None.

check_python_version(min_version: tuple[str]) bool[source]#

Check for deprecated Python version.

get_version() str[source]#
open_as_compressed_output_stream(path: str | Path, *, compression: bool | int = True, encoding: str = 'utf8') IO[str][source]#

Open a file for writing, ZIP-compressing if requested.

Example:

with open_as_compressed_stream("/path/to/foo.nutree") as fp:
    fp:
        print(line)
open_as_uncompressed_input_stream(path: str | Path, *, encoding: str = 'utf8', auto_uncompress: bool = True) IO[str][source]#

Open a file for reading, decompressing if necessary.

Decompression is done by checking for the magic header (independent of the file extension).

Example:

with open_as_uncompressed_stream("/path/to/foo.nutree") as fp:
    for line in fp:
        print(line)