File: class_node.py

package info (click to toggle)
opencv 4.10.0%2Bdfsg-5
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 282,092 kB
  • sloc: cpp: 1,178,079; xml: 682,621; python: 49,092; lisp: 31,150; java: 25,469; ansic: 11,039; javascript: 6,085; sh: 1,214; cs: 601; perl: 494; objc: 210; makefile: 173
file content (190 lines) | stat: -rw-r--r-- 7,019 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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
106
107
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
from typing import Type, Sequence, NamedTuple, Optional, Tuple, Dict
import itertools

import weakref

from .node import ASTNode, ASTNodeType

from .function_node import FunctionNode
from .enumeration_node import EnumerationNode
from .constant_node import ConstantNode

from .type_node import TypeNode, TypeResolutionError


class ClassProperty(NamedTuple):
    name: str
    type_node: TypeNode
    is_readonly: bool

    @property
    def typename(self) -> str:
        return self.type_node.full_typename

    def resolve_type_nodes(self, root: ASTNode) -> None:
        try:
            self.type_node.resolve(root)
        except TypeResolutionError as e:
            raise TypeResolutionError(
                'Failed to resolve "{}" property'.format(self.name)
            ) from e

    def relative_typename(self, full_node_name: str) -> str:
        """Typename relative to the passed AST node name.

        Args:
            full_node_name (str): Full export name of the AST node

        Returns:
            str: typename relative to the passed AST node name
        """
        return self.type_node.relative_typename(full_node_name)


class ClassNode(ASTNode):
    """Represents a C++ class that is also a class in Python.

    ClassNode can have functions (methods), enumerations, constants and other
    classes as its children nodes.

    Class properties are not treated as a part of AST for simplicity and have
    extra handling if required.
    """
    def __init__(self, name: str, parent: Optional[ASTNode] = None,
                 export_name: Optional[str] = None,
                 bases: Sequence["weakref.ProxyType[ClassNode]"] = (),
                 properties: Sequence[ClassProperty] = ()) -> None:
        super().__init__(name, parent, export_name)
        self.bases = list(bases)
        self.properties = properties

    @property
    def weight(self) -> int:
        return 1 + sum(base.weight for base in self.bases)

    @property
    def children_types(self) -> Tuple[ASTNodeType, ...]:
        return (ASTNodeType.Class, ASTNodeType.Function,
                ASTNodeType.Enumeration, ASTNodeType.Constant)

    @property
    def node_type(self) -> ASTNodeType:
        return ASTNodeType.Class

    @property
    def classes(self) -> Dict[str, "ClassNode"]:
        return self._children[ASTNodeType.Class]

    @property
    def functions(self) -> Dict[str, FunctionNode]:
        return self._children[ASTNodeType.Function]

    @property
    def enumerations(self) -> Dict[str, EnumerationNode]:
        return self._children[ASTNodeType.Enumeration]

    @property
    def constants(self) -> Dict[str, ConstantNode]:
        return self._children[ASTNodeType.Constant]

    def add_class(self, name: str,
                  bases: Sequence["weakref.ProxyType[ClassNode]"] = (),
                  properties: Sequence[ClassProperty] = ()) -> "ClassNode":
        return self._add_child(ClassNode, name, bases=bases,
                               properties=properties)

    def add_function(self, name: str, arguments: Sequence[FunctionNode.Arg] = (),
                     return_type: Optional[FunctionNode.RetType] = None,
                     is_static: bool = False) -> FunctionNode:
        """Adds function as a child node of a class.

        Function is classified in 3 categories:
            1. Instance method.
               If function is an instance method then `self` argument is
               inserted at the beginning of its arguments list.

            2. Class method (or factory method)
               If `is_static` flag is `True` and typename of the function
               return type matches name of the class then function is treated
               as class method.

               If function is a class method then `cls` argument is inserted
               at the beginning of its arguments list.

            3. Static method

        Args:
            name (str): Name of the function.
            arguments (Sequence[FunctionNode.Arg], optional): Function arguments.
                Defaults to ().
            return_type (Optional[FunctionNode.RetType], optional): Function
                return type. Defaults to None.
            is_static (bool, optional): Flag whenever function is static or not.
                Defaults to False.

        Returns:
            FunctionNode: created function node.
        """

        arguments = list(arguments)
        if return_type is not None:
            is_classmethod = return_type.typename == self.name
        else:
            is_classmethod = False
        if not is_static:
            arguments.insert(0, FunctionNode.Arg("self"))
        elif is_classmethod:
            is_static = False
            arguments.insert(0, FunctionNode.Arg("cls"))
        return self._add_child(FunctionNode, name, arguments=arguments,
                               return_type=return_type, is_static=is_static,
                               is_classmethod=is_classmethod)

    def add_enumeration(self, name: str) -> EnumerationNode:
        return self._add_child(EnumerationNode, name)

    def add_constant(self, name: str, value: str) -> ConstantNode:
        return self._add_child(ConstantNode, name, value=value)

    def add_base(self, base_class_node: "ClassNode") -> None:
        self.bases.append(weakref.proxy(base_class_node))

    def resolve_type_nodes(self, root: ASTNode) -> None:
        """Resolves type nodes for all inner-classes, methods and properties
        in 2 steps:
            1. Resolve against `self` as a tree root
            2. Resolve against `root` as a tree root
        Type resolution errors are postponed until all children nodes are
        examined.

        Args:
            root (Optional[ASTNode], optional): Root of the AST sub-tree.
                Defaults to None.
        """

        errors = []
        for child in itertools.chain(self.properties,
                                     self.functions.values(),
                                     self.classes.values()):
            try:
                try:
                    # Give priority to narrowest scope (class-level scope in this case)
                    child.resolve_type_nodes(self)  # type: ignore
                except TypeResolutionError:
                    child.resolve_type_nodes(root)  # type: ignore
            except TypeResolutionError as e:
                errors.append(str(e))
        if len(errors) > 0:
            raise TypeResolutionError(
                'Failed to resolve "{}" class against "{}". Errors: {}'.format(
                    self.full_export_name, root.full_export_name, errors
                )
            )


class ProtocolClassNode(ClassNode):
    def __init__(self, name: str, parent: Optional[ASTNode] = None,
                 export_name: Optional[str] = None,
                 properties: Sequence[ClassProperty] = ()) -> None:
        super().__init__(name, parent, export_name, bases=(),
                         properties=properties)