Skip to content

analyze

async_btree.analyze

Analyze definition.

Attributes

Classes

Node

Bases: NamedTuple

Resolved snapshot of a behaviour tree node, produced by analyze().

Holds the display name, resolved property values, and resolved child edges for a single node in the abstract tree.

Attributes:

Name Type Description
name str

display name of the node.

properties list[tuple[str, Any]]

resolved (name, value) pairs for scalar attributes.

edges list[tuple[str, list[Any]]]

resolved (edge_name, [Node, ...]) pairs for child relationships. Typed as list[Any] due to mypy #731 — actual element type is list[Node].

Source code in async_btree/analyze.py
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class Node(NamedTuple):
    """Resolved snapshot of a behaviour tree node, produced by `analyze()`.

    Holds the display name, resolved property values, and resolved child edges
    for a single node in the abstract tree.

    Attributes:
        name (str): display name of the node.
        properties (list[tuple[str, Any]]): resolved `(name, value)` pairs for scalar attributes.
        edges (list[tuple[str, list[Any]]]): resolved `(edge_name, [Node, ...])` pairs for
            child relationships. Typed as `list[Any]` due to
            [mypy #731](https://github.com/python/mypy/issues/731) — actual element type is
            `list[Node]`.
    """

    name: str
    properties: list[tuple[str, Any]]
    # edges: list[tuple[str, list['Node']]]
    # https://github.com/python/mypy/issues/731
    edges: list[tuple[str, list[Any]]]

    def __str__(self) -> str:
        """Return the stringified tree representation of this node."""
        return stringify_analyze(target=self)
Functions
__str__()

Return the stringified tree representation of this node.

Source code in async_btree/analyze.py
34
35
36
def __str__(self) -> str:
    """Return the stringified tree representation of this node."""
    return stringify_analyze(target=self)

Functions

analyze(target)

Analyze target and return a Node representation of its subtree.

Works with any callable (sync or async). If target has __node_metadata, its declared properties and edges are resolved from closure variables. Otherwise, all closure variables are included as properties with no edges.

Parameters:

Name Type Description Default
target CallableFunction

callable to analyze.

required

Returns:

Type Description
Node

resolved node tree rooted at target.

Source code in async_btree/analyze.py
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
@no_type_check  # it's a shortcut for hasattr ...
def analyze(target: CallableFunction) -> Node:
    """Analyze `target` and return a `Node` representation of its subtree.

    Works with any callable (sync or async). If `target` has `__node_metadata`,
    its declared properties and edges are resolved from closure variables. Otherwise,
    all closure variables are included as properties with no edges.

    Args:
        target (CallableFunction): callable to analyze.

    Returns:
        (Node): resolved node tree rooted at `target`.
    """

    nonlocals = getclosurevars(target).nonlocals

    def _get_nonlocals_value_for(name):
        return nonlocals.get(name, None)

    def _analyze_property(p):
        """Return a tuple (name, value) or (name, function name) as property."""
        value = _get_nonlocals_value_for(name=p)
        return p.lstrip("_"), _get_target_propertie_name(value=value)

    def _analyze_edges(egde_name):
        """Lookup children node from egde_name local var."""
        edges = _get_nonlocals_value_for(name=egde_name)
        return (egde_name.lstrip("_"), _analyze_target_edges(edges=edges))

    if hasattr(target, "__node_metadata"):
        node = get_node_metadata(target=target)
        return Node(
            name=node.name,
            properties=list(map(_analyze_property, node.properties)) if node.properties else [],
            edges=list(
                filter(
                    lambda p: p is not None,
                    map(_analyze_edges, node.edges or _DEFAULT_EDGES),
                )
            ),
        )

    # simple function
    return Node(
        name=get_function_name(target=target),
        properties=list(map(_analyze_property, nonlocals.keys())),
        edges=[],
    )

stringify_analyze(target, indent=0, label=None)

Stringify a Node tree into a human-readable indented representation.

Parameters:

Name Type Description Default
target Node

node to stringify.

required
indent int

current indentation level (default 0).

0
label Optional[str]

edge label to prefix the node with (default None).

None

Returns:

Type Description
str

indented string representation of the node tree.

Source code in async_btree/analyze.py
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
def stringify_analyze(target: Node, indent: int = 0, label: str | None = None) -> str:
    """Stringify a `Node` tree into a human-readable indented representation.

    Args:
        target (Node): node to stringify.
        indent (int): current indentation level (default 0).
        label (Optional[str]): edge label to prefix the node with (default `None`).

    Returns:
        (str): indented string representation of the node tree.
    """
    _ident = "    "
    _space = f"{_ident * indent} "
    result: str = ""
    if label:
        result += f"{_space}--({label})--> {target.name}:\n"
        _space += f"{_ident}{' ' * len(label)}"
    else:
        result += f"{_space}--> {target.name}:\n"

    for k, v in target.properties:
        result += f"{_space}    {k}: {v}\n"

    for _label, children in target.edges:
        if children:
            for child in children:
                result += stringify_analyze(target=child, indent=indent + 1, label=_label)
    return result