jaclang 0.7.16__py3-none-any.whl → 0.7.17__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 -75
- 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 +21 -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 +11 -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 +39 -0
- jaclang/langserve/server.py +16 -1
- jaclang/langserve/tests/fixtures/rename.jac +30 -0
- jaclang/langserve/tests/test_server.py +216 -2
- jaclang/langserve/utils.py +21 -2
- jaclang/plugin/default.py +78 -70
- jaclang/plugin/feature.py +7 -17
- jaclang/plugin/spec.py +6 -19
- 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 +15 -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/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/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 +113 -78
- jaclang/tests/test_reference.py +2 -9
- jaclang/utils/treeprinter.py +30 -3
- {jaclang-0.7.16.dist-info → jaclang-0.7.17.dist-info}/METADATA +1 -1
- {jaclang-0.7.16.dist-info → jaclang-0.7.17.dist-info}/RECORD +77 -56
- /jaclang/tests/fixtures/{err.test.jac → baddy.test.jac} +0 -0
- {jaclang-0.7.16.dist-info → jaclang-0.7.17.dist-info}/WHEEL +0 -0
- {jaclang-0.7.16.dist-info → jaclang-0.7.17.dist-info}/entry_points.txt +0 -0
jaclang/runtimelib/constructs.py
CHANGED
|
@@ -4,26 +4,24 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
from .architype import (
|
|
7
|
+
Anchor,
|
|
7
8
|
Architype,
|
|
8
9
|
DSFunc,
|
|
9
10
|
EdgeAnchor,
|
|
10
11
|
EdgeArchitype,
|
|
11
|
-
ElementAnchor,
|
|
12
12
|
GenericEdge,
|
|
13
13
|
NodeAnchor,
|
|
14
14
|
NodeArchitype,
|
|
15
|
-
ObjectAnchor,
|
|
16
15
|
Root,
|
|
17
16
|
WalkerAnchor,
|
|
18
17
|
WalkerArchitype,
|
|
19
18
|
)
|
|
20
|
-
from .context import ExecutionContext
|
|
21
|
-
from .memory import Memory,
|
|
19
|
+
from .context import ExecutionContext
|
|
20
|
+
from .memory import Memory, ShelfStorage
|
|
22
21
|
from .test import JacTestCheck, JacTestResult, JacTextTestRunner
|
|
23
22
|
|
|
24
23
|
__all__ = [
|
|
25
|
-
"
|
|
26
|
-
"ObjectAnchor",
|
|
24
|
+
"Anchor",
|
|
27
25
|
"NodeAnchor",
|
|
28
26
|
"EdgeAnchor",
|
|
29
27
|
"WalkerAnchor",
|
|
@@ -35,9 +33,8 @@ __all__ = [
|
|
|
35
33
|
"Root",
|
|
36
34
|
"DSFunc",
|
|
37
35
|
"Memory",
|
|
38
|
-
"
|
|
36
|
+
"ShelfStorage",
|
|
39
37
|
"ExecutionContext",
|
|
40
|
-
"exec_context",
|
|
41
38
|
"JacTestResult",
|
|
42
39
|
"JacTextTestRunner",
|
|
43
40
|
"JacTestCheck",
|
jaclang/runtimelib/context.py
CHANGED
|
@@ -4,80 +4,100 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import unittest
|
|
6
6
|
from contextvars import ContextVar
|
|
7
|
-
from typing import Callable, Optional
|
|
7
|
+
from typing import Any, Callable, Optional, cast
|
|
8
8
|
from uuid import UUID
|
|
9
9
|
|
|
10
|
-
from .architype import
|
|
11
|
-
from .
|
|
12
|
-
from .memory import Memory, ShelveStorage
|
|
10
|
+
from .architype import NodeAnchor, Root
|
|
11
|
+
from .memory import Memory, ShelfStorage
|
|
13
12
|
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def __init__(self) -> None:
|
|
22
|
-
"""Create execution context."""
|
|
23
|
-
super().__init__()
|
|
24
|
-
self.mem = ShelveStorage()
|
|
25
|
-
self.root = None
|
|
26
|
-
self.jac_machine = JacMachine()
|
|
27
|
-
jac_program = JacProgram(mod_bundle=None, bytecode=None)
|
|
28
|
-
self.jac_machine.attach_program(jac_program)
|
|
29
|
-
|
|
30
|
-
def init_memory(self, base_path: str = "", session: str = "") -> None:
|
|
31
|
-
"""Initialize memory."""
|
|
32
|
-
if session:
|
|
33
|
-
self.mem = ShelveStorage(session)
|
|
34
|
-
else:
|
|
35
|
-
self.mem = Memory()
|
|
36
|
-
self.jac_machine = JacMachine(base_path)
|
|
37
|
-
jac_program = JacProgram(mod_bundle=None, bytecode=None)
|
|
38
|
-
self.jac_machine.attach_program(jac_program)
|
|
39
|
-
|
|
40
|
-
def get_root(self) -> Root:
|
|
41
|
-
"""Get the root object."""
|
|
42
|
-
if self.mem is None:
|
|
43
|
-
raise ValueError("Memory not initialized")
|
|
44
|
-
|
|
45
|
-
if not self.root:
|
|
46
|
-
root = self.mem.get_obj(UUID(int=0))
|
|
47
|
-
if root is None:
|
|
48
|
-
self.root = Root()
|
|
49
|
-
self.mem.save_obj(self.root, persistent=self.root._jac_.persistent)
|
|
50
|
-
elif not isinstance(root, Root):
|
|
51
|
-
raise ValueError(f"Invalid root object: {root}")
|
|
52
|
-
else:
|
|
53
|
-
self.root = root
|
|
54
|
-
return self.root
|
|
55
|
-
|
|
56
|
-
def get_obj(self, obj_id: UUID) -> Architype | None:
|
|
57
|
-
"""Get object from memory."""
|
|
58
|
-
if self.mem is None:
|
|
59
|
-
raise ValueError("Memory not initialized")
|
|
60
|
-
|
|
61
|
-
return self.mem.get_obj(obj_id)
|
|
62
|
-
|
|
63
|
-
def save_obj(self, item: Architype, persistent: bool) -> None:
|
|
64
|
-
"""Save object to memory."""
|
|
65
|
-
if self.mem is None:
|
|
66
|
-
raise ValueError("Memory not initialized")
|
|
67
|
-
|
|
68
|
-
self.mem.save_obj(item, persistent)
|
|
69
|
-
|
|
70
|
-
def reset(self) -> None:
|
|
71
|
-
"""Reset the execution context."""
|
|
72
|
-
if self.mem:
|
|
73
|
-
self.mem.close()
|
|
74
|
-
self.mem = None
|
|
75
|
-
self.root = None
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
exec_context: ContextVar[ExecutionContext | None] = ContextVar(
|
|
79
|
-
"ExecutionContext", default=None
|
|
14
|
+
EXECUTION_CONTEXT = ContextVar[Optional["ExecutionContext"]]("ExecutionContext")
|
|
15
|
+
|
|
16
|
+
SUPER_ROOT_UUID = UUID("00000000-0000-0000-0000-000000000000")
|
|
17
|
+
SUPER_ROOT_ARCHITYPE = object.__new__(Root)
|
|
18
|
+
SUPER_ROOT_ANCHOR = NodeAnchor(
|
|
19
|
+
id=SUPER_ROOT_UUID, architype=SUPER_ROOT_ARCHITYPE, persistent=False, edges=[]
|
|
80
20
|
)
|
|
21
|
+
SUPER_ROOT_ARCHITYPE.__jac__ = SUPER_ROOT_ANCHOR
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ExecutionContext:
|
|
25
|
+
"""Execution Context."""
|
|
26
|
+
|
|
27
|
+
mem: Memory
|
|
28
|
+
reports: list[Any]
|
|
29
|
+
system_root: NodeAnchor
|
|
30
|
+
root: NodeAnchor
|
|
31
|
+
entry_node: NodeAnchor
|
|
32
|
+
|
|
33
|
+
def init_anchor(
|
|
34
|
+
self,
|
|
35
|
+
anchor_id: str | None,
|
|
36
|
+
default: NodeAnchor,
|
|
37
|
+
) -> NodeAnchor:
|
|
38
|
+
"""Load initial anchors."""
|
|
39
|
+
if anchor_id:
|
|
40
|
+
if isinstance(anchor := self.mem.find_by_id(UUID(anchor_id)), NodeAnchor):
|
|
41
|
+
return anchor
|
|
42
|
+
raise ValueError(f"Invalid anchor id {anchor_id} !")
|
|
43
|
+
return default
|
|
44
|
+
|
|
45
|
+
def validate_access(self) -> bool:
|
|
46
|
+
"""Validate access."""
|
|
47
|
+
return self.root.has_read_access(self.entry_node)
|
|
48
|
+
|
|
49
|
+
def set_entry_node(self, entry_node: str | None) -> None:
|
|
50
|
+
"""Override entry."""
|
|
51
|
+
self.entry_node = self.init_anchor(entry_node, self.root)
|
|
52
|
+
|
|
53
|
+
def close(self) -> None:
|
|
54
|
+
"""Close current ExecutionContext."""
|
|
55
|
+
self.mem.close()
|
|
56
|
+
EXECUTION_CONTEXT.set(None)
|
|
57
|
+
|
|
58
|
+
@staticmethod
|
|
59
|
+
def create(
|
|
60
|
+
session: Optional[str] = None,
|
|
61
|
+
root: Optional[str] = None,
|
|
62
|
+
auto_close: bool = True,
|
|
63
|
+
) -> ExecutionContext:
|
|
64
|
+
"""Create ExecutionContext."""
|
|
65
|
+
ctx = ExecutionContext()
|
|
66
|
+
ctx.mem = ShelfStorage(session)
|
|
67
|
+
ctx.reports = []
|
|
68
|
+
|
|
69
|
+
if not isinstance(
|
|
70
|
+
system_root := ctx.mem.find_by_id(SUPER_ROOT_UUID), NodeAnchor
|
|
71
|
+
):
|
|
72
|
+
system_root = Root().__jac__
|
|
73
|
+
system_root.id = SUPER_ROOT_UUID
|
|
74
|
+
ctx.mem.set(system_root.id, system_root)
|
|
75
|
+
|
|
76
|
+
ctx.system_root = system_root
|
|
77
|
+
|
|
78
|
+
ctx.entry_node = ctx.root = ctx.init_anchor(root, ctx.system_root)
|
|
79
|
+
|
|
80
|
+
if auto_close and (old_ctx := EXECUTION_CONTEXT.get(None)):
|
|
81
|
+
old_ctx.close()
|
|
82
|
+
|
|
83
|
+
EXECUTION_CONTEXT.set(ctx)
|
|
84
|
+
|
|
85
|
+
return ctx
|
|
86
|
+
|
|
87
|
+
@staticmethod
|
|
88
|
+
def get() -> ExecutionContext:
|
|
89
|
+
"""Get current ExecutionContext."""
|
|
90
|
+
if ctx := EXECUTION_CONTEXT.get(None):
|
|
91
|
+
return ctx
|
|
92
|
+
raise Exception("ExecutionContext is not yet available!")
|
|
93
|
+
|
|
94
|
+
@staticmethod
|
|
95
|
+
def get_root() -> Root:
|
|
96
|
+
"""Get current root."""
|
|
97
|
+
if ctx := EXECUTION_CONTEXT.get(None):
|
|
98
|
+
return cast(Root, ctx.root.architype)
|
|
99
|
+
|
|
100
|
+
return SUPER_ROOT_ARCHITYPE
|
|
81
101
|
|
|
82
102
|
|
|
83
103
|
class JacTestResult(unittest.TextTestResult):
|
jaclang/runtimelib/importer.py
CHANGED
|
@@ -139,7 +139,7 @@ class ImportReturn:
|
|
|
139
139
|
else module.__name__
|
|
140
140
|
)
|
|
141
141
|
if isinstance(self.importer, JacImporter):
|
|
142
|
-
new_module =
|
|
142
|
+
new_module = self.importer.jac_machine.loaded_modules.get(
|
|
143
143
|
package_name,
|
|
144
144
|
self.importer.create_jac_py_module(
|
|
145
145
|
self.importer.get_sys_mod_name(jac_file_path),
|
|
@@ -177,8 +177,8 @@ class Importer:
|
|
|
177
177
|
|
|
178
178
|
def update_sys(self, module: types.ModuleType, spec: ImportPathSpec) -> None:
|
|
179
179
|
"""Update sys.modules with the newly imported module."""
|
|
180
|
-
if spec.module_name not in
|
|
181
|
-
|
|
180
|
+
if spec.module_name not in self.jac_machine.loaded_modules:
|
|
181
|
+
self.jac_machine.load_module(spec.module_name, module)
|
|
182
182
|
|
|
183
183
|
|
|
184
184
|
class PythonImporter(Importer):
|
|
@@ -280,8 +280,8 @@ class JacImporter(Importer):
|
|
|
280
280
|
module.__path__ = [full_mod_path]
|
|
281
281
|
module.__file__ = None
|
|
282
282
|
|
|
283
|
-
if module_name not in
|
|
284
|
-
|
|
283
|
+
if module_name not in self.jac_machine.loaded_modules:
|
|
284
|
+
self.jac_machine.load_module(module_name, module)
|
|
285
285
|
return module
|
|
286
286
|
|
|
287
287
|
def create_jac_py_module(
|
|
@@ -299,7 +299,7 @@ class JacImporter(Importer):
|
|
|
299
299
|
parts = package_path.split(".")
|
|
300
300
|
for i in range(len(parts)):
|
|
301
301
|
package_name = ".".join(parts[: i + 1])
|
|
302
|
-
if package_name not in
|
|
302
|
+
if package_name not in self.jac_machine.loaded_modules:
|
|
303
303
|
full_mod_path = path.join(
|
|
304
304
|
base_path, package_name.replace(".", path.sep)
|
|
305
305
|
)
|
|
@@ -307,7 +307,7 @@ class JacImporter(Importer):
|
|
|
307
307
|
module_name=package_name,
|
|
308
308
|
full_mod_path=full_mod_path,
|
|
309
309
|
)
|
|
310
|
-
|
|
310
|
+
self.jac_machine.load_module(module_name, module)
|
|
311
311
|
return module
|
|
312
312
|
|
|
313
313
|
def run_import(
|
|
@@ -322,7 +322,7 @@ class JacImporter(Importer):
|
|
|
322
322
|
else:
|
|
323
323
|
module_name = self.get_sys_mod_name(spec.full_target)
|
|
324
324
|
|
|
325
|
-
module =
|
|
325
|
+
module = self.jac_machine.loaded_modules.get(module_name)
|
|
326
326
|
|
|
327
327
|
if not module or module.__name__ == "__main__" or reload:
|
|
328
328
|
if os.path.isdir(spec.full_target):
|
|
@@ -340,14 +340,14 @@ class JacImporter(Importer):
|
|
|
340
340
|
caller_dir=spec.caller_dir,
|
|
341
341
|
cachable=spec.cachable,
|
|
342
342
|
)
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
343
|
+
try:
|
|
344
|
+
if not codeobj:
|
|
345
|
+
raise ImportError(f"No bytecode found for {spec.full_target}")
|
|
346
|
+
with sys_path_context(spec.caller_dir):
|
|
347
347
|
exec(codeobj, module.__dict__)
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
348
|
+
except Exception as e:
|
|
349
|
+
logger.error(dump_traceback(e))
|
|
350
|
+
raise e
|
|
351
351
|
import_return = ImportReturn(module, unique_loaded_items, self)
|
|
352
352
|
if spec.items:
|
|
353
353
|
import_return.process_items(
|
jaclang/runtimelib/machine.py
CHANGED
|
@@ -1,25 +1,33 @@
|
|
|
1
1
|
"""Jac Machine module."""
|
|
2
2
|
|
|
3
|
+
import inspect
|
|
3
4
|
import marshal
|
|
4
5
|
import os
|
|
6
|
+
import sys
|
|
5
7
|
import types
|
|
8
|
+
from contextvars import ContextVar
|
|
6
9
|
from typing import Optional
|
|
7
10
|
|
|
8
11
|
from jaclang.compiler.absyntree import Module
|
|
9
12
|
from jaclang.compiler.compile import compile_jac
|
|
10
13
|
from jaclang.compiler.constant import Constants as Con
|
|
14
|
+
from jaclang.runtimelib.architype import EdgeArchitype, NodeArchitype, WalkerArchitype
|
|
11
15
|
from jaclang.utils.log import logging
|
|
12
16
|
|
|
17
|
+
|
|
13
18
|
logger = logging.getLogger(__name__)
|
|
14
19
|
|
|
15
20
|
|
|
21
|
+
JACMACHINE_CONTEXT = ContextVar["JacMachine | None"]("JacMachine")
|
|
22
|
+
|
|
23
|
+
|
|
16
24
|
class JacMachine:
|
|
17
25
|
"""JacMachine to handle the VM-related functionalities and loaded programs."""
|
|
18
26
|
|
|
19
27
|
def __init__(self, base_path: str = "") -> None:
|
|
20
28
|
"""Initialize the JacMachine object."""
|
|
21
|
-
|
|
22
|
-
if not
|
|
29
|
+
self.loaded_modules: dict[str, types.ModuleType] = {}
|
|
30
|
+
if not base_path:
|
|
23
31
|
base_path = os.getcwd()
|
|
24
32
|
self.base_path = base_path
|
|
25
33
|
self.base_path_dir = (
|
|
@@ -29,6 +37,8 @@ class JacMachine:
|
|
|
29
37
|
)
|
|
30
38
|
self.jac_program: Optional[JacProgram] = None
|
|
31
39
|
|
|
40
|
+
JACMACHINE_CONTEXT.set(self)
|
|
41
|
+
|
|
32
42
|
def attach_program(self, jac_program: "JacProgram") -> None:
|
|
33
43
|
"""Attach a JacProgram to the machine."""
|
|
34
44
|
self.jac_program = jac_program
|
|
@@ -53,6 +63,60 @@ class JacMachine:
|
|
|
53
63
|
)
|
|
54
64
|
return None
|
|
55
65
|
|
|
66
|
+
def load_module(self, module_name: str, module: types.ModuleType) -> None:
|
|
67
|
+
"""Load a module into the machine."""
|
|
68
|
+
self.loaded_modules[module_name] = module
|
|
69
|
+
sys.modules[module_name] = module
|
|
70
|
+
|
|
71
|
+
def list_modules(self) -> list[str]:
|
|
72
|
+
"""List all loaded modules."""
|
|
73
|
+
return list(self.loaded_modules.keys())
|
|
74
|
+
|
|
75
|
+
def list_walkers(self, module_name: str) -> list[str]:
|
|
76
|
+
"""List all walkers in a specific module."""
|
|
77
|
+
module = self.loaded_modules.get(module_name)
|
|
78
|
+
if module:
|
|
79
|
+
walkers = []
|
|
80
|
+
for name, obj in inspect.getmembers(module):
|
|
81
|
+
if isinstance(obj, type) and issubclass(obj, WalkerArchitype):
|
|
82
|
+
walkers.append(name)
|
|
83
|
+
return walkers
|
|
84
|
+
return []
|
|
85
|
+
|
|
86
|
+
def list_nodes(self, module_name: str) -> list[str]:
|
|
87
|
+
"""List all nodes in a specific module."""
|
|
88
|
+
module = self.loaded_modules.get(module_name)
|
|
89
|
+
if module:
|
|
90
|
+
nodes = []
|
|
91
|
+
for name, obj in inspect.getmembers(module):
|
|
92
|
+
if isinstance(obj, type) and issubclass(obj, NodeArchitype):
|
|
93
|
+
nodes.append(name)
|
|
94
|
+
return nodes
|
|
95
|
+
return []
|
|
96
|
+
|
|
97
|
+
def list_edges(self, module_name: str) -> list[str]:
|
|
98
|
+
"""List all edges in a specific module."""
|
|
99
|
+
module = self.loaded_modules.get(module_name)
|
|
100
|
+
if module:
|
|
101
|
+
nodes = []
|
|
102
|
+
for name, obj in inspect.getmembers(module):
|
|
103
|
+
if isinstance(obj, type) and issubclass(obj, EdgeArchitype):
|
|
104
|
+
nodes.append(name)
|
|
105
|
+
return nodes
|
|
106
|
+
return []
|
|
107
|
+
|
|
108
|
+
@staticmethod
|
|
109
|
+
def get(base_path: str = "") -> "JacMachine":
|
|
110
|
+
"""Get current jac machine."""
|
|
111
|
+
if (jac_machine := JACMACHINE_CONTEXT.get(None)) is None:
|
|
112
|
+
jac_machine = JacMachine(base_path)
|
|
113
|
+
return jac_machine
|
|
114
|
+
|
|
115
|
+
@staticmethod
|
|
116
|
+
def detach() -> None:
|
|
117
|
+
"""Detach current jac machine."""
|
|
118
|
+
JACMACHINE_CONTEXT.set(None)
|
|
119
|
+
|
|
56
120
|
|
|
57
121
|
class JacProgram:
|
|
58
122
|
"""Class to hold the mod_bundle and bytecode for Jac modules."""
|
jaclang/runtimelib/memory.py
CHANGED
|
@@ -2,98 +2,157 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from pickle import dumps
|
|
7
|
+
from shelve import Shelf, open
|
|
8
|
+
from typing import Callable, Generator, Generic, Iterable, TypeVar
|
|
6
9
|
from uuid import UUID
|
|
7
10
|
|
|
8
|
-
from .architype import
|
|
11
|
+
from .architype import Anchor, NodeAnchor, Root, TANCH
|
|
9
12
|
|
|
13
|
+
ID = TypeVar("ID")
|
|
10
14
|
|
|
11
|
-
class Memory:
|
|
12
|
-
"""Memory module interface."""
|
|
13
15
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
@dataclass
|
|
17
|
+
class Memory(Generic[ID, TANCH]):
|
|
18
|
+
"""Generic Memory Handler."""
|
|
16
19
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
pass
|
|
20
|
+
__mem__: dict[ID, TANCH] = field(default_factory=dict)
|
|
21
|
+
__gc__: set[ID] = field(default_factory=set)
|
|
20
22
|
|
|
21
|
-
def
|
|
22
|
-
"""
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
def close(self) -> None:
|
|
24
|
+
"""Close memory handler."""
|
|
25
|
+
self.__mem__.clear()
|
|
26
|
+
self.__gc__.clear()
|
|
27
|
+
|
|
28
|
+
def find(
|
|
29
|
+
self,
|
|
30
|
+
ids: ID | Iterable[ID],
|
|
31
|
+
filter: Callable[[TANCH], TANCH] | None = None,
|
|
32
|
+
) -> Generator[TANCH, None, None]:
|
|
33
|
+
"""Find anchors from memory by ids with filter."""
|
|
34
|
+
if not isinstance(ids, Iterable):
|
|
35
|
+
ids = [ids]
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
anchor
|
|
39
|
+
for id in ids
|
|
40
|
+
if (anchor := self.__mem__.get(id)) and (not filter or filter(anchor))
|
|
41
|
+
)
|
|
29
42
|
|
|
30
|
-
def
|
|
31
|
-
|
|
32
|
-
|
|
43
|
+
def find_one(
|
|
44
|
+
self,
|
|
45
|
+
ids: ID | Iterable[ID],
|
|
46
|
+
filter: Callable[[TANCH], TANCH] | None = None,
|
|
47
|
+
) -> TANCH | None:
|
|
48
|
+
"""Find one anchor from memory by ids with filter."""
|
|
49
|
+
return next(self.find(ids, filter), None)
|
|
33
50
|
|
|
34
|
-
def
|
|
35
|
-
"""
|
|
36
|
-
return
|
|
51
|
+
def find_by_id(self, id: ID) -> TANCH | None:
|
|
52
|
+
"""Find one by id."""
|
|
53
|
+
return self.__mem__.get(id)
|
|
37
54
|
|
|
38
|
-
def
|
|
39
|
-
"""Save
|
|
40
|
-
self.
|
|
41
|
-
if persistent:
|
|
42
|
-
# TODO: check if it needs to be saved, i.e. dirty or not
|
|
43
|
-
self.save_obj_list[item._jac_.id] = item
|
|
55
|
+
def set(self, id: ID, data: TANCH) -> None:
|
|
56
|
+
"""Save anchor to memory."""
|
|
57
|
+
self.__mem__[id] = data
|
|
44
58
|
|
|
45
|
-
def
|
|
46
|
-
"""
|
|
47
|
-
|
|
59
|
+
def remove(self, ids: ID | Iterable[ID]) -> None:
|
|
60
|
+
"""Remove anchor/s from memory."""
|
|
61
|
+
if not isinstance(ids, Iterable):
|
|
62
|
+
ids = [ids]
|
|
48
63
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
64
|
+
for id in ids:
|
|
65
|
+
self.__mem__.pop(id, None)
|
|
66
|
+
self.__gc__.add(id)
|
|
52
67
|
|
|
53
68
|
|
|
54
|
-
|
|
55
|
-
|
|
69
|
+
@dataclass
|
|
70
|
+
class ShelfStorage(Memory[UUID, Anchor]):
|
|
71
|
+
"""Shelf Handler."""
|
|
56
72
|
|
|
57
|
-
|
|
73
|
+
__shelf__: Shelf[Anchor] | None = None
|
|
58
74
|
|
|
59
|
-
def __init__(self, session: str =
|
|
60
|
-
"""
|
|
75
|
+
def __init__(self, session: str | None = None) -> None:
|
|
76
|
+
"""Initialize memory handler."""
|
|
61
77
|
super().__init__()
|
|
62
|
-
if session:
|
|
63
|
-
self.connect(session)
|
|
64
|
-
|
|
65
|
-
def get_obj_from_store(self, obj_id: UUID) -> Architype | None:
|
|
66
|
-
"""Get object from the underlying store."""
|
|
67
|
-
obj = super().get_obj_from_store(obj_id)
|
|
68
|
-
if obj is None and self.storage:
|
|
69
|
-
obj = self.storage.get(str(obj_id))
|
|
70
|
-
if obj is not None:
|
|
71
|
-
self.mem[obj_id] = obj
|
|
72
|
-
|
|
73
|
-
return obj
|
|
74
|
-
|
|
75
|
-
def has_obj_in_store(self, obj_id: UUID | str) -> bool:
|
|
76
|
-
"""Check if the object exists in the underlying store."""
|
|
77
|
-
return obj_id in self.mem or (
|
|
78
|
-
str(obj_id) in self.storage if self.storage else False
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
def commit(self) -> None:
|
|
82
|
-
"""Commit changes to persistent storage."""
|
|
83
|
-
if self.storage is not None:
|
|
84
|
-
for obj_id, obj in self.save_obj_list.items():
|
|
85
|
-
self.storage[str(obj_id)] = obj
|
|
86
|
-
self.save_obj_list.clear()
|
|
87
|
-
|
|
88
|
-
def connect(self, session: str) -> None:
|
|
89
|
-
"""Connect to storage."""
|
|
90
|
-
self.session = session
|
|
91
|
-
self.storage = shelve.open(session)
|
|
78
|
+
self.__shelf__ = open(session) if session else None # noqa: SIM115
|
|
92
79
|
|
|
93
80
|
def close(self) -> None:
|
|
94
|
-
"""Close
|
|
81
|
+
"""Close memory handler."""
|
|
82
|
+
if isinstance(self.__shelf__, Shelf):
|
|
83
|
+
from jaclang.plugin.feature import JacFeature as Jac
|
|
84
|
+
|
|
85
|
+
root = Jac.get_root().__jac__
|
|
86
|
+
|
|
87
|
+
for id in self.__gc__:
|
|
88
|
+
self.__shelf__.pop(str(id), None)
|
|
89
|
+
self.__mem__.pop(id, None)
|
|
90
|
+
|
|
91
|
+
for d in self.__mem__.values():
|
|
92
|
+
if d.persistent and d.hash != hash(dumps(d)):
|
|
93
|
+
_id = str(d.id)
|
|
94
|
+
if p_d := self.__shelf__.get(_id):
|
|
95
|
+
if (
|
|
96
|
+
isinstance(p_d, NodeAnchor)
|
|
97
|
+
and isinstance(d, NodeAnchor)
|
|
98
|
+
and p_d.edges != d.edges
|
|
99
|
+
and root.has_connect_access(d)
|
|
100
|
+
):
|
|
101
|
+
if not d.edges:
|
|
102
|
+
self.__shelf__.pop(_id, None)
|
|
103
|
+
continue
|
|
104
|
+
p_d.edges = d.edges
|
|
105
|
+
|
|
106
|
+
if root.has_write_access(d):
|
|
107
|
+
if hash(dumps(p_d.access)) != hash(dumps(d.access)):
|
|
108
|
+
p_d.access = d.access
|
|
109
|
+
if hash(dumps(d.architype)) != hash(dumps(d.architype)):
|
|
110
|
+
p_d.architype = d.architype
|
|
111
|
+
|
|
112
|
+
self.__shelf__[_id] = p_d
|
|
113
|
+
elif not (
|
|
114
|
+
isinstance(d, NodeAnchor)
|
|
115
|
+
and not isinstance(d.architype, Root)
|
|
116
|
+
and not d.edges
|
|
117
|
+
):
|
|
118
|
+
self.__shelf__[_id] = d
|
|
119
|
+
|
|
120
|
+
self.__shelf__.close()
|
|
95
121
|
super().close()
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
122
|
+
|
|
123
|
+
def find(
|
|
124
|
+
self,
|
|
125
|
+
ids: UUID | Iterable[UUID],
|
|
126
|
+
filter: Callable[[Anchor], Anchor] | None = None,
|
|
127
|
+
) -> Generator[Anchor, None, None]:
|
|
128
|
+
"""Find anchors from datasource by ids with filter."""
|
|
129
|
+
if not isinstance(ids, Iterable):
|
|
130
|
+
ids = [ids]
|
|
131
|
+
|
|
132
|
+
if isinstance(self.__shelf__, Shelf):
|
|
133
|
+
for id in ids:
|
|
134
|
+
anchor = self.__mem__.get(id)
|
|
135
|
+
|
|
136
|
+
if (
|
|
137
|
+
not anchor
|
|
138
|
+
and id not in self.__gc__
|
|
139
|
+
and (_anchor := self.__shelf__.get(str(id)))
|
|
140
|
+
):
|
|
141
|
+
self.__mem__[id] = anchor = _anchor
|
|
142
|
+
if anchor and (not filter or filter(anchor)):
|
|
143
|
+
yield anchor
|
|
144
|
+
else:
|
|
145
|
+
yield from super().find(ids, filter)
|
|
146
|
+
|
|
147
|
+
def find_by_id(self, id: UUID) -> Anchor | None:
|
|
148
|
+
"""Find one by id."""
|
|
149
|
+
data = super().find_by_id(id)
|
|
150
|
+
|
|
151
|
+
if (
|
|
152
|
+
not data
|
|
153
|
+
and isinstance(self.__shelf__, Shelf)
|
|
154
|
+
and (data := self.__shelf__.get(str(id)))
|
|
155
|
+
):
|
|
156
|
+
self.__mem__[id] = data
|
|
157
|
+
|
|
158
|
+
return data
|
jaclang/runtimelib/utils.py
CHANGED
|
@@ -37,12 +37,16 @@ def collect_node_connections(
|
|
|
37
37
|
visited_nodes.add(current_node)
|
|
38
38
|
edges = current_node.edges
|
|
39
39
|
for edge_ in edges:
|
|
40
|
-
target = edge_.
|
|
40
|
+
target = edge_.target
|
|
41
41
|
if target:
|
|
42
42
|
connections.add(
|
|
43
|
-
(
|
|
43
|
+
(
|
|
44
|
+
current_node.architype,
|
|
45
|
+
target.architype,
|
|
46
|
+
edge_.__class__.__name__,
|
|
47
|
+
)
|
|
44
48
|
)
|
|
45
|
-
collect_node_connections(target
|
|
49
|
+
collect_node_connections(target, visited_nodes, connections)
|
|
46
50
|
|
|
47
51
|
|
|
48
52
|
def traverse_graph(
|
|
@@ -61,17 +65,20 @@ def traverse_graph(
|
|
|
61
65
|
edge_limit: int,
|
|
62
66
|
) -> None:
|
|
63
67
|
"""Traverse the graph using Breadth-First Search (BFS) or Depth-First Search (DFS)."""
|
|
64
|
-
for edge in node.
|
|
65
|
-
is_self_loop = id(edge.
|
|
66
|
-
is_in_edge = edge.
|
|
67
|
-
if (traverse and is_in_edge) or edge.
|
|
68
|
+
for edge in node.__jac__.edges:
|
|
69
|
+
is_self_loop = id(edge.source) == id(edge.target)
|
|
70
|
+
is_in_edge = edge.target == node.__jac__
|
|
71
|
+
if (traverse and is_in_edge) or edge.architype.__class__.__name__ in edge_type:
|
|
68
72
|
continue
|
|
69
73
|
if is_self_loop:
|
|
70
74
|
continue # lets skip self loop for a while, need to handle it later
|
|
71
|
-
else
|
|
72
|
-
other_nd
|
|
75
|
+
elif (other_nda := edge.target if not is_in_edge else edge.source) and (
|
|
76
|
+
other_nd := other_nda.architype
|
|
77
|
+
):
|
|
73
78
|
new_con = (
|
|
74
|
-
(node, other_nd, edge)
|
|
79
|
+
(node, other_nd, edge.architype)
|
|
80
|
+
if not is_in_edge
|
|
81
|
+
else (other_nd, node, edge.architype)
|
|
75
82
|
)
|
|
76
83
|
if node in node_depths and node_depths[node] is not None:
|
|
77
84
|
if other_nd in node_depths:
|