ControlFlowException.__repr__()   A
last analyzed

Complexity

Conditions 1

Size

Total Lines 2
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nop 1
dl 0
loc 2
rs 10
c 0
b 0
f 0
1
"""Common definition.
2
3
CallableFunction Type:
4
5
Specify something callable with or without async:
6
7
```CallableFunction = Union[Callable[..., Awaitable[Any]], Callable]```
8
9
Function signature of async function implementation:
10
11
```AsyncInnerFunction = Callable[[], Awaitable[Any]]```
12
13
"""
14
# from collections import namedtuple
15
from typing import Any, Awaitable, Callable, List, NamedTuple, Optional, TypeVar, Union, no_type_check
16
17
__all__ = [
18
    'CallableFunction',
19
    'AsyncInnerFunction',
20
    'SUCCESS',
21
    'FAILURE',
22
    'ControlFlowException',
23
    'NodeMetadata',
24
    'node_metadata',
25
    'get_node_metadata',
26
]
27
28
29
CallableFunction = Union[Callable[..., Awaitable[Any]], Callable]
30
"""Something callable with or without async."""
31
32
AsyncInnerFunction = Callable[[], Awaitable[Any]]
33
"""Function signature of async function implementation."""
34
35
SUCCESS = True  # a success call
36
"""Success constant."""
37
38
FAILURE = not SUCCESS  # Well defined falsy...
39
"""Failure constant."""
40
41
42
class ControlFlowException(Exception):
43
    """ControlFlowException exception is a decorator on a real exception.
44
45
    This will ensure that ```assert ControlFlowException.__bool__ == False```.
46
    This permit to return exception as a 'FAILURE' status.
47
    """
48
49
    def __init__(self, exception: Exception):
50
        super().__init__()
51
52
        self.exception = exception
53
54
    def __bool__(self):
55
        return False
56
57
    def __repr__(self):
58
        return self.exception.__repr__()
59
60
    def __str__(self):
61
        return self.exception.__str__()
62
63
    @classmethod
64
    def instanciate(cls, exception: Exception):
65
        # this methods simplify usage of hierarchical call tree.
66
        return exception if isinstance(exception, ControlFlowException) else ControlFlowException(exception=exception)
67
68
69
class NodeMetadata(NamedTuple):
70
    """NodeMetadata is our node definition.
71
72
    A NodeMetadata is used to keep information on name, properties name,
73
    and relations ship name between a hierachical construct of functions.
74
75
    This permit us to print or analyze all information of a behaviour tree.
76
77
    Attributes:
78
        name (str): named operation
79
        properties (List[str]): a list of property name (an int value, ...).
80
        edges (List[str]): a list of member name which act as edges (a child, ...).
81
82
    """
83
84
    name: str
85
    properties: Optional[List[str]] = None
86
    edges: Optional[List[str]] = None
87
88
89
T = TypeVar('T', bound=CallableFunction)
90
91
92
def node_metadata(
93
    name: Optional[str] = None, properties: Optional[List[str]] = None, edges: Optional[List[str]] = None
94
):
95
    """'node_metadata' is a function decorator which add meta information about node.
96
97
    We add a property on decorated function named '__node_metadata'.
98
99
    Args:
100
        name (Optional[str]): override name of decorated function,
101
            default is function name left striped with '_'
102
        properties (Optional[List[str]]): a list of property name ([] as default)
103
        edges (Optional[List[str]]): a list of edges name
104
            (["child", "children"] as default)
105
106
    Returns:
107
        the decorator function
108
109
    """
110
111
    def decorate_function(function: T) -> T:
112
        function.__node_metadata = NodeMetadata(
113
            name=name if name else function.__name__.lstrip('_'), properties=properties, edges=edges
114
        )
115
        return function
116
117
    return decorate_function
118
119
120
@no_type_check
121
def get_node_metadata(target: CallableFunction) -> NodeMetadata:
122
    """Returns node metadata instance associated with target."""
123
    node = target.__node_metadata
124
    if not isinstance(node, NodeMetadata):
125
        raise RuntimeError(f'attr __node_metadata of {target} is not a NodeMetadata!')
126
    return node
127