AlgoTree package

Submodules

AlgoTree.dsl module

DSL (Domain Specific Language) for tree definition.

Supports multiple formats: - Visual tree format (with Unicode box characters) - Indent-based format (YAML-like) - S-expression format

class AlgoTree.dsl.TreeDSL[source]

Bases: object

Parser for various tree DSL formats.

static parse(text: str, format: str = 'auto') Node[source]

Parse tree from DSL text.

Parameters:
  • text – DSL text to parse.

  • format – Format to use (‘visual’, ‘indent’, ‘sexpr’, ‘auto’). ‘auto’ tries to detect format automatically.

Returns:

Root node of parsed tree.

AlgoTree.dsl.parse_tree(text: str, format: str = 'auto') Node[source]

Convenience function to parse tree from DSL text.

Parameters:
  • text – DSL text to parse.

  • format – Format to use (‘visual’, ‘indent’, ‘sexpr’, ‘auto’).

Returns:

Root node of parsed tree.

Examples

# Visual format tree = parse_tree(‘’’

company ├── engineering │ ├── frontend │ └── backend └── sales

‘’’)

# Indent format tree = parse_tree(‘’’

company
engineering

frontend backend

sales

‘’’)

# S-expression format tree = parse_tree(‘’’

(company
(engineering

(frontend) (backend))

(sales))

‘’’)

AlgoTree.exporters module

Tree export functionality for various formats.

This module provides exporters to convert trees to different representations including GraphViz, Mermaid, ASCII/Unicode trees, and more.

class AlgoTree.exporters.TreeExporter[source]

Bases: object

Base class for tree exporters.

static to_ascii(node: Node, style: str = 'ascii') str[source]

Export tree to ASCII/Unicode art.

Parameters:
  • node – Root node of tree

  • style – “ascii” for ASCII characters, “unicode” for box drawing

Returns:

String representation of tree

static to_dict(node: Node) Dict[str, Any][source]

Export tree to dictionary (already implemented in Node).

static to_graphviz(node: Node, name: str = 'Tree', node_attr: Callable[[Node], Dict[str, str]] | None = None, edge_attr: Callable[[Node, Node], Dict[str, str]] | None = None, graph_attr: Dict[str, str] | None = None) str[source]

Export tree to GraphViz DOT format.

Parameters:
  • node – Root node of tree

  • name – Graph name

  • node_attr – Function to generate node attributes

  • edge_attr – Function to generate edge attributes

  • graph_attr – Graph-level attributes

Returns:

DOT format string

Example

dot = TreeExporter.to_graphviz(tree,

node_attr=lambda n: {“label”: f”{n.name}n{n.payload.get(‘size’, ‘’)}”})

static to_html(node: Node, include_styles: bool = True, collapsible: bool = True) str[source]

Export tree to interactive HTML.

Parameters:
  • node – Root node of tree

  • include_styles – Include CSS styles

  • collapsible – Make tree nodes collapsible

Returns:

HTML string representation

static to_json(node: Node, indent: int = 2) str[source]

Export tree to JSON string.

static to_mermaid(node: Node, direction: str = 'TD', node_shape: str = 'round', node_text: Callable[[Node], str] | None = None) str[source]

Export tree to Mermaid diagram format.

Parameters:
  • node – Root node of tree

  • direction – Graph direction (TD, TB, BT, RL, LR)

  • node_shape – Shape style (“round”, “square”, “circle”, “rhombus”, “stadium”)

  • node_text – Function to generate node text

Returns:

Mermaid format string

Example

mermaid = TreeExporter.to_mermaid(tree,

node_text=lambda n: f”{n.name}<br/>{n.payload.get(‘type’, ‘’)}”)

static to_unicode(node: Node) str[source]

Export tree to Unicode box drawing (alias for to_ascii with unicode style).

static to_xml(node: Node, root_tag: str = 'tree', indent: int = 2) str[source]

Export tree to XML format.

Parameters:
  • node – Root node of tree

  • root_tag – Tag name for root element

  • indent – Number of spaces per level

Returns:

XML string representation

static to_yaml(node: Node, indent: int = 2) str[source]

Export tree to YAML-like indented format.

Parameters:
  • node – Root node of tree

  • indent – Number of spaces per level

Returns:

YAML-like string representation

AlgoTree.exporters.export_tree(node: Node, format: str, **kwargs) str[source]

Export tree to specified format.

Parameters:
  • node – Root node of tree

  • format – Export format (json, ascii, unicode, graphviz, mermaid, yaml, xml, html)

  • **kwargs – Format-specific options

Returns:

String representation in specified format

AlgoTree.exporters.save_tree(node: Node, filepath: str, format: str | None = None, **kwargs)[source]

Save tree to file in specified format.

Parameters:
  • node – Root node of tree

  • filepath – Output file path

  • format – Export format (auto-detected from extension if not specified)

  • **kwargs – Format-specific options

AlgoTree.flat_forest module

class AlgoTree.flat_forest.FlatForest(*args: Any, **kwargs: Any)[source]

Bases: dict

A forest class that is also a standard dictionary.

This class represents a forest using a flat dictionary structure where each node has a unique name (key) and an optional ‘parent’ name (keY) to reference its parent node.

Nodes without a ‘parent’ key (or with a ‘parent’ key set to None) are root nodes of trees in the forest. Nodes with a ‘parent’ key set to a valid name (key) in the forest are children of the node with that name. Each node represents a tree rooted at that node, which we expose through the FlatForestNode class, which provides a proxy interface to the structure centered around nodes abstracted away from the dictionary structure.

The FlatForest class is a dictionary whose data represents a forest-like structure. It provides methods to manipulate the forest structure and validation methods to check its integrity, but is essentially a view of the dict object passed into it (or created with it).

We provide a FlatForestNode class which provides an interface centered around nodes in a tree. See flat_forest_node.py for more details.

DETACHED_KEY = '__DETACHED__'

The name used to represent a detached node. This is a special name that is assumed to not exist in the tree. When a node is detached, its parent name is set to this value. Any descendants of the detached node are also detached.

PARENT_KEY = 'parent'

The key used to store the parent key of a node. Modify this if you want to use a different key to store the parent key.

add_child(name: str | None = None, *args, **kwargs) FlatForestNode[source]

Add a child node to the preferred root node.

Parameters:
  • name – The name of the child node.

  • payload – The payload of the child node.

Returns:

The child node.

as_tree(root_name='__ROOT__') FlatForestNode[source]

Retrieve the forest as a single tree rooted at a logical root node.

static check_valid(data) None[source]

Validate the forest structure to ensure the structural integrity of the trees.

This function performs the following validation checks:

  1. No cycles exist in any trees.

  2. All names (unique node identifiers) map to dictionary values.

  3. All nodes have a parent key that is either None or a valid key in the tree.

Parameters:

data – The forest data to validate.

Returns:

None

Raises:

ValueError – If the forest structure is invalid.

child_names(name: str) List[str][source]

Get the children names of a node with the given name.

Parameters:

name – The name of the node to get the children of.

Returns:

List of names of the children of the node.

property children

Get the children of the preferred root node.

Returns:

The children of the preferred root node.

contains(name: str) bool[source]

Check if the tree rooted at the preferred root node contains a node with the given name.

Parameters:

name – The name of the node.

Returns:

True if the node is in the forest, False otherwise.

detach(name: str) FlatForestNode[source]

Detach subtree rooted at node with the given name by setting its parent to FlatForest.DETACHED_KEY, which denotes a special name (key) that we assume doesn’t exist in the forest.

Parameters:

name – The name of the node to detach.

Returns:

The detached subtree rooted at the node.

Raises:

KeyError – If the node is not found in the tree.

property detached: FlatForestNode

Retrieve the detached tree. This is a special tree for which detached nodes are rooted at a logical node with the name FlatForest.DETACHED_KEY.

Returns:

The detached logical root node.

interior_node_names() List[str][source]

Get interior node names, i.e., nodes that are not root nodes or logical nodes.

Returns:

List of interior node names.

static is_valid(data) bool[source]

Check if the given data is a valid FlatForest.

Parameters:

data – The data to check.

Returns:

True if the data is a valid FlatForest, False otherwise.

logical_root_names() List[str][source]

Get the logical nodes of the forest. These occur when a node has a parent that is not in the forest. A special case for this is the DETACHED_KEY which is used for detached nodes, i.e., when we detach a node, we set its parent to DETACHED_KEY.

Returns:

List of logical root names.

property name: str

Get the name of the preferred root node. This is just self.preferred_root.

Returns:

The name of the preferred root node.

node(name: str) FlatForestNode[source]

Get an ancestor node with the given name under the preferred root node.

Parameters:

name – The name of the node.

Returns:

The node.

node_names() List[str][source]

Get the unique names in the tree, even if they are not nodes in the tree but only parent names without corresponding nodes.

Returns:

List of unique names in the tree.

nodes() List[FlatForestNode][source]

Get all the nodes in the forest.

Returns:

A list of all the nodes in the forest.

property parent: FlatForestNode | None

Get the parent of the preferred root node. This is always None since the preferred root node is a root node.

property payload: dict

Get the payload of the preferred root node.

Returns:

The payload of the preferred root node.

property preferred_root: str

Get the preferred root node of the forest.

Returns:

The preferred root node.

Raises:

KeyError – If the preferred root node is not found.

purge() None[source]

Purge detached nodes (tree rooted at FlatForest.DETACHED_KEY).

property root: FlatForestNode

Get the tree rooted at the preferred root node.

Returns:

The root node.

root_key(name: str) str[source]

Get the root key of the node with given name.

Parameters:

key – The key of the node.

Returns:

The key of the root node.

Raises:

KeyError – If the key is not found in the tree.

property root_names: List[str]

Retrieve the names of the root nodes. These are the nodes that have no parent.

Returns:

The names of the root nodes.

property roots: List[FlatForestNode]

Get the root nodes of the forest.

Returns:

The root nodes of the forest.

static spec() dict[source]

Get the JSON specification of the FlatForest data structure.

This method returns a dictionary representing the structure of the FlatForest. This is useful for documentation purposes and for understanding the structure of the data. The structure is as follows:

:return: A dict pattern representing the structure of the FlatForest.
subtree(name: str | None = None) FlatForestNode[source]

Get sub-tree rooted at the node with the name name with the current node also set to that node. If name is None, the preferred root node is used.

NOTE: This behavior is different from the expected behavior of a tree, since this is a forest. If the forest has multiple trees, this method will return any subtree under any root node. We could instead return the subtree under the preferred root node, which would then replicate the expected behavior and be consistent with the other node-centric methods like node and children. However, we choose to return the subtree under any root node to provide more flexibility. Once you return a subtree, the FlatForestNode class provides a node-centric interface to the tree structure consistent with the expected behavior.

Parameters:

name – The unique name of the node.

Returns:

FlatForestNode proxy representing the node.

to_dict() dict[source]

Convert the forest to a dictionary. Note this since this is already a dictionary, we just return a copy of the dictionary.

Returns:

A dictionary representation of the forest.

property trees: List[FlatForestNode]

Retrieve the trees in the forest.

Returns:

The trees in the forest.

AlgoTree.flat_forest_node module

class AlgoTree.flat_forest_node.FlatForestNode(name: str | None = None, parent: FlatForestNode | str | None = None, forest: FlatForest | None = None, payload: Dict | None = None, *args, **kwargs)[source]

Bases: MutableMapping

add_child(name: str | None = None, *args, **kwargs) FlatForestNode[source]

Add a child node. See __init__ for details on the arguments.

Returns:

The child node, from the perspective of the subtree that contains the parent.

property children: List[FlatForestNode]

Get the children of the node.

Returns:

List of child nodes.

clone(parent=None, clone_children=False) FlatForestNode[source]

Clone the node and, optionally, its children. If you want to clone the entire forest, see deepcopy. We allow the parent to be set to a new parent node to facilitate flexible cloning of nodes into new forest structures.

Returns:

A new node (or subtree rooted at the node if clone_children is True)

contains(name) bool[source]

Check if the the subtree rooted at the node contains any children with the given name

Parameters:

key – The key to check.

Returns:

True if the child node is present, False otherwise.

detach() FlatForestNode[source]

Detach the node.

Returns:

The detached node.

property forest: FlatForest

Get the underlying FlatForest object.

Returns:

The FlatForest object.

property name

Get the unique name of the node.

Returns:

The unique name of the node.

node(name: str) FlatForestNode[source]

Get an ancestor node with the given name.

Parameters:

name – The name of the node.

Returns:

The node.

property parent: FlatForestNode | None

Get the parent node of the node.

Returns:

The parent node.

property payload: Dict

Get the payload data of the node.

Returns:

Dictionary representing the data of the node.

static proxy(forest: FlatForest, node_key: str, root_key: str | None = None) FlatForestNode[source]

Create a proxy node for a node in a forest. The proxy node is a lightweight object that provides a node-centric abstraction of the tree that a node is a part of.

We do not check do any checks on the validity of the keys or the structure of the tree. This is a low-level method that should be used with caution. For instance, if node_key is not a descendent of root_key, the proxy node will not behave as expected.

Parameters:
  • forest – The forest in which the proxy node exists (or logically exists).

  • node_key – The key of the node.

  • root_key – The key of the (real or logical) root node.

property root: FlatForestNode

Get the root node of the subtree.

Returns:

The root node.

subtree(name: str | None = None) FlatForestNode[source]

Get a subtree rooted at the node with the name name. If name is None, the subtree is rooted at the current node.

Parameters:

name – The name of the root node.

Returns:

A subtree rooted at the given node.

to_dict()[source]

Convert the subtree rooted at node to a dictionary.

Returns:

A dictionary representation of the subtree.

AlgoTree.fluent module

Fluent API for tree construction and manipulation.

class AlgoTree.fluent.FluentNode(node: Node | List[Node])[source]

Bases: object

Wrapper for Node that provides fluent API for tree operations.

Example

result = (FluentNode(tree)

.filter(lambda n: n.level <= 2) .map(lambda n: n.payload.get(‘size’, 0)) .where(lambda n: n > 5) .to_list())

children() FluentNode[source]

Get all children of current nodes.

Returns:

New FluentNode with all children.

count() int[source]

Get count of current nodes.

Returns:

Number of nodes.

descendants() FluentNode[source]

Get all descendants of current nodes.

Returns:

New FluentNode with all descendants.

each(action: Callable[[Node], None]) FluentNode[source]

Execute an action on each node.

Parameters:

action – Function to execute on each node.

Returns:

Self for method chaining.

filter(predicate: Callable[[Node], bool]) FluentNode[source]

Filter nodes by predicate.

Parameters:

predicate – Function that returns True for nodes to keep.

Returns:

New FluentNode with filtered nodes.

first() Node | None[source]

Get first node or None.

Returns:

First node or None if empty.

leaves() FluentNode[source]

Get all leaf nodes from current nodes.

Returns:

New FluentNode with leaf nodes only.

map(transform: Callable[[Node], Any]) FluentNode[source]

Transform each node’s payload.

Parameters:

transform – Function to transform each node.

Returns:

Self for method chaining.

match(pattern: str | Pattern | dict) FluentNode[source]

Find nodes matching a pattern.

Parameters:

pattern – Pattern to match (string, Pattern object, or dict)

Returns:

New FluentNode with matching nodes.

prune(predicate: Callable[[Node], bool]) FluentNode[source]

Remove nodes matching predicate.

Parameters:

predicate – Function that returns True for nodes to remove.

Returns:

Self for method chaining.

replace_matches(pattern: str | Pattern | dict, replacement: Node | Callable[[Node], Node]) FluentNode[source]

Replace nodes matching a pattern.

Parameters:
  • pattern – Pattern to match

  • replacement – Node or function to generate replacement

Returns:

Self for method chaining.

sort(key: Callable[[Node], Any] | None = None, reverse: bool = False) FluentNode[source]

Sort children of each node.

Parameters:
  • key – Function to extract sort key from each node.

  • reverse – Whether to sort in reverse order.

Returns:

Self for method chaining.

to_dict() dict | List[dict][source]

Convert to dictionary representation.

Returns:

Dictionary or list of dictionaries.

to_list() List[Node][source]

Get list of current nodes.

Returns:

List of nodes.

where(predicate: Callable[[Node], bool]) FluentNode[source]

Filter current nodes by predicate (alias for filter on current set).

Parameters:

predicate – Function that returns True for nodes to keep.

Returns:

New FluentNode with filtered nodes.

class AlgoTree.fluent.TreeBuilder[source]

Bases: object

Fluent API for building trees with method chaining.

Example

tree = (TreeBuilder()

.root(“company”, type=”corporation”) .child(“engineering”, head=”Alice”)

.child(“frontend”, team_size=5) .sibling(“backend”, team_size=8) .up()

.sibling(“sales”, head=”Bob”) .build())

build() Node[source]

Build and return the tree.

Returns:

The root node of the constructed tree.

child(name: str, **payload) TreeBuilder[source]

Add a child to the current node and move to it.

Parameters:
  • name – Name for the child node.

  • **payload – Data for the child node.

Returns:

Self for method chaining.

root(name: str, **payload) TreeBuilder[source]

Create the root node.

Parameters:
  • name – Name for the root node.

  • **payload – Data for the root node.

Returns:

Self for method chaining.

sibling(name: str, **payload) TreeBuilder[source]

Add a sibling to the current node.

Parameters:
  • name – Name for the sibling node.

  • **payload – Data for the sibling node.

Returns:

Self for method chaining.

to_root() TreeBuilder[source]

Move to the root node.

Returns:

Self for method chaining.

up(levels: int = 1) TreeBuilder[source]

Move up in the tree by specified levels.

Parameters:

levels – Number of levels to move up. Default is 1.

Returns:

Self for method chaining.

AlgoTree.node module

Modern node implementation using proper classes instead of dict inheritance.

class AlgoTree.node.Node(name: str | None = None, parent: Node | None = None, **payload)[source]

Bases: object

A tree node with proper attributes instead of dict inheritance.

This class represents a single node in a tree structure with: - A unique name/identifier - Optional parent reference - List of children - Arbitrary payload data

add_child(name: str | None = None, **payload) Node[source]

Add a child node.

Parameters:
  • name – Name for the child node.

  • **payload – Data for the child node.

Returns:

The newly created child node.

clone() Node[source]

Create a deep copy of this node and its subtree.

find(predicate: Callable[[Node], bool]) Node | None[source]

Find first node matching predicate.

Parameters:

predicate – Function that returns True for matching nodes.

Returns:

First matching node or None.

find_all(predicate: Callable[[Node], bool]) List[Node][source]

Find all nodes matching predicate.

Parameters:

predicate – Function that returns True for matching nodes.

Returns:

List of matching nodes.

classmethod from_dict(data: Dict[str, Any], parent: Node | None = None) Node[source]

Create tree from nested dictionary representation.

Parameters:
  • data – Dictionary with node data and optional ‘children’ key.

  • parent – Parent node for the created tree.

Returns:

Root node of created tree.

get_path() List[Node][source]

Get path from root to this node.

property is_leaf: bool

Check if this is a leaf node.

property is_root: bool

Check if this is a root node.

property level: int

Get the level (depth) of this node in the tree.

property parent: Node | None

Get the parent node.

remove_child(child: Node) None[source]

Remove a child node.

property root: Node

Get the root node of the tree.

property siblings: List[Node]

Get list of sibling nodes.

to_dict() Dict[str, Any][source]

Convert tree to nested dictionary representation.

Returns:

Dictionary with node data and nested children.

traverse_levelorder() Iterator[Node][source]

Traverse tree in level order (breadth-first).

traverse_postorder() Iterator[Node][source]

Traverse tree in postorder (children before parent).

traverse_preorder() Iterator[Node][source]

Traverse tree in preorder (parent before children).

AlgoTree.node_hasher module

class AlgoTree.node_hasher.NodeHasher(hash_fn: Callable[[Any], int] | None = None)[source]

Bases: object

A class providing various hash functions for tree nodes.

static name(node: Any) int[source]

Compute a hash based on the name of the node.

Use Case: - Useful when you want to compare nodes solely based on their names, ignoring their position and other attributes.

Example: - Checking if two nodes represent the same entity based on their name.

Parameters:

node – The node for which to compute the hash.

Returns:

The hash value for the node’s name.

static node(node: Any) int[source]

Compute a hash based on the name and payload of the node.

Use Case: - This is the most common notion of node equality, focusing on the node’s intrinsic properties and ignoring its position in the tree. - Useful when you want to compare nodes by both name and payload, but not their position in the tree.

Example: - Checking if two nodes are equivalent in terms of both their name and the data they carry, without considering their location in the tree structure.

Parameters:

node – The node for which to compute the hash.

Returns:

The hash value for the node’s name and payload.

static path(node: Any) int[source]

Compute a hash based on the path of the node in the tree.

Use Case: - Useful when the position of the node in the tree is more important than its name or payload.

Example: - Determining if two nodes occupy the same position in the same or different trees.

Parameters:

node – The node for which to compute the hash.

Returns:

The hash value for the node’s path in the tree.

static payload(node: Any) int[source]

Compute a hash based on the payload of the node.

Use Case: - Useful when comparing nodes based on their payload, ignoring their name and position in the tree.

Example: - Identifying nodes that carry the same data.

Parameters:

node – The node for which to compute the hash.

Returns:

The hash value for the node’s payload.

AlgoTree.pattern_matcher module

Tree pattern matching functionality.

This module provides pattern matching capabilities for tree structures, allowing users to search for specific structural patterns within trees.

class AlgoTree.pattern_matcher.MatchType(*values)[source]

Bases: Enum

Types of pattern matching.

EXACT = 'exact'
PARTIAL = 'partial'
WILDCARD = 'wildcard'
class AlgoTree.pattern_matcher.Pattern(name: str | None = None, attributes: ~typing.Dict[str, ~typing.Any] = <factory>, children: ~typing.List[~AlgoTree.pattern_matcher.Pattern] = <factory>, is_wildcard: bool = False, is_deep_wildcard: bool = False, min_children: int | None = None, max_children: int | None = None, predicate: ~typing.Callable[[~AlgoTree.node.Node], bool] | None = None)[source]

Bases: object

Represents a tree pattern to match against.

Patterns can include: - Exact node matches - Wildcards (*) that match any single node - Deep wildcards (**) that match any subtree - Attribute constraints

attributes: Dict[str, Any]
children: List[Pattern]
static from_dict(pattern_dict: Dict[str, Any]) Pattern[source]

Create a pattern from dictionary representation.

Example

{

“name”: “parent”, “attributes”: {“type”: “container”}, “children”: [

{“name”: “child1”}, {“name”: “*”} # Wildcard child

]

}

static from_string(pattern_str: str) Pattern[source]

Parse a pattern from string notation.

Examples

“root” # Match node named ‘root’ “*” # Match any single node “**” # Match any subtree “node[type=foo]” # Match node with attribute “parent(child1, child2)” # Match parent with specific children “root.branch.leaf” # Dot notation path “app.*.test” # Dot notation with wildcard “root.**.target” # Dot notation with deep wildcard

is_deep_wildcard: bool = False
is_wildcard: bool = False
max_children: int | None = None
min_children: int | None = None
name: str | None = None
predicate: Callable[[Node], bool] | None = None
class AlgoTree.pattern_matcher.PatternMatcher(match_type: MatchType = MatchType.PARTIAL)[source]

Bases: object

Matches patterns against tree structures.

find_all(tree: Node, pattern: Pattern) List[Node][source]

Find all nodes in tree that match the pattern.

Parameters:
  • tree – Root node of tree to search

  • pattern – Pattern to match

Returns:

List of nodes that match the pattern

find_first(tree: Node, pattern: Pattern) Node | None[source]

Find first node that matches the pattern.

Parameters:
  • tree – Root node of tree to search

  • pattern – Pattern to match

Returns:

First matching node or None

match(node: Node, pattern: Pattern) bool[source]

Check if a node matches a pattern.

Parameters:
  • node – Node to match against

  • pattern – Pattern to match

Returns:

True if node matches pattern

replace(tree: Node, pattern: Pattern, replacement: Node | Callable[[Node], Node]) int[source]

Replace all nodes matching pattern with replacement.

Parameters:
  • tree – Root node of tree

  • pattern – Pattern to match

  • replacement – Node or function to generate replacement

Returns:

Number of replacements made

AlgoTree.pattern_matcher.dotcount(tree: Node, dot_path: str) int[source]

Count nodes matching a path pattern.

Parameters:
  • tree – Tree to count in

  • dot_path – Dot notation path pattern

Returns:

Number of matching nodes

AlgoTree.pattern_matcher.dotexists(tree: Node, dot_path: str) bool[source]

Check if a path exists in the tree.

Inspired by dotsuite’s dotexists from the Truth pillar.

Parameters:
  • tree – Tree to check

  • dot_path – Dot notation path

Returns:

True if path exists, False otherwise

AlgoTree.pattern_matcher.dotfilter(tree: Node, filter_expr: str) List[Node][source]

Filter tree nodes using advanced expressions.

Supports: - Comparison operators: >, <, >=, <=, ==, != - Logical operators: and, or, not - Path expressions: @.property - Functions: contains(), startswith(), endswith()

Parameters:
  • tree – Tree to filter

  • filter_expr – Filter expression

Returns:

List of matching nodes

Examples

# Size greater than 100 dotfilter(tree, “@.size > 100”)

# Name contains “test” and has children dotfilter(tree, “contains(@.name, ‘test’) and @.children.length > 0”)

AlgoTree.pattern_matcher.dotmatch(tree: Node, dot_path: str, return_paths: bool = False) List[Node] | List[str][source]

Match nodes using dot notation paths, inspired by dotsuite.

This provides a clean, intuitive way to navigate and match tree paths using dot notation similar to JSONPath or object property access.

Parameters:
  • tree – Tree to search

  • dot_path – Dot notation path (e.g., “root.branch.*.leaf”, “app.**.test”)

  • return_paths – If True, return dot paths to matches instead of nodes

Returns:

List of matching nodes or their dot paths

Examples

# Find all test files dotmatch(tree, “src.**.test_*”)

# Find specific path dotmatch(tree, “app.models.user”)

# Find with wildcards dotmatch(tree, “data.*.value”)

# Get paths instead of nodes paths = dotmatch(tree, “**.config”, return_paths=True) # Returns: [“app.config”, “modules.auth.config”, …]

AlgoTree.pattern_matcher.dotpluck(tree: Node, *dot_paths: str) List[Any][source]

Extract values from tree using dot notation paths.

Inspired by dotsuite’s dotpluck, this extracts payload values from nodes at specified paths.

Parameters:
  • tree – Tree to extract from

  • *dot_paths – Variable number of dot notation paths

Returns:

List of values (payload or None for missing paths)

Examples

# Extract multiple values values = dotpluck(tree, “user.name”, “user.age”, “user.email”)

# Extract with wildcards (returns all matching values) values = dotpluck(tree, “users.*.name”)

AlgoTree.pattern_matcher.pattern_match(tree: Node, pattern: str | Dict | Pattern, match_type: MatchType = MatchType.PARTIAL) List[Node][source]

Convenience function to find all matches of a pattern in a tree.

Parameters:
  • tree – Tree to search

  • pattern – Pattern as string, dict, or Pattern object

  • match_type – Type of matching

Returns:

List of matching nodes

AlgoTree.pretty_tree module

class AlgoTree.pretty_tree.PrettyTree(style: ~typing.Dict[str, str] | None = None, node_name: ~typing.Callable[[~typing.Any], str] = <function PrettyTree.<lambda>>, indent: int = 7, mark: ~typing.List[str] | None = None, node_details: ~typing.Callable[[~typing.Any], ~typing.Any] | None = None)[source]

Bases: object

A class to print a tree in a more readable way.

default_style = {'child_connector': '├', 'horizontal': '─', 'last_child_connector': '└', 'markers': ['🔵', '🔴', '🟢', '🟣', '🟠', '🟡', '🟤', '⚫', '⚪', '⭕', '🔘'], 'payload_connector': '◄', 'spacer': ' ', 'vertical': '│'}
static mark(name: str, markers: List[str]) str[source]

Get the marker for a node based on the hash of the node name.

Parameters:

name – The name of the node.

Returns:

The marker for the node.

AlgoTree.pretty_tree.pretty_tree(node, **kwargs) str[source]

Converts a tree to a pretty string representation.

Parameters:

kwargs – Key-word arguments. See PrettyTree for more details.

Returns:

A pretty string representation of the tree.

AlgoTree.serialization module

Serialization for AlgoTree - Modern Pythonic Approach.

Philosophy: - JSON is the native format (human-readable, universal) - Trees are naturally hierarchical, so nested JSON is default - Support other formats only when there’s a clear use case - Don’t reinvent the wheel - use standard libraries

The Node class already provides to_dict() and from_dict() methods that work seamlessly with Python’s json module. This module adds: - Convenience functions for file I/O - Optional compression - Streaming for large trees - Integration with modern data formats (Parquet for analytics)

AlgoTree.serialization.dumps(tree: Node, **json_kwargs) str[source]

Serialize tree to JSON string.

Parameters:
  • tree – Tree to serialize

  • **json_kwargs – Arguments passed to json.dumps (indent, sort_keys, etc.)

Returns:

JSON string

Example

json_str = dumps(tree, indent=2, sort_keys=True)

AlgoTree.serialization.from_parquet(path: str | Path) Node[source]

Load tree from Parquet format.

Reconstructs tree structure from tabular data.

Parameters:

path – Parquet file path

Returns:

Root node of tree

AlgoTree.serialization.from_pickle(data: bytes) Node[source]

Deserialize tree from pickle.

Parameters:

data – Pickled bytes

Returns:

Tree root node

AlgoTree.serialization.iter_nodes(path: str | Path) Iterator[Dict[str, Any]][source]

Iterate over nodes in a JSON file without loading entire tree.

Useful for processing large trees.

Parameters:

path – Path to JSON file

Yields:

Node dictionaries

Example

for node_data in iter_nodes(“large_tree.json”):
if node_data.get(“type”) == “file”:

process_file(node_data)

AlgoTree.serialization.load(path: str | Path) Node[source]

Load tree from JSON file.

Automatically handles gzip compression if file ends with .gz

Parameters:

path – File path

Returns:

Root node of loaded tree

Example

tree = load(“my_tree.json”) tree = load(“my_tree.json.gz”) # Auto-detects compression

AlgoTree.serialization.loads(json_str: str) Node[source]

Deserialize tree from JSON string.

Parameters:

json_str – JSON string

Returns:

Root node of tree

Example

tree = loads(‘{“name”: “root”, “children”: […]}’)

AlgoTree.serialization.register_json_encoder()[source]

Register Node class with json module for seamless serialization.

After calling this, you can use json.dumps directly on Node objects:

register_json_encoder() json.dumps(tree) # Works without calling to_dict()

AlgoTree.serialization.save(tree: Node, path: str | Path, compress: bool = False) None[source]

Save tree to JSON file.

This is the recommended way to persist trees. Uses the natural hierarchical JSON representation.

Parameters:
  • tree – Tree to save

  • path – File path (can be string or Path object)

  • compress – If True, use gzip compression

Example

save(tree, “my_tree.json”) save(tree, “my_tree.json.gz”, compress=True)

AlgoTree.serialization.stream_writer(path: str | Path, compress: bool = False)[source]

Context manager for streaming tree writes.

Useful for very large trees that shouldn’t be held entirely in memory.

Example

with stream_writer(“large_tree.json”) as writer:

writer.write_node(root) for subtree in generate_subtrees():

writer.write_subtree(subtree)

AlgoTree.serialization.to_parquet(tree: Node, path: str | Path, include_path: bool = True) None[source]

Export tree to Parquet format for analytics.

Parquet is columnar format excellent for analytical queries. Each node becomes a row with its attributes as columns.

Parameters:
  • tree – Tree to export

  • path – Output file path

  • include_path – Include full path from root as column

Example

to_parquet(tree, “tree_data.parquet”) # Then analyze with pandas, Spark, DuckDB, etc. df = pd.read_parquet(“tree_data.parquet”) df[df[‘type’] == ‘file’].groupby(‘extension’).size()

AlgoTree.serialization.to_pickle(tree: Node) bytes[source]

Serialize tree using pickle.

Faster than JSON but Python-only and not human-readable. Use only for temporary caching or when speed is critical.

Parameters:

tree – Tree to serialize

Returns:

Pickled bytes

AlgoTree.serialization.upgrade_legacy_format(legacy_data: Dict[str, Any]) Dict[str, Any][source]

Upgrade legacy TreeNode/FlatForest format to modern format.

Parameters:

legacy_data – Old format dictionary

Returns:

Modern format dictionary compatible with Node.from_dict()

AlgoTree.tree_converter module

class AlgoTree.tree_converter.TreeConverter[source]

Bases: object

Utility class for converting between tree representations.

static convert(source: ~typing.Any, target_type: ~typing.Type, node_name: ~typing.Callable[[~typing.Any], str] = <staticmethod(<function TreeConverter.default_node_name>)>, extract: ~typing.Callable[[~typing.Any], ~typing.Dict[str, ~typing.Any] | None] = <staticmethod(<function TreeConverter.default_extract>)>) Any[source]

Convert a tree rooted at node to a target tree type representation.

Parameters:
  • src_node – The root node of the tree to convert.

  • target_type – The target tree type to convert to.

  • node_name – The function to map nodes to unique keys.

  • extract – A callable to extract relevant data from a node.

Returns:

The converted tree.

static copy_under(node: ~typing.Any, under: ~typing.Any, node_name: ~typing.Callable[[~typing.Any], str] = <staticmethod(<function TreeConverter.default_node_name>)>, extract: ~typing.Callable[[~typing.Any], ~typing.Dict[str, ~typing.Any] | None] = <staticmethod(<function TreeConverter.default_extract>)>, max_tries: int = 1000) Any[source]

Copy the subtree rooted at node as a child of under, where the copy takes on the node type of under. It returns the subtree that contains under with current node at under.

Parameters:
  • node – The subtree rooted at node to copy.

  • under – The node to copy the subtree under.

  • node_name – The function to map nodes to names.

  • extract – A callable to extract relevant payload from a node.

  • max_tries – The maximum number of tries to generate a unique name if a name conflict occurs.

Returns:

A subtree extending under with the copied nodes.

static default_extract(node: Any) Dict[str, Any] | None[source]

Default extractor of relevant payload from a node.

Parameters:

node – The node to extract payload data from.

Returns:

The extracted data.

static default_node_name(node: Any) str[source]

Default function to map nodes to unique keys. If the node has a name attribute, then it is used as the unique key. Otherwise, a random UUID is generated.

Parameters:

node – The node to map to a unique key.

Returns:

The unique key for the node.

static to_dict(node, node_name: ~typing.Callable = <staticmethod(<function TreeConverter.default_node_name>)>, extract: ~typing.Callable = <staticmethod(<function TreeConverter.default_extract>)>, **kwargs) Dict[source]

Convert the subtree rooted at node to a dictionary.

Parameters:
  • node – The root node of the subtree to convert.

  • node_name – The function to map nodes to unique keys.

  • extract – A callable to extract relevant data from a node.

Returns:

A dictionary representation of the subtree.

AlgoTree.tree_hasher module

class AlgoTree.tree_hasher.TreeHasher(hash_fn: Callable[[Any], int] | None = None)[source]

Bases: object

A class that provides various hash functions for trees, with a default hashing strategy.

static isomorphic(tree: Any) int[source]

Hash based on tree structure only, ignoring node names and payloads.

static tree(tree: Any) int[source]

Hash based on the entire tree structure.

Parameters:

tree – The tree to hash.

Returns:

The hash value for the tree.

AlgoTree.tree_shaper module

Tree shaping functionality for open transformations.

This module provides reshaping capabilities for transforming trees into arbitrary structures (open transformations), inspired by dotsuite’s Shape pillar.

AlgoTree.tree_shaper.dotcollect(tree: Node, collector: Callable[[Node, Any], Any], initial: Any = None, dot_path: str = '**') Any[source]

Collect data from tree using a collector function.

Similar to reduce but more focused on building collections.

Parameters:
  • tree – Tree to collect from

  • collector – Function (node, accumulator) -> new_accumulator

  • initial – Initial accumulator value

  • dot_path – Pattern to match nodes

Returns:

Final collected value

Examples

# Collect into a dict by type by_type = dotcollect(tree,

lambda n, acc: {

**acc, n.payload.get(“type”, “unknown”):

acc.get(n.payload.get(“type”, “unknown”), []) + [n.name]

}, initial={})

# Collect statistics stats = dotcollect(tree,

lambda n, acc: {

“count”: acc[“count”] + 1, “total_size”: acc[“total_size”] + n.payload.get(“size”, 0), “max_depth”: max(acc[“max_depth”], n.level)

}, initial={“count”: 0, “total_size”: 0, “max_depth”: 0})

AlgoTree.tree_shaper.dotextract(tree: Node, extractor: Callable[[Node], Any], dot_path: str = '**', flatten: bool = True) List[Any] | Dict[str, Any][source]

Extract data from tree nodes using custom extractor.

Parameters:
  • tree – Tree to extract from

  • extractor – Function to extract data from each node

  • dot_path – Pattern to match nodes

  • flatten – If True, return flat list; if False, preserve structure

Returns:

Extracted data as list or structured dict

Examples

# Extract all sizes sizes = dotextract(tree, lambda n: n.payload.get(“size”))

# Extract node info for files files = dotextract(tree,

lambda n: {“name”: n.name, “size”: n.payload[“size”]}, dot_path=”**[type=file]”)

AlgoTree.tree_shaper.dotgroup(tree: Node, grouper: str | Callable[[Node], Any], dot_path: str = '**') Dict[Any, List[Node]][source]

Group tree nodes by a key.

Parameters:
  • tree – Tree to group nodes from

  • grouper – Either: - String: payload key to group by - Callable: function to compute group key

  • dot_path – Pattern to match nodes

Returns:

Dictionary mapping group keys to lists of nodes

Examples

# Group by type by_type = dotgroup(tree, “type”)

# Group by custom function by_level = dotgroup(tree, lambda n: n.level)

# Group files by extension by_ext = dotgroup(tree,

lambda n: n.name.split(“.”)[-1] if “.” in n.name else “no_ext”, dot_path=”**[type=file]”)

AlgoTree.tree_shaper.dotpartition(tree: Node, predicate: Callable[[Node], bool], dot_path: str = '**') Tuple[List[Node], List[Node]][source]

Partition nodes into two groups based on predicate.

Parameters:
  • tree – Tree to partition

  • predicate – Function returning True for first group, False for second

  • dot_path – Pattern to match nodes

Returns:

Tuple of (matching_nodes, non_matching_nodes)

Examples

# Partition by size large, small = dotpartition(tree, lambda n: n.payload.get(“size”, 0) > 1000)

# Partition enabled/disabled enabled, disabled = dotpartition(tree,

lambda n: n.payload.get(“enabled”, False), dot_path=”app.modules.*”)

AlgoTree.tree_shaper.dotpipe(tree: Node, *transformers: Callable | Tuple[str, Callable]) Any[source]

Pipe tree through a series of transformations.

This is the main function for open transformations - converting trees to any structure through a pipeline of operations.

Parameters:
  • tree – Tree to transform

  • *transformers – Variable number of transformers, each can be: - Callable: Applied to entire result of previous stage - Tuple[str, Callable]: (dot_path, transformer) applies to matched nodes

Returns:

Result of the final transformation (can be any type)

Examples

# Extract all names as a list names = dotpipe(tree,

lambda t: [n.name for n in t.traverse_preorder()])

# Pipeline of transformations result = dotpipe(tree,

(”**.config”, lambda n: n.payload), # Extract config payloads lambda configs: {c.get(“name”): c for c in configs}, # Dict by name lambda d: list(d.values())) # Back to list

# Convert tree to nested dict data = dotpipe(tree, to_dict)

# Extract and process specific data totals = dotpipe(tree,

(”**[type=file]”, lambda n: n.payload.get(“size”, 0)), sum) # Sum all sizes

AlgoTree.tree_shaper.dotproject(tree: Node, projection: List[str] | Dict[str, str], dot_path: str = '**') List[Dict[str, Any]][source]

Project specific fields from nodes (like SQL SELECT).

Parameters:
  • tree – Tree to project from

  • projection – Either: - List of field names to include - Dict mapping field names to aliases

  • dot_path – Pattern to match nodes

Returns:

List of projected dictionaries

Examples

# Select specific fields data = dotproject(tree, [“name”, “size”, “type”])

# With aliases data = dotproject(tree, {

“name”: “file_name”, “size”: “file_size”, “type”: “file_type”

})

AlgoTree.tree_shaper.to_adjacency_list(node: Node) Dict[str, List[str]][source]

Convert tree to adjacency list representation.

Parameters:

node – Root node

Returns:

Dictionary mapping node names to lists of child names

Examples

adj = to_adjacency_list(tree) # {“app”: [“config”, “database”], “config”: [], …}

AlgoTree.tree_shaper.to_dict(node: Node, include_children: bool = True, include_parent: bool = False, key_mapper: Callable[[str], str] | None = None) Dict[str, Any][source]

Convert tree node to dictionary representation.

Parameters:
  • node – Node to convert

  • include_children – If True, recursively include children

  • include_parent – If True, include parent reference (careful: can be circular)

  • key_mapper – Optional function to transform keys

Returns:

Dictionary representation of the node

Examples

# Simple conversion data = to_dict(tree)

# Without children (just this node) node_data = to_dict(tree, include_children=False)

# With key transformation data = to_dict(tree, key_mapper=str.upper)

AlgoTree.tree_shaper.to_edge_list(node: Node, include_root: bool = False) List[Tuple[str, str]][source]

Convert tree to edge list representation.

Parameters:
  • node – Root node

  • include_root – If True, include edge from None to root

Returns:

List of (parent, child) tuples

Examples

edges = to_edge_list(tree) # [(“app”, “config”), (“app”, “database”), …]

AlgoTree.tree_shaper.to_graphviz_data(tree: Node) Dict[str, Any][source]

Convert tree to GraphViz data structure.

Returns dict with ‘nodes’ and ‘edges’ suitable for visualization.

AlgoTree.tree_shaper.to_json_schema(tree: Node, type_key: str = 'type', required_key: str = 'required') Dict[str, Any][source]

Convert tree to JSON Schema-like structure.

Useful for representing configuration schemas or data models.

AlgoTree.tree_shaper.to_list(node: Node, traverse_order: str = 'preorder', include_data: bool = True) List[str | Dict[str, Any]][source]

Convert tree to flat list representation.

Parameters:
  • node – Root node

  • traverse_order – Traversal order (“preorder”, “postorder”, “levelorder”)

  • include_data – If True, include full node data; if False, just names

Returns:

List of nodes (as dicts or names)

Examples

# List of all node names names = to_list(tree, include_data=False)

# List of node data in level order nodes = to_list(tree, traverse_order=”levelorder”)

AlgoTree.tree_shaper.to_nested_lists(node: Node) List[source]

Convert tree to nested list structure (like S-expressions).

Parameters:

node – Root node

Returns:

Nested list representation

Examples

nested = to_nested_lists(tree) # [“app”, [“config”], [“database”], [“modules”, [“auth”], [“api”]]]

AlgoTree.tree_shaper.to_paths(node: Node, path_separator: str = '.', include_payload: bool = False) List[str] | Dict[str, Any][source]

Convert tree to path representations.

Parameters:
  • node – Root node

  • path_separator – Separator for path components

  • include_payload – If True, return dict mapping paths to payloads

Returns:

List of paths or dict mapping paths to payloads

Examples

# List of all paths paths = to_paths(tree) # [“app”, “app.config”, “app.database”, …]

# Paths with payloads path_data = to_paths(tree, include_payload=True) # {“app”: {…}, “app.config”: {…}, …}

# Using different separator paths = to_paths(tree, path_separator=”/”) # [“app”, “app/config”, “app/database”, …]

AlgoTree.tree_shaper.to_table(node: Node, columns: List[str] | None = None, include_path: bool = True) List[Dict[str, Any]][source]

Convert tree to tabular/relational format.

Parameters:
  • node – Root node

  • columns – Specific payload columns to include (None = all)

  • include_path – If True, include full path to node

Returns:

List of row dictionaries suitable for DataFrame or CSV

Examples

# Convert to table format rows = to_table(tree, columns=[“type”, “size”, “enabled”])

# Can be used with pandas: # df = pd.DataFrame(rows)

AlgoTree.tree_transformer module

Tree transformation functionality.

This module provides transformation capabilities for tree structures, allowing users to modify trees while preserving their structure (closed transformations). Inspired by dotsuite’s Shape pillar.

AlgoTree.tree_transformer.dotannotate(tree: Node, annotator: Callable[[Node], Dict[str, Any]] | Dict[str, Any], annotation_key: str = '_annotation', dot_path: str = '**', in_place: bool = False) Node[source]

Add annotations to nodes in the tree.

Parameters:
  • tree – Tree to annotate

  • annotator – Either: - Function that returns annotation dict for each node - Static annotation dict to add to all matching nodes

  • annotation_key – Key to store annotations under in payload

  • dot_path – Pattern for nodes to annotate

  • in_place – If True, modify tree in place

Returns:

Annotated tree

Examples

# Add computed annotations tree = dotannotate(tree,

lambda n: {

“depth”: n.level, “has_children”: len(n.children) > 0, “path”: “.”.join(p.name for p in n.get_path())

})

# Add static annotations to specific nodes tree = dotannotate(tree,

{“reviewed”: True, “version”: “1.0”}, dot_path=”**.critical_*”)

AlgoTree.tree_transformer.dotflatten(tree: Node, flatten_pattern: str = '**', max_depth: int | None = None) List[Node][source]

Flatten tree structure into a list of nodes.

Parameters:
  • tree – Tree to flatten

  • flatten_pattern – Pattern for nodes to include

  • max_depth – Maximum depth to flatten to

Returns:

List of nodes (flattened)

Examples

# Get all nodes as flat list all_nodes = dotflatten(tree)

# Get only file nodes files = dotflatten(tree, “**[type=file]”)

# Flatten only top 3 levels top_nodes = dotflatten(tree, max_depth=3)

AlgoTree.tree_transformer.dotgraft(tree: Node, graft_point: str, subtree: Node, position: int | str = 'append', in_place: bool = False) Node[source]

Graft a subtree onto a tree at specified point(s).

Parameters:
  • tree – Tree to graft onto

  • graft_point – Dot path to graft point(s)

  • subtree – Subtree to graft

  • position – Where to add the subtree: - “append”: Add at end of children (default) - “prepend”: Add at beginning of children - int: Insert at specific index - “replace”: Replace existing children

  • in_place – If True, modify tree in place

Returns:

Tree with grafted subtree

Examples

# Add new module to app new_module = Node(“auth”, type=”module”) tree = dotgraft(tree, “app.modules”, new_module)

# Insert at specific position tree = dotgraft(tree, “app.modules”, new_module, position=0)

# Replace children tree = dotgraft(tree, “app.old_modules”, new_modules, position=”replace”)

AlgoTree.tree_transformer.dotmap(tree: Node, mapper: Callable[[Node], Dict[str, Any]] | Dict[str, Callable], dot_path: str = '**', in_place: bool = False) Node[source]

Map a transformation function over nodes matching a pattern.

This is a convenience function that applies the same transformation to all nodes matching a pattern.

Parameters:
  • tree – Tree to transform

  • mapper – Either: - Function that takes a node and returns dict to update payload - Dict mapping payload keys to transformation functions

  • dot_path – Pattern to match nodes (default “**” for all nodes)

  • in_place – If True, modify tree in place

Returns:

Transformed tree

Examples

# Double all sizes tree = dotmap(tree, lambda n: {“size”: n.payload.get(“size”, 0) * 2})

# Transform specific fields tree = dotmap(tree, {

“size”: lambda v: v * 2, “name”: lambda v: v.upper(), “timestamp”: lambda v: str(v)

})

# Apply only to specific nodes tree = dotmap(tree,

lambda n: {“processed”: True}, dot_path=”app.data.**”)

AlgoTree.tree_transformer.dotmerge(tree1: Node, tree2: Node, merge_strategy: str = 'overlay', conflict_resolver: Callable[[Node, Node], Node] | None = None, in_place: bool = False) Node[source]

Merge two trees using various strategies.

Parameters:
  • tree1 – First tree (base)

  • tree2 – Second tree (overlay)

  • merge_strategy – Strategy for merging: - “overlay”: tree2 values override tree1 - “underlay”: tree1 values take precedence - “combine”: merge payloads, keeping both values - “custom”: use conflict_resolver function

  • conflict_resolver – Function to resolve conflicts (for custom strategy)

  • in_place – If True, modify tree1 in place

Returns:

Merged tree

Examples

# Simple overlay merge merged = dotmerge(tree1, tree2, “overlay”)

# Custom conflict resolution def resolver(node1, node2):

# Combine arrays, prefer tree2 for other values merged_payload = {} for key in set(node1.payload.keys()) | set(node2.payload.keys()):

if key in node1.payload and key in node2.payload:

val1, val2 = node1.payload[key], node2.payload[key] if isinstance(val1, list) and isinstance(val2, list):

merged_payload[key] = val1 + val2

else:

merged_payload[key] = val2

elif key in node1.payload:

merged_payload[key] = node1.payload[key]

else:

merged_payload[key] = node2.payload[key]

return Node(node2.name, **merged_payload)

merged = dotmerge(tree1, tree2, “custom”, conflict_resolver=resolver)

AlgoTree.tree_transformer.dotmod(tree: Node, transformations: Dict[str, Any] | List[Tuple[str, Any]], in_place: bool = False) Node[source]

Apply closed tree-to-tree transformations using dot notation paths.

This function modifies nodes in the tree based on dot notation paths, preserving the tree structure (closed transformation).

Parameters:
  • tree – Tree to transform

  • transformations – Either: - Dict mapping dot paths to transformations - List of (dot_path, transformation) tuples

  • in_place – If True, modify tree in place; otherwise create a copy

Returns:

Transformed tree (same object if in_place=True, copy otherwise)

Transformations can be:
  • Dict: Update node payload with dict contents

  • Callable: Apply function to node (receives node, returns dict to update payload)

  • String: Rename the node

  • None: Clear the payload (but keep the node)

Examples

# Update specific nodes tree = dotmod(tree, {

“app.config”: {“version”: “2.0”, “debug”: False}, “app.database”: {“host”: “localhost”, “port”: 5432}

})

# Rename nodes tree = dotmod(tree, {

“app.oldname”: “newname”

})

# Apply functions tree = dotmod(tree, {

“app.**.file”: lambda n: {“size”: n.payload.get(“size”, 0) * 2}

})

# Multiple transformations to same path pattern tree = dotmod(tree, [

(“app.**.test_*”, lambda n: {“tested”: True}), (“app.**.test_*”, lambda n: {“priority”: “high”})

])

AlgoTree.tree_transformer.dotnormalize(tree: Node, normalizer: Callable[[str], str] | None = None, normalize_payload: bool = False, in_place: bool = False) Node[source]

Normalize node names and optionally payload keys.

Parameters:
  • tree – Tree to normalize

  • normalizer – Function to normalize names (default: lowercase + underscore)

  • normalize_payload – If True, also normalize payload keys

  • in_place – If True, modify tree in place

Returns:

Normalized tree

Examples

# Default normalization (lowercase, spaces to underscores) tree = dotnormalize(tree)

# Custom normalization tree = dotnormalize(tree,

normalizer=lambda s: s.lower().replace(“-”, “_”))

# Also normalize payload keys tree = dotnormalize(tree, normalize_payload=True)

AlgoTree.tree_transformer.dotprune(tree: Node, condition: str | Callable[[Node], bool], keep_structure: bool = False, in_place: bool = False) Node[source]

Prune nodes from tree based on condition.

Parameters:
  • tree – Tree to prune

  • condition – Either: - Dot path pattern of nodes to remove - Predicate function (returns True for nodes to remove)

  • keep_structure – If True, replace pruned nodes with empty placeholders

  • in_place – If True, modify tree in place

Returns:

Pruned tree

Examples

# Remove all test files tree = dotprune(tree, “**.test_*”)

# Remove empty directories tree = dotprune(tree, lambda n: n.payload.get(“type”) == “dir” and len(n.children) == 0)

# Keep structure but clear nodes tree = dotprune(tree, “**.deprecated”, keep_structure=True)

AlgoTree.tree_transformer.dotreduce(tree: Node, reducer: Callable[[Any, Node], Any], initial: Any = None, traverse_pattern: str = '**') Any[source]

Reduce tree to a single value using a reducer function.

Parameters:
  • tree – Tree to reduce

  • reducer – Function (accumulator, node) -> new_accumulator

  • initial – Initial value for accumulator

  • traverse_pattern – Pattern for nodes to include in reduction

Returns:

Reduced value

Examples

# Sum all sizes total_size = dotreduce(tree,

lambda acc, n: acc + n.payload.get(“size”, 0), initial=0)

# Collect all names names = dotreduce(tree,

lambda acc, n: acc + [n.name], initial=[])

# Find maximum depth max_depth = dotreduce(tree,

lambda acc, n: max(acc, n.level), initial=0)

AlgoTree.tree_transformer.dotsplit(tree: Node, split_point: str, include_point: bool = True) Tuple[Node, List[Node]][source]

Split tree at specified point(s), extracting subtrees.

Parameters:
  • tree – Tree to split

  • split_point – Dot path to split point(s)

  • include_point – If True, include split point in extracted subtrees

Returns:

Tuple of (modified tree, list of extracted subtrees)

Examples

# Extract all test modules tree, test_modules = dotsplit(tree, “**.tests”)

# Extract but keep the container node tree, extracted = dotsplit(tree, “app.deprecated”, include_point=False)

AlgoTree.tree_transformer.dotvalidate(tree: Node, validator: Callable[[Node], bool] | Dict[str, Any], dot_path: str = '**', raise_on_invalid: bool = True) bool | List[Node][source]

Validate nodes in the tree against criteria.

Parameters:
  • tree – Tree to validate

  • validator – Either: - Predicate function returning True for valid nodes - Dict of required attributes and values

  • dot_path – Pattern for nodes to validate

  • raise_on_invalid – If True, raise exception on first invalid node

Returns:

List of invalid nodes (empty if all valid) If raise_on_invalid=True: True if all valid (raises otherwise)

Return type:

If raise_on_invalid=False

Examples

# Validate with function dotvalidate(tree,

lambda n: n.payload.get(“size”, 0) < 1000000, dot_path=”**[type=file]”)

# Validate required attributes dotvalidate(tree,

{“type”: “file”, “tested”: True}, dot_path=”app.src.**”)

# Get list of invalid nodes invalid = dotvalidate(tree,

lambda n: len(n.name) <= 255, raise_on_invalid=False)

AlgoTree.treenode module

class AlgoTree.treenode.TreeNode(parent: TreeNode | None = None, name: str | None = None, payload: Any | None = None, *args, **kwargs)[source]

Bases: dict

A tree node class. This class stores a nested representation of the tree. Each node is a TreeNode object, and if a node is a child of another node, it is stored in the parent node’s children attribute.

add_child(name: str | None = None, payload: Any | None = None, *args, **kwargs) TreeNode[source]

Add a child node to the tree. Just invokes __init__. See __init__ for details.

clone() TreeNode[source]

Clone the tree node (sub-tree) rooted at the current node.

Returns:

A new TreeNode object with the same data as the current node.

static from_dict(data: Dict) TreeNode[source]

Create a TreeNode from a dictionary.

Parameters:

data – The dictionary to convert to a TreeNode.

Returns:

A TreeNode object.

static is_valid(data) bool[source]

Check if the given data is a valid TreeNode data.

Parameters:

data – The data to check.

Returns:

True if the data is a valid TreeNode, False otherwise.

node(name: str) TreeNode[source]

Get the node with the given name in the current sub-tree. The sub-tree remains the same, we just change the current node position. If the name is not found, raise a KeyError.

Parameters:

name – The name of the node.

Returns:

The node with the given name.

nodes() List[TreeNode][source]

Get all the nodes in the current sub-tree.

Returns:

A list of all the nodes in the current sub-tree.

property parent: TreeNode | None

Get the parent of the node.

Returns:

The parent of the node.

property root: TreeNode

Get the root of the tree.

Returns:

The root node of the tree.

subtree(name: str) TreeNode[source]

Get the subtree rooted at the node with the given name. This is not a view, but a new tree rooted at the node with the given name. This is different from the node method, which just changes the current node position. It’s also different from the subtree method in the FlatForestNode class, which returns a view of the tree.

Parameters:

name – The name of the node.

Returns:

The subtree rooted at the node with the given name.

to_dict()[source]

Convert the subtree rooted at node to a dictionary.

Returns:

A dictionary representation of the subtree.

AlgoTree.treenode_api module

class AlgoTree.treenode_api.TreeNodeApi[source]

Bases: object

A class to check if a tree object models the concept of a tree node. The tree node concept is defined as follows:

  • children property

    Represents a list of child nodes for the current node that can be accessed and modified.

  • parent property

    Represents the parent node of the current node that can be accessed and modified.

    Suppose we have the subtree G at node G:

    B (root)
    ├── D
    └── E (parent)
        └── G (current node)
    

    Then, G.parent should refer node E. G.root.parent should be None since root is the root node of subtree G and the root node has no parent. This is true even if subtree G is a subtree view of a larger tree.

    If we set G.parent = D, then the tree structure changes to:

    B (root)
    ├── D
    │   └── G (current node)
    └── E
    

    This also changes the view of the sub-tree, since we changed the underlying tree structure. However, the same nodes are still accessible from the sub-tree.

    If we had set G.parent = X where X is not in the subtree G, then we would have an invalid subtree view even if is is a well-defined operation on the underlying tree structure. It is undefined behavior to set a parent that is not in the subtree, but leave it up to each implementation to decide how to handle such cases.

  • node(name: str) -> NodeType method.

    Returns a node in the current subtree that the current node belongs to. The returned node should be the node with the given name, if it exists. If the node does not exist, it should raise a KeyError.

    The node-centric view of the returned node should be consistent with the view of the current node, i.e., if the current node belongs to a specific sub-tree rooted at some other node, the returned node should also belong to the same sub-tree (i.e., with the same root), just pointing to the new node, but it should be possible to use parent and children to go up and down the sub-tree to reach the same nodes. Any node that is an ancestor of the root of the sub-tree remains inaccessible.

    Example: Suppose we have the sub-tree t rooted at A and the current node is B:

    A (root)
    ├── B (current node)
    │   ├── D
    │   └── E
    |       └── G
    └── C
        └── F
    

    If we get node F, t.node(F), then the sub-tree t remains the same, but the current node is now F:

    A (root)
    ├── B
    │   ├── D
    │   └── E
    |       └── G
    └── C
        └── F (current node)
    
  • subtree(node: Optional[NodeType] = None) -> NodeType method.

    Returns a view of another sub-tree rooted at node where node is contained in the original sub-tree view. If node is None, the method will return the sub-tree rooted at the current node.

    subtree is a partial function over the the nodes in the sub-tree, which means it is only well-defined when node is a descendant of the root of the sub-tree. We do not specify how to deal with the case when this condition is not met, but one approach would be to raise an exception.

    Example: Suppose we have the sub-tree t rooted at A and the current node is C:

    A (root)
    ├── B
    │   ├── D
    │   └── E
    |       └── G
    └── C (current node)
        └── F
    

    The subtree t.subtree(B) returns a new subtree:

    B (root, current node)
    ├── D
    └── E
        └── G
    
  • root property

    An immutable property that represents the root node of the sub-tree.

    Suppose we have the subtree G at node G:

    B (root)
    ├── D
    └── E
        └── G (current node)
    

    Then, G.root should refer node B.

  • payload property

    Returns the payload of the current node. The payload is the data associated with the node but not with the structure of the tree, e.g., it does not include the parent or children of the node.

  • name property

    Returns the name of the current node. The name is an identifier for the node within the tree. It is not necessarily unique, and nor is it necessarily even a meaningful identifier, e.g., a random UUID.

  • contains(name) -> bool method.

    Returns True if the sub-tree contains a node with the given name, otherwise False.

static check(node: Any, require_props: List[str] = ['name', 'root', 'children', 'parent', 'node', 'subtree', 'payload', 'contains']) Any[source]
static is_valid(value: Any, require_props: List[str] = ['name', 'root', 'children', 'parent', 'node', 'subtree', 'payload', 'contains']) bool[source]
static missing(node: Any, require_props: List[str] = ['name', 'root', 'children', 'parent', 'node', 'subtree', 'payload', 'contains']) List[str][source]
properties: List[str] = ['name', 'root', 'children', 'parent', 'node', 'subtree', 'payload', 'contains']

AlgoTree.utils module

AlgoTree.utils.ancestors(node: Any) List[Any][source]

Get the ancestors of a node.

We could have used the path function, but we want to show potentially more efficient use of the parent property. As a tree, each node has at most one parent, so we can traverse the tree by following the parent relationship without having to search for the path from the root to the node. If parent pointers are not available but children pointers are, we can use the path function. In our implementations of trees, we implement both parent and children pointers.

Parameters:

node – The root node.

Returns:

List of ancestor nodes.

AlgoTree.utils.average_distance(node: Any) float[source]

Compute the average distance between all pairs of nodes in the subtree rooted at the current node.

Parameters:

node – The node.

Returns:

The average distance between all pairs of nodes.

AlgoTree.utils.breadth_first(node: Any, func: Callable[[Any], bool], max_lvl: int | None = None, **kwargs) bool[source]

Traverse the tree in breadth-first order. The function func is called on each node and level. The function should have a side-effect you want to achieve, and if it returns True, the traversal will stop. The keyword arguments are passed to func.

If func returns True, the traversal will stop and the traversal will return True immediately. Otherwise, it will return False after traversing all nodes. This is useful if you want to find a node that satisfies a condition, and you want to stop the traversal as soon as you find it.

Requirement:

  • This function requires that the node has a children property that is iterable.

  • The function func should have the signature:

    func(node: Any, **kwargs) -> bool
    
Parameters:
  • node – The root node.

  • func – The function to call on each node and any additional keyword arguments. We augment kwargs with a level key, too, which specifies the level of the node in the tree.

  • max_lvl – The maximum number of levels to descend. If None, the traversal will continue until all nodes are visited or until func returns True.

  • kwargs – Additional keyword arguments to pass to func.

Raises:
  • TypeError – If func is not callable.

  • AttributeError – If the node does not have a ‘children’.

Returns:

None

AlgoTree.utils.breadth_first_undirected(node: Any, max_hops: int | float = inf) List[Any][source]

Traverse the tree in breadth-first order. It treats the tree as an undirected graph, where each node is connected to its parent and children.

AlgoTree.utils.depth(node) int[source]

Get the depth of a node in its subtree view.

Parameters:

node – The node.

Returns:

The depth of the node.

AlgoTree.utils.descendants(node: Any) List[Any][source]

Get the descendants of a node.

Parameters:

node – The root node.

Returns:

List of descendant nodes.

AlgoTree.utils.distance(node1: Any, node2: Any) int[source]

Find the distance between two nodes.

Parameters:
  • node1 – The first node.

  • node2 – The second node.

Returns:

The distance between the two nodes.

AlgoTree.utils.find_node(node: Any, pred: Callable[[Any], bool], **kwargs) Any[source]

Find closest descendent node of node that satisfies a predicate (where distance is defined with respect to path length). Technically, an order defined by path length is a partial order, sine many desendents that satisfy the condition may be at the same distance from node. We leave it up to each implementation to define which among these nodes to return. Use find_nodes if you want to return all nodes that satisfy the condition (return all the nodes in the partial order).

The predicate function pred should return True if the node satisfies the condition. It can also accept any additional keyword arguments, which are passed to the predicate. Note that we also augment the keyword arguments with a level key, which specifies the level of the node in the tree, so you can use this information in your predicate function.

Parameters:
  • pred – The predicate function which returns True if the node satisfies the condition.

  • kwargs – Additional keyword arguments to pass to pred.

Returns:

The node that satisfies the predicate.

AlgoTree.utils.find_nodes(node: Any, pred: Callable[[Any], bool], **kwargs) List[Any][source]

Find nodes that satisfy a predicate.

Parameters:
  • pred – The predicate function.

  • kwargs – Additional keyword arguments to pass to pred.

Returns:

List of nodes that satisfy the predicate.

AlgoTree.utils.find_path(source: Any, dest: Any, bidirectional: bool = False) List[Any][source]

Find the path from a source node to a destination node.

Parameters:
  • source – The source node.

  • dest – The destination node.

Returns:

The path from the source node to the destination node.

AlgoTree.utils.height(node) int[source]

Get the height of a subtree (containing the node node, but any other node in the subtree would return the same height)

Parameters:

node – The subtree containing node.

Returns:

The height of the subtree.

AlgoTree.utils.is_internal(node) bool[source]

Check if a node is an internal node.

Parameters:

node – The node to check.

Returns:

True if the node is an internal node, False otherwise.

AlgoTree.utils.is_isomorphic(node1: Any, node2: Any) bool[source]

Check if two (sub)trees are isomorphic. To check if two trees are isomorphic, just pass in the root nodes of the trees.

This is another kind of equivalence: two nodes are equivalent if they have the same substructure (extensic relations), but the names and payloads of the nodes (intrinsic relations) can be different.

We ignore the parents of the nodes in this comparison. If we also included the parents, this would be the same as calling is_isomorphic on the root nodes of the trees.

Parameters:
  • node1 – The root node of the first tree.

  • node2 – The root node of the second tree.

Returns:

True if the trees are isomorphic, False otherwise.

AlgoTree.utils.is_leaf(node) bool[source]

Check if a node is a leaf node.

Parameters:

node – The node to check.

Returns:

True if the node is a leaf node, False otherwise.

AlgoTree.utils.is_root(node) bool[source]

Check if a node is a root node.

Parameters:

node – The node to check.

Returns:

True if the node is a root node, False otherwise.

AlgoTree.utils.lca(node1: Any, node2: Any, hash_fn: Callable[[Any], int] | None = None) Any[source]

Find the lowest common ancestor of two nodes.

Parameters:
  • node1 – The first node.

  • node2 – The second node.

Returns:

The lowest common ancestor of the two nodes.

AlgoTree.utils.leaves(node: Any) List[Any][source]

Get the leaves of a node.

Parameters:

node – The root node.

Returns:

List of leaf nodes.

AlgoTree.utils.map(node: Any, func: Callable[[Any], Any], order: str = 'post', **kwargs) Any[source]

Map a function over the nodes in the tree rooted at node. It is a map operation over trees. In particular, the function func, of type:

func : Node -> Node,

is called on each node in pre or post order traversal. The function should return a new node. The tree rooted at node will be replaced (in-place) with the tree rooted at the new node. The order of traversal can be specified as ‘pre’ or ‘post’.

Requirement:

  • This function requires that the node has a children property that is iterable and assignable, e.g., node.children = [child1, child2, …].

Parameters:
  • node – The root node to start the traversal.

  • func – The function to call on each node. The function should take a single argument, the node, and return a new node (or have some other side effect you want to achieve).

  • order – The order of traversal (pre or post).

  • kwargs – Additional keyword arguments to pass to func.

Raises:
  • ValueError – If the order is not ‘pre’ or ‘post’.

  • TypeError – If func is not callable.

Returns:

The modified node. If func returns a new node, the tree rooted at node will be replaced with the tree rooted at the new node.

AlgoTree.utils.node_stats(node: ~typing.Any, node_name: ~typing.Callable[[~typing.Any], str] = <function <lambda>>, payload: ~typing.Callable[[~typing.Any], ~typing.Any] = <function <lambda>>) Dict[str, Any][source]

Gather statistics about the current node and its subtree.

Parameters:
  • node – The current node in the subtree.

  • node_name – A function that returns the name of a node. Defaults to returning the node’s name property.

  • payload – A function that returns the payload of a node. Defaults to returning the node’s payload property.

Returns:

A dictionary containing the statistics.

AlgoTree.utils.node_to_leaf_paths(node: Any) List[List[Any]][source]

List all node-to-leaf paths in a tree. Each path is a list of nodes from the current node node to a leaf node.

Example: Suppose we have the following sub-tree structure for node A:

A
├── B
│   ├── D
│   └── E
└── C
    └── F

Invoking node_to_leaf_paths(A) enumerates the following list of paths:

[[A, B, D], [A, B, E], [A, C, F]]
Parameters:

node – The current node.

Returns:

List of paths in the tree under the current node.

AlgoTree.utils.path(node: Any) List[Any][source]

Get the path from the root node to the given node.

Parameters:

node – The node.

Returns:

The path from the root node to the given node.

AlgoTree.utils.paths_to_tree(paths: List[List[str]], type: Type, max_tries: int = 1000) Any[source]

Convert a list of paths to a tree structure. Each path is a list of nodes from the root to a leaf node. (A tree can be uniquely identified by this list of paths.)

Example: Suppose we have the following list of paths:

paths = [ ["A", "B", "D"], ["A", "B", "E"], ["A", "C", "F"] ]

We can convert this list of paths to a tree structure using the following code:

tree = paths_to_tree(paths, TreeNode)

This will create the following tree structure:

A
├── B
│   ├── D
│   └── E
└── C
    └── F

For some tree-like data structures, it may be the case that the names of nodes must be unique. We can use the max_tries parameter to try to create a node with a unique name like the one provided by suffixing the name with an integer.

Parameters:
  • paths – The list of paths.

  • type – The type of the tree node.

  • max_tries – The maximum number of tries to create a node with a unique name.

AlgoTree.utils.prune(node: Any, pred: Callable[[Any], bool], **kwargs) Any[source]

Prune the tree rooted at node by removing nodes that satisfy a predicate. The predicate function should return True if the node should be pruned. It can also accept any additional keyword arguments, which are passed to the predicate.

Parameters:
  • node – The root node.

  • pred – The predicate function which returns True if the node should be pruned.

  • kwargs – Additional keyword arguments to pass to pred.

Returns:

The pruned tree.

AlgoTree.utils.siblings(node: Any) List[Any][source]

Get the siblings of a node.

Parameters:

node – The node.

Returns:

List of sibling nodes.

AlgoTree.utils.size(node: Any) int[source]

Get the size of the subtree under the current node.

Parameters:

node – The node.

Returns:

The number of descendents of the node.

AlgoTree.utils.subtree_centered_at(node: Any, max_hops: int) Any[source]

Get the subtree centered at a node within a certain number of hops from the node. We return a subtree rooted at some ancestor of the node, or the node itself, that contains all nodes within max_hops hops from the node.

Parameters:

node – The node.

Returns:

The subtree centered at the node.

AlgoTree.utils.subtree_rooted_at(node: Any, max_lvl: int) Any[source]

Get the subtree rooted at a node whose descendents are within a certain number of hops it. We return a subtree rooted the node itself, that contains all nodes within max_hops hops from the node.

Parameters:

node – The node.

Returns:

The subtree centered at the node.

AlgoTree.utils.visit(node: Any, func: Callable[[Any], bool], order: str = 'post', max_hops: int = inf, **kwargs) bool[source]

Visit the nodes in the tree rooted at node in a pre-order or post-order traversal. The procedure proc should have a side-effect you want to achieve, such as printing the node or mutating the node in some way.

If func returns True, the traversal will stop and the traversal will return True immediately. Otherwise, it will return False after traversing all nodes.

Requirement:

  • This function requires that the node has a children property that is iterable.

Parameters:
  • node – The root node to start the traversal.

  • func – The function to call on each node. The function should take a single argument, the node. It should have some side-effect you want to achieve. See map if you want to return a new node to rewrite the sub-tree rooted at node.

  • max_hops – The maximum number of hops to traverse.

  • order – The order of traversal (pre, post, or level).

  • kwargs – Additional keyword arguments to pass to func.

Raises:
  • ValueError – If the order is not valid.

  • TypeError – If func is not callable.

  • AttributeError – If the node does not have a ‘children’.

Module contents

class AlgoTree.FluentNode(node: Node | List[Node])[source]

Bases: object

Wrapper for Node that provides fluent API for tree operations.

Example

result = (FluentNode(tree)

.filter(lambda n: n.level <= 2) .map(lambda n: n.payload.get(‘size’, 0)) .where(lambda n: n > 5) .to_list())

children() FluentNode[source]

Get all children of current nodes.

Returns:

New FluentNode with all children.

count() int[source]

Get count of current nodes.

Returns:

Number of nodes.

descendants() FluentNode[source]

Get all descendants of current nodes.

Returns:

New FluentNode with all descendants.

each(action: Callable[[Node], None]) FluentNode[source]

Execute an action on each node.

Parameters:

action – Function to execute on each node.

Returns:

Self for method chaining.

filter(predicate: Callable[[Node], bool]) FluentNode[source]

Filter nodes by predicate.

Parameters:

predicate – Function that returns True for nodes to keep.

Returns:

New FluentNode with filtered nodes.

first() Node | None[source]

Get first node or None.

Returns:

First node or None if empty.

leaves() FluentNode[source]

Get all leaf nodes from current nodes.

Returns:

New FluentNode with leaf nodes only.

map(transform: Callable[[Node], Any]) FluentNode[source]

Transform each node’s payload.

Parameters:

transform – Function to transform each node.

Returns:

Self for method chaining.

match(pattern: str | Pattern | dict) FluentNode[source]

Find nodes matching a pattern.

Parameters:

pattern – Pattern to match (string, Pattern object, or dict)

Returns:

New FluentNode with matching nodes.

prune(predicate: Callable[[Node], bool]) FluentNode[source]

Remove nodes matching predicate.

Parameters:

predicate – Function that returns True for nodes to remove.

Returns:

Self for method chaining.

replace_matches(pattern: str | Pattern | dict, replacement: Node | Callable[[Node], Node]) FluentNode[source]

Replace nodes matching a pattern.

Parameters:
  • pattern – Pattern to match

  • replacement – Node or function to generate replacement

Returns:

Self for method chaining.

sort(key: Callable[[Node], Any] | None = None, reverse: bool = False) FluentNode[source]

Sort children of each node.

Parameters:
  • key – Function to extract sort key from each node.

  • reverse – Whether to sort in reverse order.

Returns:

Self for method chaining.

to_dict() dict | List[dict][source]

Convert to dictionary representation.

Returns:

Dictionary or list of dictionaries.

to_list() List[Node][source]

Get list of current nodes.

Returns:

List of nodes.

where(predicate: Callable[[Node], bool]) FluentNode[source]

Filter current nodes by predicate (alias for filter on current set).

Parameters:

predicate – Function that returns True for nodes to keep.

Returns:

New FluentNode with filtered nodes.

class AlgoTree.MatchType(*values)[source]

Bases: Enum

Types of pattern matching.

EXACT = 'exact'
PARTIAL = 'partial'
WILDCARD = 'wildcard'
class AlgoTree.Node(name: str | None = None, parent: Node | None = None, **payload)[source]

Bases: object

A tree node with proper attributes instead of dict inheritance.

This class represents a single node in a tree structure with: - A unique name/identifier - Optional parent reference - List of children - Arbitrary payload data

add_child(name: str | None = None, **payload) Node[source]

Add a child node.

Parameters:
  • name – Name for the child node.

  • **payload – Data for the child node.

Returns:

The newly created child node.

clone() Node[source]

Create a deep copy of this node and its subtree.

find(predicate: Callable[[Node], bool]) Node | None[source]

Find first node matching predicate.

Parameters:

predicate – Function that returns True for matching nodes.

Returns:

First matching node or None.

find_all(predicate: Callable[[Node], bool]) List[Node][source]

Find all nodes matching predicate.

Parameters:

predicate – Function that returns True for matching nodes.

Returns:

List of matching nodes.

classmethod from_dict(data: Dict[str, Any], parent: Node | None = None) Node[source]

Create tree from nested dictionary representation.

Parameters:
  • data – Dictionary with node data and optional ‘children’ key.

  • parent – Parent node for the created tree.

Returns:

Root node of created tree.

get_path() List[Node][source]

Get path from root to this node.

property is_leaf: bool

Check if this is a leaf node.

property is_root: bool

Check if this is a root node.

property level: int

Get the level (depth) of this node in the tree.

property parent: Node | None

Get the parent node.

remove_child(child: Node) None[source]

Remove a child node.

property root: Node

Get the root node of the tree.

property siblings: List[Node]

Get list of sibling nodes.

to_dict() Dict[str, Any][source]

Convert tree to nested dictionary representation.

Returns:

Dictionary with node data and nested children.

traverse_levelorder() Iterator[Node][source]

Traverse tree in level order (breadth-first).

traverse_postorder() Iterator[Node][source]

Traverse tree in postorder (children before parent).

traverse_preorder() Iterator[Node][source]

Traverse tree in preorder (parent before children).

class AlgoTree.Pattern(name: str | None = None, attributes: ~typing.Dict[str, ~typing.Any] = <factory>, children: ~typing.List[~AlgoTree.pattern_matcher.Pattern] = <factory>, is_wildcard: bool = False, is_deep_wildcard: bool = False, min_children: int | None = None, max_children: int | None = None, predicate: ~typing.Callable[[~AlgoTree.node.Node], bool] | None = None)[source]

Bases: object

Represents a tree pattern to match against.

Patterns can include: - Exact node matches - Wildcards (*) that match any single node - Deep wildcards (**) that match any subtree - Attribute constraints

attributes: Dict[str, Any]
children: List[Pattern]
static from_dict(pattern_dict: Dict[str, Any]) Pattern[source]

Create a pattern from dictionary representation.

Example

{

“name”: “parent”, “attributes”: {“type”: “container”}, “children”: [

{“name”: “child1”}, {“name”: “*”} # Wildcard child

]

}

static from_string(pattern_str: str) Pattern[source]

Parse a pattern from string notation.

Examples

“root” # Match node named ‘root’ “*” # Match any single node “**” # Match any subtree “node[type=foo]” # Match node with attribute “parent(child1, child2)” # Match parent with specific children “root.branch.leaf” # Dot notation path “app.*.test” # Dot notation with wildcard “root.**.target” # Dot notation with deep wildcard

is_deep_wildcard: bool = False
is_wildcard: bool = False
max_children: int | None = None
min_children: int | None = None
name: str | None = None
predicate: Callable[[Node], bool] | None = None
class AlgoTree.PatternMatcher(match_type: MatchType = MatchType.PARTIAL)[source]

Bases: object

Matches patterns against tree structures.

find_all(tree: Node, pattern: Pattern) List[Node][source]

Find all nodes in tree that match the pattern.

Parameters:
  • tree – Root node of tree to search

  • pattern – Pattern to match

Returns:

List of nodes that match the pattern

find_first(tree: Node, pattern: Pattern) Node | None[source]

Find first node that matches the pattern.

Parameters:
  • tree – Root node of tree to search

  • pattern – Pattern to match

Returns:

First matching node or None

match(node: Node, pattern: Pattern) bool[source]

Check if a node matches a pattern.

Parameters:
  • node – Node to match against

  • pattern – Pattern to match

Returns:

True if node matches pattern

replace(tree: Node, pattern: Pattern, replacement: Node | Callable[[Node], Node]) int[source]

Replace all nodes matching pattern with replacement.

Parameters:
  • tree – Root node of tree

  • pattern – Pattern to match

  • replacement – Node or function to generate replacement

Returns:

Number of replacements made

class AlgoTree.PrettyTree(style: ~typing.Dict[str, str] | None = None, node_name: ~typing.Callable[[~typing.Any], str] = <function PrettyTree.<lambda>>, indent: int = 7, mark: ~typing.List[str] | None = None, node_details: ~typing.Callable[[~typing.Any], ~typing.Any] | None = None)[source]

Bases: object

A class to print a tree in a more readable way.

default_style = {'child_connector': '├', 'horizontal': '─', 'last_child_connector': '└', 'markers': ['🔵', '🔴', '🟢', '🟣', '🟠', '🟡', '🟤', '⚫', '⚪', '⭕', '🔘'], 'payload_connector': '◄', 'spacer': ' ', 'vertical': '│'}
static mark(name: str, markers: List[str]) str[source]

Get the marker for a node based on the hash of the node name.

Parameters:

name – The name of the node.

Returns:

The marker for the node.

class AlgoTree.TreeBuilder[source]

Bases: object

Fluent API for building trees with method chaining.

Example

tree = (TreeBuilder()

.root(“company”, type=”corporation”) .child(“engineering”, head=”Alice”)

.child(“frontend”, team_size=5) .sibling(“backend”, team_size=8) .up()

.sibling(“sales”, head=”Bob”) .build())

build() Node[source]

Build and return the tree.

Returns:

The root node of the constructed tree.

child(name: str, **payload) TreeBuilder[source]

Add a child to the current node and move to it.

Parameters:
  • name – Name for the child node.

  • **payload – Data for the child node.

Returns:

Self for method chaining.

root(name: str, **payload) TreeBuilder[source]

Create the root node.

Parameters:
  • name – Name for the root node.

  • **payload – Data for the root node.

Returns:

Self for method chaining.

sibling(name: str, **payload) TreeBuilder[source]

Add a sibling to the current node.

Parameters:
  • name – Name for the sibling node.

  • **payload – Data for the sibling node.

Returns:

Self for method chaining.

to_root() TreeBuilder[source]

Move to the root node.

Returns:

Self for method chaining.

up(levels: int = 1) TreeBuilder[source]

Move up in the tree by specified levels.

Parameters:

levels – Number of levels to move up. Default is 1.

Returns:

Self for method chaining.

class AlgoTree.TreeDSL[source]

Bases: object

Parser for various tree DSL formats.

static parse(text: str, format: str = 'auto') Node[source]

Parse tree from DSL text.

Parameters:
  • text – DSL text to parse.

  • format – Format to use (‘visual’, ‘indent’, ‘sexpr’, ‘auto’). ‘auto’ tries to detect format automatically.

Returns:

Root node of parsed tree.

class AlgoTree.TreeExporter[source]

Bases: object

Base class for tree exporters.

static to_ascii(node: Node, style: str = 'ascii') str[source]

Export tree to ASCII/Unicode art.

Parameters:
  • node – Root node of tree

  • style – “ascii” for ASCII characters, “unicode” for box drawing

Returns:

String representation of tree

static to_dict(node: Node) Dict[str, Any][source]

Export tree to dictionary (already implemented in Node).

static to_graphviz(node: Node, name: str = 'Tree', node_attr: Callable[[Node], Dict[str, str]] | None = None, edge_attr: Callable[[Node, Node], Dict[str, str]] | None = None, graph_attr: Dict[str, str] | None = None) str[source]

Export tree to GraphViz DOT format.

Parameters:
  • node – Root node of tree

  • name – Graph name

  • node_attr – Function to generate node attributes

  • edge_attr – Function to generate edge attributes

  • graph_attr – Graph-level attributes

Returns:

DOT format string

Example

dot = TreeExporter.to_graphviz(tree,

node_attr=lambda n: {“label”: f”{n.name}n{n.payload.get(‘size’, ‘’)}”})

static to_html(node: Node, include_styles: bool = True, collapsible: bool = True) str[source]

Export tree to interactive HTML.

Parameters:
  • node – Root node of tree

  • include_styles – Include CSS styles

  • collapsible – Make tree nodes collapsible

Returns:

HTML string representation

static to_json(node: Node, indent: int = 2) str[source]

Export tree to JSON string.

static to_mermaid(node: Node, direction: str = 'TD', node_shape: str = 'round', node_text: Callable[[Node], str] | None = None) str[source]

Export tree to Mermaid diagram format.

Parameters:
  • node – Root node of tree

  • direction – Graph direction (TD, TB, BT, RL, LR)

  • node_shape – Shape style (“round”, “square”, “circle”, “rhombus”, “stadium”)

  • node_text – Function to generate node text

Returns:

Mermaid format string

Example

mermaid = TreeExporter.to_mermaid(tree,

node_text=lambda n: f”{n.name}<br/>{n.payload.get(‘type’, ‘’)}”)

static to_unicode(node: Node) str[source]

Export tree to Unicode box drawing (alias for to_ascii with unicode style).

static to_xml(node: Node, root_tag: str = 'tree', indent: int = 2) str[source]

Export tree to XML format.

Parameters:
  • node – Root node of tree

  • root_tag – Tag name for root element

  • indent – Number of spaces per level

Returns:

XML string representation

static to_yaml(node: Node, indent: int = 2) str[source]

Export tree to YAML-like indented format.

Parameters:
  • node – Root node of tree

  • indent – Number of spaces per level

Returns:

YAML-like string representation

AlgoTree.dotannotate(tree: Node, annotator: Callable[[Node], Dict[str, Any]] | Dict[str, Any], annotation_key: str = '_annotation', dot_path: str = '**', in_place: bool = False) Node[source]

Add annotations to nodes in the tree.

Parameters:
  • tree – Tree to annotate

  • annotator – Either: - Function that returns annotation dict for each node - Static annotation dict to add to all matching nodes

  • annotation_key – Key to store annotations under in payload

  • dot_path – Pattern for nodes to annotate

  • in_place – If True, modify tree in place

Returns:

Annotated tree

Examples

# Add computed annotations tree = dotannotate(tree,

lambda n: {

“depth”: n.level, “has_children”: len(n.children) > 0, “path”: “.”.join(p.name for p in n.get_path())

})

# Add static annotations to specific nodes tree = dotannotate(tree,

{“reviewed”: True, “version”: “1.0”}, dot_path=”**.critical_*”)

AlgoTree.dotcollect(tree: Node, collector: Callable[[Node, Any], Any], initial: Any = None, dot_path: str = '**') Any[source]

Collect data from tree using a collector function.

Similar to reduce but more focused on building collections.

Parameters:
  • tree – Tree to collect from

  • collector – Function (node, accumulator) -> new_accumulator

  • initial – Initial accumulator value

  • dot_path – Pattern to match nodes

Returns:

Final collected value

Examples

# Collect into a dict by type by_type = dotcollect(tree,

lambda n, acc: {

**acc, n.payload.get(“type”, “unknown”):

acc.get(n.payload.get(“type”, “unknown”), []) + [n.name]

}, initial={})

# Collect statistics stats = dotcollect(tree,

lambda n, acc: {

“count”: acc[“count”] + 1, “total_size”: acc[“total_size”] + n.payload.get(“size”, 0), “max_depth”: max(acc[“max_depth”], n.level)

}, initial={“count”: 0, “total_size”: 0, “max_depth”: 0})

AlgoTree.dotcount(tree: Node, dot_path: str) int[source]

Count nodes matching a path pattern.

Parameters:
  • tree – Tree to count in

  • dot_path – Dot notation path pattern

Returns:

Number of matching nodes

AlgoTree.dotexists(tree: Node, dot_path: str) bool[source]

Check if a path exists in the tree.

Inspired by dotsuite’s dotexists from the Truth pillar.

Parameters:
  • tree – Tree to check

  • dot_path – Dot notation path

Returns:

True if path exists, False otherwise

AlgoTree.dotextract(tree: Node, extractor: Callable[[Node], Any], dot_path: str = '**', flatten: bool = True) List[Any] | Dict[str, Any][source]

Extract data from tree nodes using custom extractor.

Parameters:
  • tree – Tree to extract from

  • extractor – Function to extract data from each node

  • dot_path – Pattern to match nodes

  • flatten – If True, return flat list; if False, preserve structure

Returns:

Extracted data as list or structured dict

Examples

# Extract all sizes sizes = dotextract(tree, lambda n: n.payload.get(“size”))

# Extract node info for files files = dotextract(tree,

lambda n: {“name”: n.name, “size”: n.payload[“size”]}, dot_path=”**[type=file]”)

AlgoTree.dotfilter(tree: Node, filter_expr: str) List[Node][source]

Filter tree nodes using advanced expressions.

Supports: - Comparison operators: >, <, >=, <=, ==, != - Logical operators: and, or, not - Path expressions: @.property - Functions: contains(), startswith(), endswith()

Parameters:
  • tree – Tree to filter

  • filter_expr – Filter expression

Returns:

List of matching nodes

Examples

# Size greater than 100 dotfilter(tree, “@.size > 100”)

# Name contains “test” and has children dotfilter(tree, “contains(@.name, ‘test’) and @.children.length > 0”)

AlgoTree.dotflatten(tree: Node, flatten_pattern: str = '**', max_depth: int | None = None) List[Node][source]

Flatten tree structure into a list of nodes.

Parameters:
  • tree – Tree to flatten

  • flatten_pattern – Pattern for nodes to include

  • max_depth – Maximum depth to flatten to

Returns:

List of nodes (flattened)

Examples

# Get all nodes as flat list all_nodes = dotflatten(tree)

# Get only file nodes files = dotflatten(tree, “**[type=file]”)

# Flatten only top 3 levels top_nodes = dotflatten(tree, max_depth=3)

AlgoTree.dotgraft(tree: Node, graft_point: str, subtree: Node, position: int | str = 'append', in_place: bool = False) Node[source]

Graft a subtree onto a tree at specified point(s).

Parameters:
  • tree – Tree to graft onto

  • graft_point – Dot path to graft point(s)

  • subtree – Subtree to graft

  • position – Where to add the subtree: - “append”: Add at end of children (default) - “prepend”: Add at beginning of children - int: Insert at specific index - “replace”: Replace existing children

  • in_place – If True, modify tree in place

Returns:

Tree with grafted subtree

Examples

# Add new module to app new_module = Node(“auth”, type=”module”) tree = dotgraft(tree, “app.modules”, new_module)

# Insert at specific position tree = dotgraft(tree, “app.modules”, new_module, position=0)

# Replace children tree = dotgraft(tree, “app.old_modules”, new_modules, position=”replace”)

AlgoTree.dotgroup(tree: Node, grouper: str | Callable[[Node], Any], dot_path: str = '**') Dict[Any, List[Node]][source]

Group tree nodes by a key.

Parameters:
  • tree – Tree to group nodes from

  • grouper – Either: - String: payload key to group by - Callable: function to compute group key

  • dot_path – Pattern to match nodes

Returns:

Dictionary mapping group keys to lists of nodes

Examples

# Group by type by_type = dotgroup(tree, “type”)

# Group by custom function by_level = dotgroup(tree, lambda n: n.level)

# Group files by extension by_ext = dotgroup(tree,

lambda n: n.name.split(“.”)[-1] if “.” in n.name else “no_ext”, dot_path=”**[type=file]”)

AlgoTree.dotmap(tree: Node, mapper: Callable[[Node], Dict[str, Any]] | Dict[str, Callable], dot_path: str = '**', in_place: bool = False) Node[source]

Map a transformation function over nodes matching a pattern.

This is a convenience function that applies the same transformation to all nodes matching a pattern.

Parameters:
  • tree – Tree to transform

  • mapper – Either: - Function that takes a node and returns dict to update payload - Dict mapping payload keys to transformation functions

  • dot_path – Pattern to match nodes (default “**” for all nodes)

  • in_place – If True, modify tree in place

Returns:

Transformed tree

Examples

# Double all sizes tree = dotmap(tree, lambda n: {“size”: n.payload.get(“size”, 0) * 2})

# Transform specific fields tree = dotmap(tree, {

“size”: lambda v: v * 2, “name”: lambda v: v.upper(), “timestamp”: lambda v: str(v)

})

# Apply only to specific nodes tree = dotmap(tree,

lambda n: {“processed”: True}, dot_path=”app.data.**”)

AlgoTree.dotmatch(tree: Node, dot_path: str, return_paths: bool = False) List[Node] | List[str][source]

Match nodes using dot notation paths, inspired by dotsuite.

This provides a clean, intuitive way to navigate and match tree paths using dot notation similar to JSONPath or object property access.

Parameters:
  • tree – Tree to search

  • dot_path – Dot notation path (e.g., “root.branch.*.leaf”, “app.**.test”)

  • return_paths – If True, return dot paths to matches instead of nodes

Returns:

List of matching nodes or their dot paths

Examples

# Find all test files dotmatch(tree, “src.**.test_*”)

# Find specific path dotmatch(tree, “app.models.user”)

# Find with wildcards dotmatch(tree, “data.*.value”)

# Get paths instead of nodes paths = dotmatch(tree, “**.config”, return_paths=True) # Returns: [“app.config”, “modules.auth.config”, …]

AlgoTree.dotmerge(tree1: Node, tree2: Node, merge_strategy: str = 'overlay', conflict_resolver: Callable[[Node, Node], Node] | None = None, in_place: bool = False) Node[source]

Merge two trees using various strategies.

Parameters:
  • tree1 – First tree (base)

  • tree2 – Second tree (overlay)

  • merge_strategy – Strategy for merging: - “overlay”: tree2 values override tree1 - “underlay”: tree1 values take precedence - “combine”: merge payloads, keeping both values - “custom”: use conflict_resolver function

  • conflict_resolver – Function to resolve conflicts (for custom strategy)

  • in_place – If True, modify tree1 in place

Returns:

Merged tree

Examples

# Simple overlay merge merged = dotmerge(tree1, tree2, “overlay”)

# Custom conflict resolution def resolver(node1, node2):

# Combine arrays, prefer tree2 for other values merged_payload = {} for key in set(node1.payload.keys()) | set(node2.payload.keys()):

if key in node1.payload and key in node2.payload:

val1, val2 = node1.payload[key], node2.payload[key] if isinstance(val1, list) and isinstance(val2, list):

merged_payload[key] = val1 + val2

else:

merged_payload[key] = val2

elif key in node1.payload:

merged_payload[key] = node1.payload[key]

else:

merged_payload[key] = node2.payload[key]

return Node(node2.name, **merged_payload)

merged = dotmerge(tree1, tree2, “custom”, conflict_resolver=resolver)

AlgoTree.dotmod(tree: Node, transformations: Dict[str, Any] | List[Tuple[str, Any]], in_place: bool = False) Node[source]

Apply closed tree-to-tree transformations using dot notation paths.

This function modifies nodes in the tree based on dot notation paths, preserving the tree structure (closed transformation).

Parameters:
  • tree – Tree to transform

  • transformations – Either: - Dict mapping dot paths to transformations - List of (dot_path, transformation) tuples

  • in_place – If True, modify tree in place; otherwise create a copy

Returns:

Transformed tree (same object if in_place=True, copy otherwise)

Transformations can be:
  • Dict: Update node payload with dict contents

  • Callable: Apply function to node (receives node, returns dict to update payload)

  • String: Rename the node

  • None: Clear the payload (but keep the node)

Examples

# Update specific nodes tree = dotmod(tree, {

“app.config”: {“version”: “2.0”, “debug”: False}, “app.database”: {“host”: “localhost”, “port”: 5432}

})

# Rename nodes tree = dotmod(tree, {

“app.oldname”: “newname”

})

# Apply functions tree = dotmod(tree, {

“app.**.file”: lambda n: {“size”: n.payload.get(“size”, 0) * 2}

})

# Multiple transformations to same path pattern tree = dotmod(tree, [

(“app.**.test_*”, lambda n: {“tested”: True}), (“app.**.test_*”, lambda n: {“priority”: “high”})

])

AlgoTree.dotnormalize(tree: Node, normalizer: Callable[[str], str] | None = None, normalize_payload: bool = False, in_place: bool = False) Node[source]

Normalize node names and optionally payload keys.

Parameters:
  • tree – Tree to normalize

  • normalizer – Function to normalize names (default: lowercase + underscore)

  • normalize_payload – If True, also normalize payload keys

  • in_place – If True, modify tree in place

Returns:

Normalized tree

Examples

# Default normalization (lowercase, spaces to underscores) tree = dotnormalize(tree)

# Custom normalization tree = dotnormalize(tree,

normalizer=lambda s: s.lower().replace(“-”, “_”))

# Also normalize payload keys tree = dotnormalize(tree, normalize_payload=True)

AlgoTree.dotpartition(tree: Node, predicate: Callable[[Node], bool], dot_path: str = '**') Tuple[List[Node], List[Node]][source]

Partition nodes into two groups based on predicate.

Parameters:
  • tree – Tree to partition

  • predicate – Function returning True for first group, False for second

  • dot_path – Pattern to match nodes

Returns:

Tuple of (matching_nodes, non_matching_nodes)

Examples

# Partition by size large, small = dotpartition(tree, lambda n: n.payload.get(“size”, 0) > 1000)

# Partition enabled/disabled enabled, disabled = dotpartition(tree,

lambda n: n.payload.get(“enabled”, False), dot_path=”app.modules.*”)

AlgoTree.dotpipe(tree: Node, *transformers: Callable | Tuple[str, Callable]) Any[source]

Pipe tree through a series of transformations.

This is the main function for open transformations - converting trees to any structure through a pipeline of operations.

Parameters:
  • tree – Tree to transform

  • *transformers – Variable number of transformers, each can be: - Callable: Applied to entire result of previous stage - Tuple[str, Callable]: (dot_path, transformer) applies to matched nodes

Returns:

Result of the final transformation (can be any type)

Examples

# Extract all names as a list names = dotpipe(tree,

lambda t: [n.name for n in t.traverse_preorder()])

# Pipeline of transformations result = dotpipe(tree,

(”**.config”, lambda n: n.payload), # Extract config payloads lambda configs: {c.get(“name”): c for c in configs}, # Dict by name lambda d: list(d.values())) # Back to list

# Convert tree to nested dict data = dotpipe(tree, to_dict)

# Extract and process specific data totals = dotpipe(tree,

(”**[type=file]”, lambda n: n.payload.get(“size”, 0)), sum) # Sum all sizes

AlgoTree.dotpluck(tree: Node, *dot_paths: str) List[Any][source]

Extract values from tree using dot notation paths.

Inspired by dotsuite’s dotpluck, this extracts payload values from nodes at specified paths.

Parameters:
  • tree – Tree to extract from

  • *dot_paths – Variable number of dot notation paths

Returns:

List of values (payload or None for missing paths)

Examples

# Extract multiple values values = dotpluck(tree, “user.name”, “user.age”, “user.email”)

# Extract with wildcards (returns all matching values) values = dotpluck(tree, “users.*.name”)

AlgoTree.dotproject(tree: Node, projection: List[str] | Dict[str, str], dot_path: str = '**') List[Dict[str, Any]][source]

Project specific fields from nodes (like SQL SELECT).

Parameters:
  • tree – Tree to project from

  • projection – Either: - List of field names to include - Dict mapping field names to aliases

  • dot_path – Pattern to match nodes

Returns:

List of projected dictionaries

Examples

# Select specific fields data = dotproject(tree, [“name”, “size”, “type”])

# With aliases data = dotproject(tree, {

“name”: “file_name”, “size”: “file_size”, “type”: “file_type”

})

AlgoTree.dotprune(tree: Node, condition: str | Callable[[Node], bool], keep_structure: bool = False, in_place: bool = False) Node[source]

Prune nodes from tree based on condition.

Parameters:
  • tree – Tree to prune

  • condition – Either: - Dot path pattern of nodes to remove - Predicate function (returns True for nodes to remove)

  • keep_structure – If True, replace pruned nodes with empty placeholders

  • in_place – If True, modify tree in place

Returns:

Pruned tree

Examples

# Remove all test files tree = dotprune(tree, “**.test_*”)

# Remove empty directories tree = dotprune(tree, lambda n: n.payload.get(“type”) == “dir” and len(n.children) == 0)

# Keep structure but clear nodes tree = dotprune(tree, “**.deprecated”, keep_structure=True)

AlgoTree.dotreduce(tree: Node, reducer: Callable[[Any, Node], Any], initial: Any = None, traverse_pattern: str = '**') Any[source]

Reduce tree to a single value using a reducer function.

Parameters:
  • tree – Tree to reduce

  • reducer – Function (accumulator, node) -> new_accumulator

  • initial – Initial value for accumulator

  • traverse_pattern – Pattern for nodes to include in reduction

Returns:

Reduced value

Examples

# Sum all sizes total_size = dotreduce(tree,

lambda acc, n: acc + n.payload.get(“size”, 0), initial=0)

# Collect all names names = dotreduce(tree,

lambda acc, n: acc + [n.name], initial=[])

# Find maximum depth max_depth = dotreduce(tree,

lambda acc, n: max(acc, n.level), initial=0)

AlgoTree.dotsplit(tree: Node, split_point: str, include_point: bool = True) Tuple[Node, List[Node]][source]

Split tree at specified point(s), extracting subtrees.

Parameters:
  • tree – Tree to split

  • split_point – Dot path to split point(s)

  • include_point – If True, include split point in extracted subtrees

Returns:

Tuple of (modified tree, list of extracted subtrees)

Examples

# Extract all test modules tree, test_modules = dotsplit(tree, “**.tests”)

# Extract but keep the container node tree, extracted = dotsplit(tree, “app.deprecated”, include_point=False)

AlgoTree.dotvalidate(tree: Node, validator: Callable[[Node], bool] | Dict[str, Any], dot_path: str = '**', raise_on_invalid: bool = True) bool | List[Node][source]

Validate nodes in the tree against criteria.

Parameters:
  • tree – Tree to validate

  • validator – Either: - Predicate function returning True for valid nodes - Dict of required attributes and values

  • dot_path – Pattern for nodes to validate

  • raise_on_invalid – If True, raise exception on first invalid node

Returns:

List of invalid nodes (empty if all valid) If raise_on_invalid=True: True if all valid (raises otherwise)

Return type:

If raise_on_invalid=False

Examples

# Validate with function dotvalidate(tree,

lambda n: n.payload.get(“size”, 0) < 1000000, dot_path=”**[type=file]”)

# Validate required attributes dotvalidate(tree,

{“type”: “file”, “tested”: True}, dot_path=”app.src.**”)

# Get list of invalid nodes invalid = dotvalidate(tree,

lambda n: len(n.name) <= 255, raise_on_invalid=False)

AlgoTree.dumps(tree: Node, **json_kwargs) str[source]

Serialize tree to JSON string.

Parameters:
  • tree – Tree to serialize

  • **json_kwargs – Arguments passed to json.dumps (indent, sort_keys, etc.)

Returns:

JSON string

Example

json_str = dumps(tree, indent=2, sort_keys=True)

AlgoTree.export_tree(node: Node, format: str, **kwargs) str[source]

Export tree to specified format.

Parameters:
  • node – Root node of tree

  • format – Export format (json, ascii, unicode, graphviz, mermaid, yaml, xml, html)

  • **kwargs – Format-specific options

Returns:

String representation in specified format

AlgoTree.load(path: str | Path) Node[source]

Load tree from JSON file.

Automatically handles gzip compression if file ends with .gz

Parameters:

path – File path

Returns:

Root node of loaded tree

Example

tree = load(“my_tree.json”) tree = load(“my_tree.json.gz”) # Auto-detects compression

AlgoTree.loads(json_str: str) Node[source]

Deserialize tree from JSON string.

Parameters:

json_str – JSON string

Returns:

Root node of tree

Example

tree = loads(‘{“name”: “root”, “children”: […]}’)

AlgoTree.parse_tree(text: str, format: str = 'auto') Node[source]

Convenience function to parse tree from DSL text.

Parameters:
  • text – DSL text to parse.

  • format – Format to use (‘visual’, ‘indent’, ‘sexpr’, ‘auto’).

Returns:

Root node of parsed tree.

Examples

# Visual format tree = parse_tree(‘’’

company ├── engineering │ ├── frontend │ └── backend └── sales

‘’’)

# Indent format tree = parse_tree(‘’’

company
engineering

frontend backend

sales

‘’’)

# S-expression format tree = parse_tree(‘’’

(company
(engineering

(frontend) (backend))

(sales))

‘’’)

AlgoTree.pattern_match(tree: Node, pattern: str | Dict | Pattern, match_type: MatchType = MatchType.PARTIAL) List[Node][source]

Convenience function to find all matches of a pattern in a tree.

Parameters:
  • tree – Tree to search

  • pattern – Pattern as string, dict, or Pattern object

  • match_type – Type of matching

Returns:

List of matching nodes

AlgoTree.pretty_tree(node, **kwargs) str[source]

Converts a tree to a pretty string representation.

Parameters:

kwargs – Key-word arguments. See PrettyTree for more details.

Returns:

A pretty string representation of the tree.

AlgoTree.save(tree: Node, path: str | Path, compress: bool = False) None[source]

Save tree to JSON file.

This is the recommended way to persist trees. Uses the natural hierarchical JSON representation.

Parameters:
  • tree – Tree to save

  • path – File path (can be string or Path object)

  • compress – If True, use gzip compression

Example

save(tree, “my_tree.json”) save(tree, “my_tree.json.gz”, compress=True)

AlgoTree.save_tree(node: Node, filepath: str, format: str | None = None, **kwargs)[source]

Save tree to file in specified format.

Parameters:
  • node – Root node of tree

  • filepath – Output file path

  • format – Export format (auto-detected from extension if not specified)

  • **kwargs – Format-specific options

AlgoTree.to_adjacency_list(node: Node) Dict[str, List[str]][source]

Convert tree to adjacency list representation.

Parameters:

node – Root node

Returns:

Dictionary mapping node names to lists of child names

Examples

adj = to_adjacency_list(tree) # {“app”: [“config”, “database”], “config”: [], …}

AlgoTree.to_dict(node: Node, include_children: bool = True, include_parent: bool = False, key_mapper: Callable[[str], str] | None = None) Dict[str, Any][source]

Convert tree node to dictionary representation.

Parameters:
  • node – Node to convert

  • include_children – If True, recursively include children

  • include_parent – If True, include parent reference (careful: can be circular)

  • key_mapper – Optional function to transform keys

Returns:

Dictionary representation of the node

Examples

# Simple conversion data = to_dict(tree)

# Without children (just this node) node_data = to_dict(tree, include_children=False)

# With key transformation data = to_dict(tree, key_mapper=str.upper)

AlgoTree.to_edge_list(node: Node, include_root: bool = False) List[Tuple[str, str]][source]

Convert tree to edge list representation.

Parameters:
  • node – Root node

  • include_root – If True, include edge from None to root

Returns:

List of (parent, child) tuples

Examples

edges = to_edge_list(tree) # [(“app”, “config”), (“app”, “database”), …]

AlgoTree.to_graphviz_data(tree: Node) Dict[str, Any][source]

Convert tree to GraphViz data structure.

Returns dict with ‘nodes’ and ‘edges’ suitable for visualization.

AlgoTree.to_json_schema(tree: Node, type_key: str = 'type', required_key: str = 'required') Dict[str, Any][source]

Convert tree to JSON Schema-like structure.

Useful for representing configuration schemas or data models.

AlgoTree.to_list(node: Node, traverse_order: str = 'preorder', include_data: bool = True) List[str | Dict[str, Any]][source]

Convert tree to flat list representation.

Parameters:
  • node – Root node

  • traverse_order – Traversal order (“preorder”, “postorder”, “levelorder”)

  • include_data – If True, include full node data; if False, just names

Returns:

List of nodes (as dicts or names)

Examples

# List of all node names names = to_list(tree, include_data=False)

# List of node data in level order nodes = to_list(tree, traverse_order=”levelorder”)

AlgoTree.to_nested_lists(node: Node) List[source]

Convert tree to nested list structure (like S-expressions).

Parameters:

node – Root node

Returns:

Nested list representation

Examples

nested = to_nested_lists(tree) # [“app”, [“config”], [“database”], [“modules”, [“auth”], [“api”]]]

AlgoTree.to_paths(node: Node, path_separator: str = '.', include_payload: bool = False) List[str] | Dict[str, Any][source]

Convert tree to path representations.

Parameters:
  • node – Root node

  • path_separator – Separator for path components

  • include_payload – If True, return dict mapping paths to payloads

Returns:

List of paths or dict mapping paths to payloads

Examples

# List of all paths paths = to_paths(tree) # [“app”, “app.config”, “app.database”, …]

# Paths with payloads path_data = to_paths(tree, include_payload=True) # {“app”: {…}, “app.config”: {…}, …}

# Using different separator paths = to_paths(tree, path_separator=”/”) # [“app”, “app/config”, “app/database”, …]

AlgoTree.to_table(node: Node, columns: List[str] | None = None, include_path: bool = True) List[Dict[str, Any]][source]

Convert tree to tabular/relational format.

Parameters:
  • node – Root node

  • columns – Specific payload columns to include (None = all)

  • include_path – If True, include full path to node

Returns:

List of row dictionaries suitable for DataFrame or CSV

Examples

# Convert to table format rows = to_table(tree, columns=[“type”, “size”, “enabled”])

# Can be used with pandas: # df = pd.DataFrame(rows)