jaclang 0.7.16__py3-none-any.whl → 0.7.18__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of jaclang might be problematic. Click here for more details.
- jaclang/cli/cli.py +140 -77
- jaclang/compiler/absyntree.py +9 -4
- jaclang/compiler/constant.py +8 -8
- jaclang/compiler/parser.py +10 -2
- jaclang/compiler/passes/main/__init__.py +1 -1
- jaclang/compiler/passes/main/access_modifier_pass.py +96 -147
- jaclang/compiler/passes/main/fuse_typeinfo_pass.py +152 -50
- jaclang/compiler/passes/main/import_pass.py +88 -59
- jaclang/compiler/passes/main/py_collect_dep_pass.py +70 -0
- jaclang/compiler/passes/main/pyast_gen_pass.py +46 -6
- jaclang/compiler/passes/main/pyast_load_pass.py +1 -0
- jaclang/compiler/passes/main/pyjac_ast_link_pass.py +7 -0
- jaclang/compiler/passes/main/schedules.py +9 -2
- jaclang/compiler/passes/main/sym_tab_build_pass.py +9 -5
- jaclang/compiler/passes/main/tests/fixtures/autoimpl.empty.impl.jac +0 -0
- jaclang/compiler/passes/main/tests/fixtures/autoimpl.jac +1 -1
- jaclang/compiler/passes/main/tests/fixtures/py_imp_test.jac +29 -0
- jaclang/compiler/passes/main/tests/fixtures/pygame_mock/__init__.py +3 -0
- jaclang/compiler/passes/main/tests/fixtures/pygame_mock/color.py +3 -0
- jaclang/compiler/passes/main/tests/fixtures/pygame_mock/constants.py +5 -0
- jaclang/compiler/passes/main/tests/fixtures/pygame_mock/display.py +2 -0
- jaclang/compiler/passes/main/tests/test_import_pass.py +72 -13
- jaclang/compiler/passes/main/type_check_pass.py +15 -5
- jaclang/compiler/passes/tool/jac_formatter_pass.py +13 -3
- jaclang/compiler/passes/tool/tests/fixtures/corelib.jac +37 -41
- jaclang/compiler/passes/tool/tests/fixtures/corelib_fmt.jac +37 -41
- jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/access_mod_check.jac +27 -0
- jaclang/compiler/passes/utils/mypy_ast_build.py +45 -0
- jaclang/compiler/symtable.py +16 -11
- jaclang/compiler/tests/test_importer.py +17 -9
- jaclang/langserve/engine.py +64 -16
- jaclang/langserve/server.py +16 -1
- jaclang/langserve/tests/fixtures/import_include_statements.jac +3 -3
- jaclang/langserve/tests/fixtures/rename.jac +30 -0
- jaclang/langserve/tests/test_server.py +224 -6
- jaclang/langserve/utils.py +28 -98
- jaclang/plugin/builtin.py +8 -4
- jaclang/plugin/default.py +86 -64
- jaclang/plugin/feature.py +13 -13
- jaclang/plugin/spec.py +10 -11
- jaclang/plugin/tests/fixtures/other_root_access.jac +82 -0
- jaclang/plugin/tests/test_jaseci.py +414 -42
- jaclang/runtimelib/architype.py +481 -333
- jaclang/runtimelib/constructs.py +5 -8
- jaclang/runtimelib/context.py +89 -69
- jaclang/runtimelib/importer.py +16 -15
- jaclang/runtimelib/machine.py +66 -2
- jaclang/runtimelib/memory.py +134 -75
- jaclang/runtimelib/utils.py +17 -10
- jaclang/settings.py +2 -4
- jaclang/tests/fixtures/access_checker.jac +12 -17
- jaclang/tests/fixtures/access_modifier.jac +88 -33
- jaclang/tests/fixtures/baddy.jac +3 -0
- jaclang/tests/fixtures/bar.jac +34 -0
- jaclang/tests/fixtures/builtin_dotgen.jac +1 -0
- jaclang/tests/fixtures/edge_node_walk.jac +1 -1
- jaclang/tests/fixtures/edge_ops.jac +1 -1
- jaclang/tests/fixtures/edges_walk.jac +1 -1
- jaclang/tests/fixtures/foo.jac +43 -0
- jaclang/tests/fixtures/game1.jac +1 -1
- jaclang/tests/fixtures/gendot_bubble_sort.jac +1 -1
- jaclang/tests/fixtures/import.jac +9 -0
- jaclang/tests/fixtures/index_slice.jac +30 -0
- jaclang/tests/fixtures/objref.jac +12 -0
- jaclang/tests/fixtures/pyfunc_1.py +1 -1
- jaclang/tests/fixtures/pyfunc_2.py +2 -2
- jaclang/tests/fixtures/pygame_mock/__init__.py +3 -0
- jaclang/tests/fixtures/pygame_mock/color.py +3 -0
- jaclang/tests/fixtures/pygame_mock/constants.py +5 -0
- jaclang/tests/fixtures/pygame_mock/display.py +2 -0
- jaclang/tests/fixtures/pygame_mock/inner/__init__.py +0 -0
- jaclang/tests/fixtures/pygame_mock/inner/iner_mod.py +2 -0
- jaclang/tests/test_cli.py +49 -6
- jaclang/tests/test_language.py +126 -80
- jaclang/tests/test_reference.py +2 -9
- jaclang/utils/treeprinter.py +30 -3
- {jaclang-0.7.16.dist-info → jaclang-0.7.18.dist-info}/METADATA +3 -1
- {jaclang-0.7.16.dist-info → jaclang-0.7.18.dist-info}/RECORD +81 -59
- /jaclang/tests/fixtures/{err.test.jac → baddy.test.jac} +0 -0
- {jaclang-0.7.16.dist-info → jaclang-0.7.18.dist-info}/WHEEL +0 -0
- {jaclang-0.7.16.dist-info → jaclang-0.7.18.dist-info}/entry_points.txt +0 -0
jaclang/runtimelib/architype.py
CHANGED
|
@@ -2,76 +2,307 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
from
|
|
7
|
-
from
|
|
5
|
+
from dataclasses import asdict, dataclass, field, fields, is_dataclass
|
|
6
|
+
from enum import IntEnum
|
|
7
|
+
from logging import getLogger
|
|
8
|
+
from pickle import dumps
|
|
9
|
+
from types import UnionType
|
|
10
|
+
from typing import Any, Callable, ClassVar, Iterable, Optional, TypeVar
|
|
8
11
|
from uuid import UUID, uuid4
|
|
9
12
|
|
|
10
13
|
from jaclang.compiler.constant import EdgeDir
|
|
11
14
|
from jaclang.runtimelib.utils import collect_node_connections
|
|
12
15
|
|
|
16
|
+
logger = getLogger(__name__)
|
|
13
17
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"""Element Anchor."""
|
|
18
|
+
TARCH = TypeVar("TARCH", bound="Architype")
|
|
19
|
+
TANCH = TypeVar("TANCH", bound="Anchor")
|
|
17
20
|
|
|
18
|
-
obj: Architype
|
|
19
|
-
id: UUID = field(default_factory=uuid4)
|
|
20
21
|
|
|
22
|
+
class AccessLevel(IntEnum):
|
|
23
|
+
"""Access level enum."""
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
NO_ACCESS = -1
|
|
26
|
+
READ = 0
|
|
27
|
+
CONNECT = 1
|
|
28
|
+
WRITE = 2
|
|
25
29
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
30
|
+
@staticmethod
|
|
31
|
+
def cast(val: int | str | AccessLevel) -> AccessLevel:
|
|
32
|
+
"""Cast access level."""
|
|
33
|
+
match val:
|
|
34
|
+
case int():
|
|
35
|
+
return AccessLevel(val)
|
|
36
|
+
case str():
|
|
37
|
+
return AccessLevel[val]
|
|
38
|
+
case _:
|
|
39
|
+
return val
|
|
29
40
|
|
|
30
41
|
|
|
31
|
-
@dataclass
|
|
32
|
-
class
|
|
33
|
-
"""
|
|
42
|
+
@dataclass
|
|
43
|
+
class Access:
|
|
44
|
+
"""Access Structure."""
|
|
45
|
+
|
|
46
|
+
anchors: dict[str, AccessLevel] = field(default_factory=dict)
|
|
47
|
+
|
|
48
|
+
def check(self, anchor: str) -> AccessLevel:
|
|
49
|
+
"""Validate access."""
|
|
50
|
+
return self.anchors.get(anchor, AccessLevel.NO_ACCESS)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@dataclass
|
|
54
|
+
class Permission:
|
|
55
|
+
"""Anchor Access Handler."""
|
|
34
56
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
57
|
+
all: AccessLevel = AccessLevel.NO_ACCESS
|
|
58
|
+
roots: Access = field(default_factory=Access)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@dataclass
|
|
62
|
+
class AnchorReport:
|
|
63
|
+
"""Report Handler."""
|
|
64
|
+
|
|
65
|
+
id: str
|
|
66
|
+
context: dict[str, Any]
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@dataclass(eq=False, repr=False, kw_only=True)
|
|
70
|
+
class Anchor:
|
|
71
|
+
"""Object Anchor."""
|
|
72
|
+
|
|
73
|
+
architype: Architype
|
|
74
|
+
id: UUID = field(default_factory=uuid4)
|
|
75
|
+
root: Optional[UUID] = None
|
|
76
|
+
access: Permission = field(default_factory=Permission)
|
|
38
77
|
persistent: bool = False
|
|
78
|
+
hash: int = 0
|
|
39
79
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
state.pop("obj")
|
|
44
|
-
if self.edges and "edges" in state:
|
|
45
|
-
edges = state.pop("edges")
|
|
46
|
-
state["edge_ids"] = [e._jac_.id for e in edges]
|
|
80
|
+
##########################################################################
|
|
81
|
+
# ACCESS CONTROL: TODO: Make Base Type #
|
|
82
|
+
##########################################################################
|
|
47
83
|
|
|
48
|
-
|
|
84
|
+
def allow_root(
|
|
85
|
+
self, root_id: UUID, level: AccessLevel | int | str = AccessLevel.READ
|
|
86
|
+
) -> None:
|
|
87
|
+
"""Allow all access from target root graph to current Architype."""
|
|
88
|
+
level = AccessLevel.cast(level)
|
|
89
|
+
access = self.access.roots
|
|
49
90
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
91
|
+
_root_id = str(root_id)
|
|
92
|
+
if level != access.anchors.get(_root_id, AccessLevel.NO_ACCESS):
|
|
93
|
+
access.anchors[_root_id] = level
|
|
94
|
+
|
|
95
|
+
def disallow_root(
|
|
96
|
+
self, root_id: UUID, level: AccessLevel | int | str = AccessLevel.READ
|
|
97
|
+
) -> None:
|
|
98
|
+
"""Disallow all access from target root graph to current Architype."""
|
|
99
|
+
level = AccessLevel.cast(level)
|
|
100
|
+
access = self.access.roots
|
|
101
|
+
|
|
102
|
+
access.anchors.pop(str(root_id), None)
|
|
103
|
+
|
|
104
|
+
def unrestrict(self, level: AccessLevel | int | str = AccessLevel.READ) -> None:
|
|
105
|
+
"""Allow everyone to access current Architype."""
|
|
106
|
+
level = AccessLevel.cast(level)
|
|
107
|
+
if level != self.access.all:
|
|
108
|
+
self.access.all = level
|
|
109
|
+
|
|
110
|
+
def restrict(self) -> None:
|
|
111
|
+
"""Disallow others to access current Architype."""
|
|
112
|
+
if self.access.all > AccessLevel.NO_ACCESS:
|
|
113
|
+
self.access.all = AccessLevel.NO_ACCESS
|
|
114
|
+
|
|
115
|
+
def has_read_access(self, to: Anchor) -> bool:
|
|
116
|
+
"""Read Access Validation."""
|
|
117
|
+
if not (access_level := self.access_level(to) > AccessLevel.NO_ACCESS):
|
|
118
|
+
logger.info(
|
|
119
|
+
f"Current root doesn't have read access to {to.__class__.__name__}[{to.id}]"
|
|
120
|
+
)
|
|
121
|
+
return access_level
|
|
122
|
+
|
|
123
|
+
def has_connect_access(self, to: Anchor) -> bool:
|
|
124
|
+
"""Write Access Validation."""
|
|
125
|
+
if not (access_level := self.access_level(to) > AccessLevel.READ):
|
|
126
|
+
logger.info(
|
|
127
|
+
f"Current root doesn't have connect access to {to.__class__.__name__}[{to.id}]"
|
|
128
|
+
)
|
|
129
|
+
return access_level
|
|
130
|
+
|
|
131
|
+
def has_write_access(self, to: Anchor) -> bool:
|
|
132
|
+
"""Write Access Validation."""
|
|
133
|
+
if not (access_level := self.access_level(to) > AccessLevel.CONNECT):
|
|
134
|
+
logger.info(
|
|
135
|
+
f"Current root doesn't have write access to {to.__class__.__name__}[{to.id}]"
|
|
136
|
+
)
|
|
137
|
+
return access_level
|
|
138
|
+
|
|
139
|
+
def access_level(self, to: Anchor) -> AccessLevel:
|
|
140
|
+
"""Access validation."""
|
|
141
|
+
if not to.persistent:
|
|
142
|
+
return AccessLevel.WRITE
|
|
143
|
+
|
|
144
|
+
from jaclang.plugin.feature import JacFeature as Jac
|
|
145
|
+
|
|
146
|
+
jctx = Jac.get_context()
|
|
147
|
+
|
|
148
|
+
jroot = jctx.root
|
|
149
|
+
|
|
150
|
+
# if current root is system_root
|
|
151
|
+
# if current root id is equal to target anchor's root id
|
|
152
|
+
# if current root is the target anchor
|
|
153
|
+
if jroot == jctx.system_root or jroot.id == to.root or jroot == to:
|
|
154
|
+
return AccessLevel.WRITE
|
|
155
|
+
|
|
156
|
+
access_level = AccessLevel.NO_ACCESS
|
|
157
|
+
|
|
158
|
+
# if target anchor have set access.all
|
|
159
|
+
if (to_access := to.access).all > AccessLevel.NO_ACCESS:
|
|
160
|
+
access_level = to_access.all
|
|
161
|
+
|
|
162
|
+
# if target anchor's root have set allowed roots
|
|
163
|
+
# if current root is allowed to the whole graph of target anchor's root
|
|
164
|
+
if to.root and isinstance(to_root := jctx.mem.find_one(to.root), Anchor):
|
|
165
|
+
if to_root.access.all > access_level:
|
|
166
|
+
access_level = to_root.access.all
|
|
167
|
+
|
|
168
|
+
level = to_root.access.roots.check(str(jroot.id))
|
|
169
|
+
if level > AccessLevel.NO_ACCESS and access_level == AccessLevel.NO_ACCESS:
|
|
170
|
+
access_level = level
|
|
171
|
+
|
|
172
|
+
# if target anchor have set allowed roots
|
|
173
|
+
# if current root is allowed to target anchor
|
|
174
|
+
level = to_access.roots.check(str(jroot.id))
|
|
175
|
+
if level > AccessLevel.NO_ACCESS and access_level == AccessLevel.NO_ACCESS:
|
|
176
|
+
access_level = level
|
|
177
|
+
|
|
178
|
+
return access_level
|
|
179
|
+
|
|
180
|
+
# ---------------------------------------------------------------------- #
|
|
55
181
|
|
|
56
|
-
def
|
|
57
|
-
"""
|
|
182
|
+
def save(self) -> None:
|
|
183
|
+
"""Save Anchor."""
|
|
58
184
|
from jaclang.plugin.feature import JacFeature as Jac
|
|
59
185
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
186
|
+
jctx = Jac.get_context()
|
|
187
|
+
|
|
188
|
+
self.persistent = True
|
|
189
|
+
self.root = jctx.root.id
|
|
190
|
+
|
|
191
|
+
jctx.mem.set(self.id, self)
|
|
192
|
+
|
|
193
|
+
def destroy(self) -> None:
|
|
194
|
+
"""Destroy Anchor."""
|
|
195
|
+
from jaclang.plugin.feature import JacFeature as Jac
|
|
196
|
+
|
|
197
|
+
jctx = Jac.get_context()
|
|
198
|
+
|
|
199
|
+
if jctx.root.has_write_access(self):
|
|
200
|
+
jctx.mem.remove(self.id)
|
|
201
|
+
|
|
202
|
+
def is_populated(self) -> bool:
|
|
203
|
+
"""Check if state."""
|
|
204
|
+
return "architype" in self.__dict__
|
|
205
|
+
|
|
206
|
+
def make_stub(self: TANCH) -> TANCH:
|
|
207
|
+
"""Return unsynced copy of anchor."""
|
|
208
|
+
if self.is_populated():
|
|
209
|
+
unloaded = object.__new__(self.__class__)
|
|
210
|
+
unloaded.id = self.id
|
|
211
|
+
return unloaded
|
|
212
|
+
return self
|
|
213
|
+
|
|
214
|
+
def populate(self) -> None:
|
|
215
|
+
"""Retrieve the Architype from db and return."""
|
|
216
|
+
from jaclang.plugin.feature import JacFeature as Jac
|
|
217
|
+
|
|
218
|
+
jsrc = Jac.get_context().mem
|
|
219
|
+
|
|
220
|
+
if anchor := jsrc.find_by_id(self.id):
|
|
221
|
+
self.__dict__.update(anchor.__dict__)
|
|
222
|
+
|
|
223
|
+
def __getattr__(self, name: str) -> object:
|
|
224
|
+
"""Trigger load if detects unloaded state."""
|
|
225
|
+
if not self.is_populated():
|
|
226
|
+
self.populate()
|
|
227
|
+
|
|
228
|
+
if not self.is_populated():
|
|
229
|
+
raise ValueError(
|
|
230
|
+
f"{self.__class__.__name__} [{self.id}] is not a valid reference!"
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
return getattr(self, name)
|
|
234
|
+
|
|
235
|
+
raise AttributeError(
|
|
236
|
+
f"'{self.__class__.__name__}' object has not attribute '{name}'"
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
def __getstate__(self) -> dict[str, Any]: # NOTE: May be better type hinting
|
|
240
|
+
"""Serialize Anchor."""
|
|
241
|
+
if self.is_populated():
|
|
242
|
+
unlinked = object.__new__(self.architype.__class__)
|
|
243
|
+
unlinked.__dict__.update(self.architype.__dict__)
|
|
244
|
+
unlinked.__dict__.pop("__jac__", None)
|
|
245
|
+
|
|
246
|
+
return {
|
|
247
|
+
"id": self.id,
|
|
248
|
+
"architype": unlinked,
|
|
249
|
+
"root": self.root,
|
|
250
|
+
"access": self.access,
|
|
251
|
+
"persistent": self.persistent,
|
|
252
|
+
}
|
|
253
|
+
else:
|
|
254
|
+
return {"id": self.id}
|
|
255
|
+
|
|
256
|
+
def __setstate__(self, state: dict[str, Any]) -> None:
|
|
257
|
+
"""Deserialize Anchor."""
|
|
258
|
+
self.__dict__.update(state)
|
|
259
|
+
|
|
260
|
+
if self.is_populated() and self.architype:
|
|
261
|
+
self.architype.__jac__ = self
|
|
262
|
+
self.hash = hash(dumps(self))
|
|
263
|
+
|
|
264
|
+
def __repr__(self) -> str:
|
|
265
|
+
"""Override representation."""
|
|
266
|
+
if self.is_populated():
|
|
267
|
+
attrs = ""
|
|
268
|
+
for f in fields(self):
|
|
269
|
+
if f.name in self.__dict__:
|
|
270
|
+
attrs += f"{f.name}={self.__dict__[f.name]}, "
|
|
271
|
+
attrs = attrs[:-2]
|
|
272
|
+
else:
|
|
273
|
+
attrs = f"id={self.id}"
|
|
274
|
+
|
|
275
|
+
return f"{self.__class__.__name__}({attrs})"
|
|
276
|
+
|
|
277
|
+
def report(self) -> AnchorReport:
|
|
278
|
+
"""Report Anchor."""
|
|
279
|
+
return AnchorReport(
|
|
280
|
+
id=self.id.hex,
|
|
281
|
+
context=(
|
|
282
|
+
asdict(self.architype)
|
|
283
|
+
if is_dataclass(self.architype) and not isinstance(self.architype, type)
|
|
284
|
+
else {}
|
|
285
|
+
),
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
def __hash__(self) -> int:
|
|
289
|
+
"""Override hash for anchor."""
|
|
290
|
+
return hash(self.id)
|
|
291
|
+
|
|
292
|
+
def __eq__(self, other: object) -> bool:
|
|
293
|
+
"""Override equal implementation."""
|
|
294
|
+
if isinstance(other, Anchor):
|
|
295
|
+
return self.__class__ is other.__class__ and self.id == other.id
|
|
296
|
+
|
|
297
|
+
return False
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
@dataclass(eq=False, repr=False, kw_only=True)
|
|
301
|
+
class NodeAnchor(Anchor):
|
|
302
|
+
"""Node Anchor."""
|
|
303
|
+
|
|
304
|
+
architype: NodeArchitype
|
|
305
|
+
edges: list[EdgeAnchor]
|
|
75
306
|
|
|
76
307
|
def get_edges(
|
|
77
308
|
self,
|
|
@@ -80,27 +311,32 @@ class NodeAnchor(ObjectAnchor):
|
|
|
80
311
|
target_obj: Optional[list[NodeArchitype]],
|
|
81
312
|
) -> list[EdgeArchitype]:
|
|
82
313
|
"""Get edges connected to this node."""
|
|
83
|
-
|
|
314
|
+
from jaclang.plugin.feature import JacFeature as Jac
|
|
84
315
|
|
|
85
|
-
|
|
316
|
+
root = Jac.get_root().__jac__
|
|
86
317
|
ret_edges: list[EdgeArchitype] = []
|
|
87
|
-
|
|
88
|
-
for e in edge_list:
|
|
318
|
+
for anchor in self.edges:
|
|
89
319
|
if (
|
|
90
|
-
|
|
91
|
-
and
|
|
92
|
-
and (
|
|
320
|
+
(source := anchor.source)
|
|
321
|
+
and (target := anchor.target)
|
|
322
|
+
and (not filter_func or filter_func([anchor.architype]))
|
|
323
|
+
and source.architype
|
|
324
|
+
and target.architype
|
|
325
|
+
):
|
|
326
|
+
if (
|
|
93
327
|
dir in [EdgeDir.OUT, EdgeDir.ANY]
|
|
94
|
-
and self
|
|
95
|
-
and (not target_obj or
|
|
96
|
-
|
|
97
|
-
|
|
328
|
+
and self == source
|
|
329
|
+
and (not target_obj or target.architype in target_obj)
|
|
330
|
+
and root.has_read_access(target)
|
|
331
|
+
):
|
|
332
|
+
ret_edges.append(anchor.architype)
|
|
333
|
+
if (
|
|
98
334
|
dir in [EdgeDir.IN, EdgeDir.ANY]
|
|
99
|
-
and self
|
|
100
|
-
and (not target_obj or
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
335
|
+
and self == target
|
|
336
|
+
and (not target_obj or source.architype in target_obj)
|
|
337
|
+
and root.has_read_access(source)
|
|
338
|
+
):
|
|
339
|
+
ret_edges.append(anchor.architype)
|
|
104
340
|
return ret_edges
|
|
105
341
|
|
|
106
342
|
def edges_to_nodes(
|
|
@@ -110,27 +346,40 @@ class NodeAnchor(ObjectAnchor):
|
|
|
110
346
|
target_obj: Optional[list[NodeArchitype]],
|
|
111
347
|
) -> list[NodeArchitype]:
|
|
112
348
|
"""Get set of nodes connected to this node."""
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
349
|
+
from jaclang.plugin.feature import JacFeature as Jac
|
|
350
|
+
|
|
351
|
+
root = Jac.get_root().__jac__
|
|
352
|
+
ret_edges: list[NodeArchitype] = []
|
|
353
|
+
for anchor in self.edges:
|
|
354
|
+
if (
|
|
355
|
+
(source := anchor.source)
|
|
356
|
+
and (target := anchor.target)
|
|
357
|
+
and (not filter_func or filter_func([anchor.architype]))
|
|
358
|
+
and source.architype
|
|
359
|
+
and target.architype
|
|
360
|
+
):
|
|
121
361
|
if (
|
|
122
362
|
dir in [EdgeDir.OUT, EdgeDir.ANY]
|
|
123
|
-
and self
|
|
124
|
-
and (not target_obj or
|
|
363
|
+
and self == source
|
|
364
|
+
and (not target_obj or target.architype in target_obj)
|
|
365
|
+
and root.has_read_access(target)
|
|
125
366
|
):
|
|
126
|
-
|
|
367
|
+
ret_edges.append(target.architype)
|
|
127
368
|
if (
|
|
128
369
|
dir in [EdgeDir.IN, EdgeDir.ANY]
|
|
129
|
-
and self
|
|
130
|
-
and (not target_obj or
|
|
370
|
+
and self == target
|
|
371
|
+
and (not target_obj or source.architype in target_obj)
|
|
372
|
+
and root.has_read_access(source)
|
|
131
373
|
):
|
|
132
|
-
|
|
133
|
-
return
|
|
374
|
+
ret_edges.append(source.architype)
|
|
375
|
+
return ret_edges
|
|
376
|
+
|
|
377
|
+
def remove_edge(self, edge: EdgeAnchor) -> None:
|
|
378
|
+
"""Remove reference without checking sync status."""
|
|
379
|
+
for idx, ed in enumerate(self.edges):
|
|
380
|
+
if ed.id == edge.id:
|
|
381
|
+
self.edges.pop(idx)
|
|
382
|
+
break
|
|
134
383
|
|
|
135
384
|
def gen_dot(self, dot_file: Optional[str] = None) -> str:
|
|
136
385
|
"""Generate Dot file for visualizing nodes and edges."""
|
|
@@ -140,7 +389,7 @@ class NodeAnchor(ObjectAnchor):
|
|
|
140
389
|
|
|
141
390
|
collect_node_connections(self, visited_nodes, connections)
|
|
142
391
|
dot_content = 'digraph {\nnode [style="filled", shape="ellipse", fillcolor="invis", fontcolor="black"];\n'
|
|
143
|
-
for idx, i in enumerate([nodes_.
|
|
392
|
+
for idx, i in enumerate([nodes_.architype for nodes_ in visited_nodes]):
|
|
144
393
|
unique_node_id_dict[i] = (i.__class__.__name__, str(idx))
|
|
145
394
|
dot_content += f'{idx} [label="{i}"];\n'
|
|
146
395
|
dot_content += 'edge [color="gray", style="solid"];\n'
|
|
@@ -155,130 +404,115 @@ class NodeAnchor(ObjectAnchor):
|
|
|
155
404
|
f.write(dot_content + "}")
|
|
156
405
|
return dot_content + "}"
|
|
157
406
|
|
|
407
|
+
def spawn_call(self, walk: WalkerAnchor) -> WalkerArchitype:
|
|
408
|
+
"""Invoke data spatial call."""
|
|
409
|
+
return walk.spawn_call(self)
|
|
158
410
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
411
|
+
def destroy(self) -> None:
|
|
412
|
+
"""Destroy Anchor."""
|
|
413
|
+
from jaclang.plugin.feature import JacFeature as Jac
|
|
162
414
|
|
|
163
|
-
|
|
164
|
-
source: Optional[NodeArchitype] = None
|
|
165
|
-
target: Optional[NodeArchitype] = None
|
|
166
|
-
source_id: Optional[UUID] = None
|
|
167
|
-
target_id: Optional[UUID] = None
|
|
168
|
-
is_undirected: bool = False
|
|
169
|
-
persistent: bool = False
|
|
415
|
+
jctx = Jac.get_context()
|
|
170
416
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
state.pop("obj")
|
|
417
|
+
if jctx.root.has_write_access(self):
|
|
418
|
+
for edge in self.edges:
|
|
419
|
+
edge.destroy()
|
|
175
420
|
|
|
176
|
-
|
|
177
|
-
state["source_id"] = self.source._jac_.id
|
|
178
|
-
state.pop("source")
|
|
421
|
+
jctx.mem.remove(self.id)
|
|
179
422
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
423
|
+
def __getstate__(self) -> dict[str, object]:
|
|
424
|
+
"""Serialize Node Anchor."""
|
|
425
|
+
state = super().__getstate__()
|
|
426
|
+
|
|
427
|
+
if self.is_populated():
|
|
428
|
+
state["edges"] = [edge.make_stub() for edge in self.edges]
|
|
183
429
|
|
|
184
430
|
return state
|
|
185
431
|
|
|
186
|
-
def __setstate__(self, state: dict) -> None:
|
|
187
|
-
"""Override setstate for pickle and shelve."""
|
|
188
|
-
self.__dict__.update(state)
|
|
189
432
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
"""Attach edge to nodes."""
|
|
194
|
-
self.source = src
|
|
195
|
-
self.target = trg
|
|
196
|
-
self.is_undirected = is_undirected
|
|
197
|
-
src._jac_.edges.append(self.obj)
|
|
198
|
-
trg._jac_.edges.append(self.obj)
|
|
199
|
-
return self
|
|
433
|
+
@dataclass(eq=False, repr=False, kw_only=True)
|
|
434
|
+
class EdgeAnchor(Anchor):
|
|
435
|
+
"""Edge Anchor."""
|
|
200
436
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
437
|
+
architype: EdgeArchitype
|
|
438
|
+
source: NodeAnchor
|
|
439
|
+
target: NodeAnchor
|
|
440
|
+
is_undirected: bool
|
|
441
|
+
|
|
442
|
+
def __post_init__(self) -> None:
|
|
443
|
+
"""Populate edge to source and target."""
|
|
444
|
+
self.source.edges.append(self)
|
|
445
|
+
self.target.edges.append(self)
|
|
446
|
+
|
|
447
|
+
def detach(self) -> None:
|
|
204
448
|
"""Detach edge from nodes."""
|
|
205
|
-
self.
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
self.target = None
|
|
210
|
-
del self
|
|
211
|
-
|
|
212
|
-
def spawn_call(self, walk: WalkerArchitype) -> WalkerArchitype:
|
|
449
|
+
self.source.remove_edge(self)
|
|
450
|
+
self.target.remove_edge(self)
|
|
451
|
+
|
|
452
|
+
def spawn_call(self, walk: WalkerAnchor) -> WalkerArchitype:
|
|
213
453
|
"""Invoke data spatial call."""
|
|
214
|
-
|
|
215
|
-
return walk._jac_.spawn_call(self.target)
|
|
216
|
-
else:
|
|
217
|
-
raise ValueError("Edge has no target.")
|
|
454
|
+
return walk.spawn_call(self.target)
|
|
218
455
|
|
|
456
|
+
def destroy(self) -> None:
|
|
457
|
+
"""Destroy Anchor."""
|
|
458
|
+
from jaclang.plugin.feature import JacFeature as Jac
|
|
219
459
|
|
|
220
|
-
|
|
221
|
-
|
|
460
|
+
jctx = Jac.get_context()
|
|
461
|
+
|
|
462
|
+
if jctx.root.has_write_access(self):
|
|
463
|
+
self.detach()
|
|
464
|
+
jctx.mem.remove(self.id)
|
|
465
|
+
|
|
466
|
+
def __getstate__(self) -> dict[str, object]:
|
|
467
|
+
"""Serialize Node Anchor."""
|
|
468
|
+
state = super().__getstate__()
|
|
469
|
+
|
|
470
|
+
if self.is_populated():
|
|
471
|
+
state.update(
|
|
472
|
+
{
|
|
473
|
+
"source": self.source.make_stub(),
|
|
474
|
+
"target": self.target.make_stub(),
|
|
475
|
+
"is_undirected": self.is_undirected,
|
|
476
|
+
}
|
|
477
|
+
)
|
|
478
|
+
|
|
479
|
+
return state
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
@dataclass(eq=False, repr=False, kw_only=True)
|
|
483
|
+
class WalkerAnchor(Anchor):
|
|
222
484
|
"""Walker Anchor."""
|
|
223
485
|
|
|
224
|
-
|
|
225
|
-
path: list[
|
|
226
|
-
next: list[
|
|
227
|
-
ignores: list[
|
|
486
|
+
architype: WalkerArchitype
|
|
487
|
+
path: list[Anchor] = field(default_factory=list)
|
|
488
|
+
next: list[Anchor] = field(default_factory=list)
|
|
489
|
+
ignores: list[Anchor] = field(default_factory=list)
|
|
228
490
|
disengaged: bool = False
|
|
229
491
|
|
|
230
|
-
def visit_node(
|
|
231
|
-
self,
|
|
232
|
-
nds: (
|
|
233
|
-
list[NodeArchitype | EdgeArchitype]
|
|
234
|
-
| list[NodeArchitype]
|
|
235
|
-
| list[EdgeArchitype]
|
|
236
|
-
| NodeArchitype
|
|
237
|
-
| EdgeArchitype
|
|
238
|
-
),
|
|
239
|
-
) -> bool:
|
|
492
|
+
def visit_node(self, anchors: Iterable[NodeAnchor | EdgeAnchor]) -> bool:
|
|
240
493
|
"""Walker visits node."""
|
|
241
|
-
nd_list: list[NodeArchitype | EdgeArchitype]
|
|
242
|
-
if not isinstance(nds, list):
|
|
243
|
-
nd_list = [nds]
|
|
244
|
-
else:
|
|
245
|
-
nd_list = list(nds)
|
|
246
494
|
before_len = len(self.next)
|
|
247
|
-
for
|
|
248
|
-
if
|
|
249
|
-
if isinstance(
|
|
250
|
-
self.next.append(
|
|
251
|
-
elif isinstance(
|
|
252
|
-
if
|
|
253
|
-
self.next.append(
|
|
495
|
+
for anchor in anchors:
|
|
496
|
+
if anchor not in self.ignores:
|
|
497
|
+
if isinstance(anchor, NodeAnchor):
|
|
498
|
+
self.next.append(anchor)
|
|
499
|
+
elif isinstance(anchor, EdgeAnchor):
|
|
500
|
+
if target := anchor.target:
|
|
501
|
+
self.next.append(target)
|
|
254
502
|
else:
|
|
255
503
|
raise ValueError("Edge has no target.")
|
|
256
504
|
return len(self.next) > before_len
|
|
257
505
|
|
|
258
|
-
def ignore_node(
|
|
259
|
-
self,
|
|
260
|
-
nds: (
|
|
261
|
-
list[NodeArchitype | EdgeArchitype]
|
|
262
|
-
| list[NodeArchitype]
|
|
263
|
-
| list[EdgeArchitype]
|
|
264
|
-
| NodeArchitype
|
|
265
|
-
| EdgeArchitype
|
|
266
|
-
),
|
|
267
|
-
) -> bool:
|
|
506
|
+
def ignore_node(self, anchors: Iterable[NodeAnchor | EdgeAnchor]) -> bool:
|
|
268
507
|
"""Walker ignores node."""
|
|
269
|
-
nd_list: list[NodeArchitype | EdgeArchitype]
|
|
270
|
-
if not isinstance(nds, list):
|
|
271
|
-
nd_list = [nds]
|
|
272
|
-
else:
|
|
273
|
-
nd_list = list(nds)
|
|
274
508
|
before_len = len(self.ignores)
|
|
275
|
-
for
|
|
276
|
-
if
|
|
277
|
-
if isinstance(
|
|
278
|
-
self.ignores.append(
|
|
279
|
-
elif isinstance(
|
|
280
|
-
if
|
|
281
|
-
self.ignores.append(
|
|
509
|
+
for anchor in anchors:
|
|
510
|
+
if anchor not in self.ignores:
|
|
511
|
+
if isinstance(anchor, NodeAnchor):
|
|
512
|
+
self.ignores.append(anchor)
|
|
513
|
+
elif isinstance(anchor, EdgeAnchor):
|
|
514
|
+
if target := anchor.target:
|
|
515
|
+
self.ignores.append(target)
|
|
282
516
|
else:
|
|
283
517
|
raise ValueError("Edge has no target.")
|
|
284
518
|
return len(self.ignores) > before_len
|
|
@@ -287,206 +521,120 @@ class WalkerAnchor(ObjectAnchor):
|
|
|
287
521
|
"""Disengage walker from traversal."""
|
|
288
522
|
self.disengaged = True
|
|
289
523
|
|
|
290
|
-
def spawn_call(self,
|
|
524
|
+
def spawn_call(self, node: Anchor) -> WalkerArchitype:
|
|
291
525
|
"""Invoke data spatial call."""
|
|
292
|
-
self.
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
526
|
+
if walker := self.architype:
|
|
527
|
+
self.path = []
|
|
528
|
+
self.next = [node]
|
|
529
|
+
while len(self.next):
|
|
530
|
+
if current_node := self.next.pop(0).architype:
|
|
531
|
+
for i in current_node._jac_entry_funcs_:
|
|
532
|
+
if not i.trigger or isinstance(walker, i.trigger):
|
|
533
|
+
if i.func:
|
|
534
|
+
i.func(current_node, walker)
|
|
535
|
+
else:
|
|
536
|
+
raise ValueError(f"No function {i.name} to call.")
|
|
537
|
+
if self.disengaged:
|
|
538
|
+
return walker
|
|
539
|
+
for i in walker._jac_entry_funcs_:
|
|
540
|
+
if not i.trigger or isinstance(current_node, i.trigger):
|
|
541
|
+
if i.func:
|
|
542
|
+
i.func(walker, current_node)
|
|
543
|
+
else:
|
|
544
|
+
raise ValueError(f"No function {i.name} to call.")
|
|
545
|
+
if self.disengaged:
|
|
546
|
+
return walker
|
|
547
|
+
for i in walker._jac_exit_funcs_:
|
|
548
|
+
if not i.trigger or isinstance(current_node, i.trigger):
|
|
549
|
+
if i.func:
|
|
550
|
+
i.func(walker, current_node)
|
|
551
|
+
else:
|
|
552
|
+
raise ValueError(f"No function {i.name} to call.")
|
|
553
|
+
if self.disengaged:
|
|
554
|
+
return walker
|
|
555
|
+
for i in current_node._jac_exit_funcs_:
|
|
556
|
+
if not i.trigger or isinstance(walker, i.trigger):
|
|
557
|
+
if i.func:
|
|
558
|
+
i.func(current_node, walker)
|
|
559
|
+
else:
|
|
560
|
+
raise ValueError(f"No function {i.name} to call.")
|
|
561
|
+
if self.disengaged:
|
|
562
|
+
return walker
|
|
563
|
+
self.ignores = []
|
|
564
|
+
return walker
|
|
565
|
+
raise Exception(f"Invalid Reference {self.id}")
|
|
330
566
|
|
|
331
567
|
|
|
332
568
|
class Architype:
|
|
333
569
|
"""Architype Protocol."""
|
|
334
570
|
|
|
335
|
-
_jac_entry_funcs_: list[DSFunc]
|
|
336
|
-
_jac_exit_funcs_: list[DSFunc]
|
|
571
|
+
_jac_entry_funcs_: ClassVar[list[DSFunc]]
|
|
572
|
+
_jac_exit_funcs_: ClassVar[list[DSFunc]]
|
|
337
573
|
|
|
338
574
|
def __init__(self) -> None:
|
|
339
575
|
"""Create default architype."""
|
|
340
|
-
self.
|
|
341
|
-
|
|
342
|
-
def __hash__(self) -> int:
|
|
343
|
-
"""Override hash for architype."""
|
|
344
|
-
return hash(self._jac_.id)
|
|
345
|
-
|
|
346
|
-
def __eq__(self, other: object) -> bool:
|
|
347
|
-
"""Override equality for architype."""
|
|
348
|
-
if not isinstance(other, Architype):
|
|
349
|
-
return False
|
|
350
|
-
else:
|
|
351
|
-
return self._jac_.id == other._jac_.id
|
|
576
|
+
self.__jac__ = Anchor(architype=self)
|
|
352
577
|
|
|
353
578
|
def __repr__(self) -> str:
|
|
354
579
|
"""Override repr for architype."""
|
|
355
580
|
return f"{self.__class__.__name__}"
|
|
356
581
|
|
|
357
|
-
def __getstate__(self) -> dict:
|
|
358
|
-
"""Override getstate for pickle and shelve."""
|
|
359
|
-
raise NotImplementedError
|
|
360
|
-
|
|
361
582
|
|
|
362
583
|
class NodeArchitype(Architype):
|
|
363
584
|
"""Node Architype Protocol."""
|
|
364
585
|
|
|
365
|
-
|
|
586
|
+
__jac__: NodeAnchor
|
|
366
587
|
|
|
367
588
|
def __init__(self) -> None:
|
|
368
589
|
"""Create node architype."""
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
self._jac_: NodeAnchor = NodeAnchor(obj=self)
|
|
372
|
-
Jac.context().save_obj(self, persistent=self._jac_.persistent)
|
|
373
|
-
|
|
374
|
-
def save(self) -> None:
|
|
375
|
-
"""Save the node to the memory/storage hierarchy."""
|
|
376
|
-
from jaclang.plugin.feature import JacFeature as Jac
|
|
377
|
-
|
|
378
|
-
self._jac_.persistent = True
|
|
379
|
-
Jac.context().save_obj(self, persistent=True)
|
|
380
|
-
|
|
381
|
-
def __getstate__(self) -> dict:
|
|
382
|
-
"""Override getstate for pickle and shelve."""
|
|
383
|
-
state = self.__dict__.copy()
|
|
384
|
-
state["_jac_"] = self._jac_.__getstate__()
|
|
385
|
-
return state
|
|
386
|
-
|
|
387
|
-
def __setstate__(self, state: dict) -> None:
|
|
388
|
-
"""Override setstate for pickle and shelve."""
|
|
389
|
-
self.__dict__.update(state)
|
|
390
|
-
self._jac_ = NodeAnchor(obj=self)
|
|
391
|
-
self._jac_.__setstate__(state["_jac_"])
|
|
590
|
+
self.__jac__ = NodeAnchor(architype=self, edges=[])
|
|
392
591
|
|
|
393
592
|
|
|
394
593
|
class EdgeArchitype(Architype):
|
|
395
594
|
"""Edge Architype Protocol."""
|
|
396
595
|
|
|
397
|
-
|
|
398
|
-
persistent: bool = False
|
|
399
|
-
|
|
400
|
-
def __init__(self) -> None:
|
|
401
|
-
"""Create edge architype."""
|
|
402
|
-
from jaclang.plugin.feature import JacFeature as Jac
|
|
403
|
-
|
|
404
|
-
self._jac_: EdgeAnchor = EdgeAnchor(obj=self)
|
|
405
|
-
Jac.context().save_obj(self, persistent=self.persistent)
|
|
406
|
-
|
|
407
|
-
def save(self) -> None:
|
|
408
|
-
"""Save the edge to the memory/storage hierarchy."""
|
|
409
|
-
from jaclang.plugin.feature import JacFeature as Jac
|
|
410
|
-
|
|
411
|
-
self.persistent = True
|
|
412
|
-
Jac.context().save_obj(self, persistent=True)
|
|
596
|
+
__jac__: EdgeAnchor
|
|
413
597
|
|
|
414
|
-
def
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
self._jac_.__setstate__(state["_jac_"])
|
|
425
|
-
|
|
426
|
-
def populate_nodes(self) -> None:
|
|
427
|
-
"""Populate nodes for the edges from node ids."""
|
|
428
|
-
from jaclang.plugin.feature import JacFeature as Jac
|
|
429
|
-
|
|
430
|
-
if self._jac_.source_id:
|
|
431
|
-
obj = Jac.context().get_obj(self._jac_.source_id)
|
|
432
|
-
if obj is None:
|
|
433
|
-
raise ValueError(f"Node with id {self._jac_.source_id} not found.")
|
|
434
|
-
elif not isinstance(obj, NodeArchitype):
|
|
435
|
-
raise ValueError(
|
|
436
|
-
f"Object with id {self._jac_.source_id} is not a node."
|
|
437
|
-
)
|
|
438
|
-
else:
|
|
439
|
-
self._jac_.source = obj
|
|
440
|
-
self._jac_.source_id = None
|
|
441
|
-
if self._jac_.target_id:
|
|
442
|
-
obj = Jac.context().get_obj(self._jac_.target_id)
|
|
443
|
-
if obj is None:
|
|
444
|
-
raise ValueError(f"Node with id {self._jac_.target_id} not found.")
|
|
445
|
-
elif not isinstance(obj, NodeArchitype):
|
|
446
|
-
raise ValueError(
|
|
447
|
-
f"Object with id {self._jac_.target_id} is not a node."
|
|
448
|
-
)
|
|
449
|
-
else:
|
|
450
|
-
self._jac_.target = obj
|
|
451
|
-
self._jac_.target_id = None
|
|
598
|
+
def __attach__(
|
|
599
|
+
self,
|
|
600
|
+
source: NodeAnchor,
|
|
601
|
+
target: NodeAnchor,
|
|
602
|
+
is_undirected: bool,
|
|
603
|
+
) -> None:
|
|
604
|
+
"""Attach EdgeAnchor properly."""
|
|
605
|
+
self.__jac__ = EdgeAnchor(
|
|
606
|
+
architype=self, source=source, target=target, is_undirected=is_undirected
|
|
607
|
+
)
|
|
452
608
|
|
|
453
609
|
|
|
454
610
|
class WalkerArchitype(Architype):
|
|
455
611
|
"""Walker Architype Protocol."""
|
|
456
612
|
|
|
457
|
-
|
|
613
|
+
__jac__: WalkerAnchor
|
|
458
614
|
|
|
459
615
|
def __init__(self) -> None:
|
|
460
616
|
"""Create walker architype."""
|
|
461
|
-
self.
|
|
617
|
+
self.__jac__ = WalkerAnchor(architype=self)
|
|
462
618
|
|
|
463
619
|
|
|
620
|
+
@dataclass(eq=False)
|
|
464
621
|
class GenericEdge(EdgeArchitype):
|
|
465
622
|
"""Generic Root Node."""
|
|
466
623
|
|
|
467
|
-
_jac_entry_funcs_ = []
|
|
468
|
-
_jac_exit_funcs_ = []
|
|
624
|
+
_jac_entry_funcs_: ClassVar[list[DSFunc]] = []
|
|
625
|
+
_jac_exit_funcs_: ClassVar[list[DSFunc]] = []
|
|
469
626
|
|
|
470
627
|
|
|
628
|
+
@dataclass(eq=False)
|
|
471
629
|
class Root(NodeArchitype):
|
|
472
630
|
"""Generic Root Node."""
|
|
473
631
|
|
|
474
|
-
_jac_entry_funcs_ = []
|
|
475
|
-
_jac_exit_funcs_ = []
|
|
476
|
-
reachable_nodes: list[NodeArchitype] = []
|
|
477
|
-
connections: set[tuple[NodeArchitype, NodeArchitype, EdgeArchitype]] = set()
|
|
632
|
+
_jac_entry_funcs_: ClassVar[list[DSFunc]] = []
|
|
633
|
+
_jac_exit_funcs_: ClassVar[list[DSFunc]] = []
|
|
478
634
|
|
|
479
635
|
def __init__(self) -> None:
|
|
480
636
|
"""Create root node."""
|
|
481
|
-
|
|
482
|
-
self._jac_.id = UUID(int=0)
|
|
483
|
-
self._jac_.persistent = True
|
|
484
|
-
|
|
485
|
-
def reset(self) -> None:
|
|
486
|
-
"""Reset the root."""
|
|
487
|
-
self.reachable_nodes = []
|
|
488
|
-
self.connections = set()
|
|
489
|
-
self._jac_.edges = []
|
|
637
|
+
self.__jac__ = NodeAnchor(architype=self, persistent=True, edges=[])
|
|
490
638
|
|
|
491
639
|
|
|
492
640
|
@dataclass(eq=False)
|
|
@@ -494,7 +642,7 @@ class DSFunc:
|
|
|
494
642
|
"""Data Spatial Function."""
|
|
495
643
|
|
|
496
644
|
name: str
|
|
497
|
-
trigger: type |
|
|
645
|
+
trigger: type | UnionType | tuple[type | UnionType, ...] | None
|
|
498
646
|
func: Callable[[Any, Any], Any] | None = None
|
|
499
647
|
|
|
500
648
|
def resolve(self, cls: type) -> None:
|