jaclang 0.7.29__py3-none-any.whl → 0.7.31__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.

Files changed (40) hide show
  1. jaclang/__init__.py +419 -3
  2. jaclang/compiler/__init__.py +1 -1
  3. jaclang/compiler/absyntree.py +15 -5
  4. jaclang/compiler/compile.py +1 -1
  5. jaclang/compiler/constant.py +4 -5
  6. jaclang/compiler/jac.lark +227 -180
  7. jaclang/compiler/parser.py +1335 -1826
  8. jaclang/compiler/passes/main/fuse_typeinfo_pass.py +2 -2
  9. jaclang/compiler/passes/main/import_pass.py +3 -2
  10. jaclang/compiler/passes/main/pyast_gen_pass.py +570 -747
  11. jaclang/compiler/passes/main/tests/test_import_pass.py +4 -1
  12. jaclang/compiler/passes/main/tests/test_type_check_pass.py +6 -3
  13. jaclang/compiler/passes/tool/jac_formatter_pass.py +0 -1
  14. jaclang/compiler/tests/test_importer.py +45 -1
  15. jaclang/compiler/tests/test_parser.py +13 -5
  16. jaclang/plugin/builtin.py +11 -0
  17. jaclang/plugin/default.py +55 -20
  18. jaclang/plugin/feature.py +14 -5
  19. jaclang/plugin/spec.py +16 -6
  20. jaclang/plugin/tests/fixtures/graph_purger.jac +2 -0
  21. jaclang/plugin/tests/fixtures/other_root_access.jac +1 -0
  22. jaclang/plugin/tests/fixtures/savable_object.jac +2 -0
  23. jaclang/plugin/tests/test_jaseci.py +1 -1
  24. jaclang/runtimelib/architype.py +11 -21
  25. jaclang/runtimelib/context.py +25 -9
  26. jaclang/runtimelib/importer.py +26 -3
  27. jaclang/runtimelib/machine.py +2 -2
  28. jaclang/settings.py +2 -0
  29. jaclang/tests/fixtures/create_dynamic_architype.jac +1 -1
  30. jaclang/tests/fixtures/nested_impls.jac +55 -0
  31. jaclang/tests/test_cli.py +1 -1
  32. jaclang/tests/test_language.py +27 -13
  33. jaclang/tests/test_reference.py +2 -2
  34. jaclang/utils/helpers.py +4 -3
  35. jaclang/utils/test.py +2 -2
  36. jaclang/utils/tests/test_lang_tools.py +4 -2
  37. {jaclang-0.7.29.dist-info → jaclang-0.7.31.dist-info}/METADATA +2 -2
  38. {jaclang-0.7.29.dist-info → jaclang-0.7.31.dist-info}/RECORD +40 -39
  39. {jaclang-0.7.29.dist-info → jaclang-0.7.31.dist-info}/WHEEL +1 -1
  40. {jaclang-0.7.29.dist-info → jaclang-0.7.31.dist-info}/entry_points.txt +0 -0
@@ -83,7 +83,10 @@ class ImportPassPassTests(TestCase):
83
83
  for i in p:
84
84
  self.assertIn(i, build.ir.py_info.py_raise_map)
85
85
  self.assertRegex(
86
- re.sub(r".*fixtures/", "", build.ir.py_info.py_raise_map[i]), p[i]
86
+ re.sub(r".*fixtures/", "", build.ir.py_info.py_raise_map[i]).replace(
87
+ "\\", "/"
88
+ ),
89
+ p[i],
87
90
  )
88
91
 
89
92
  def test_py_raised_mods(self) -> None:
@@ -70,19 +70,22 @@ class MypyTypeCheckPassTests(TestCase):
70
70
  )
71
71
  self.assertRegex(
72
72
  out,
73
- r"129:24 - 129:28.*SpecialVarRef - _Jac.get_root\(\) \- Type\: jaclang.runtimelib.architype.Root",
73
+ r"129:24 - 129:28.*SpecialVarRef - Jac.get_root\(\) \- Type\: jaclang.Root",
74
74
  )
75
+
75
76
  self.assertRegex(out, r"129:11 - 129:29.*FuncCall \- Type\: builtins\.str")
76
77
  self.assertRegex(
77
78
  out,
78
79
  r"129:15 - 129:23.*Name \- node_dot \- Type\: builtins.str, SymbolTable\: str",
79
80
  )
81
+
80
82
  self.assertRegex(
81
83
  out,
82
- r"128:5 - 128:25.*BinaryExpr \- Type\: jaclang.runtimelib.architype.WalkerArchitype",
84
+ r"128:5 - 128:25.*BinaryExpr \- Type\: jaclang.Walker",
83
85
  )
84
86
  self.assertRegex(
85
87
  out,
86
- r"48:11 - 48:28.*EdgeRefTrailer \- Type\: builtins.list\[data_spatial_types.A\]",
88
+ r"48:11 - 48:28.*EdgeRefTrailer \- Type\: jaclang.JacList\[data_spatial_types.A\]",
87
89
  )
90
+
88
91
  self.assertRegex(out, r"24:5 - 24:25.*BinaryExpr \- Type\: builtins.bool", out)
@@ -973,7 +973,6 @@ class JacFormatPass(Pass):
973
973
  Tok.PIPE_FWD,
974
974
  Tok.KW_SPAWN,
975
975
  Tok.A_PIPE_FWD,
976
- Tok.ELVIS_OP,
977
976
  ]
978
977
  or (
979
978
  node.op.name == Tok.PIPE_FWD
@@ -33,7 +33,7 @@ class TestLoader(TestCase):
33
33
  )
34
34
  self.assertIn(
35
35
  "/tests/fixtures/hello_world.jac",
36
- str(JacMachine.get().loaded_modules),
36
+ str(JacMachine.get().loaded_modules).replace("\\\\", "/"),
37
37
  )
38
38
  JacMachine.detach()
39
39
 
@@ -62,3 +62,47 @@ class TestLoader(TestCase):
62
62
  "{SomeObj(a=10): 'check'} [MyObj(apple=5, banana=7), MyObj(apple=5, banana=7)]",
63
63
  stdout_value,
64
64
  )
65
+
66
+ def test_import_with_jacpath(self) -> None:
67
+ """Test module import using JACPATH."""
68
+ # Set up a temporary JACPATH environment variable
69
+ import os
70
+ import tempfile
71
+
72
+ jacpath_dir = tempfile.TemporaryDirectory()
73
+ os.environ["JACPATH"] = jacpath_dir.name
74
+
75
+ # Create a mock Jac file in the JACPATH directory
76
+ module_name = "test_module"
77
+ jac_file_path = os.path.join(jacpath_dir.name, f"{module_name}.jac")
78
+ with open(jac_file_path, "w") as f:
79
+ f.write(
80
+ """
81
+ with entry {
82
+ "Hello from JACPATH!" :> print;
83
+ }
84
+ """
85
+ )
86
+
87
+ # Capture the output
88
+ captured_output = io.StringIO()
89
+ sys.stdout = captured_output
90
+
91
+ try:
92
+ JacMachine(self.fixture_abs_path(__file__)).attach_program(
93
+ JacProgram(mod_bundle=None, bytecode=None, sem_ir=None)
94
+ )
95
+ jac_import(module_name, base_path=__file__)
96
+ cli.run(jac_file_path)
97
+
98
+ # Reset stdout and get the output
99
+ sys.stdout = sys.__stdout__
100
+ stdout_value = captured_output.getvalue()
101
+
102
+ self.assertIn("Hello from JACPATH!", stdout_value)
103
+
104
+ finally:
105
+ captured_output.close()
106
+ JacMachine.detach()
107
+ os.environ.pop("JACPATH", None)
108
+ jacpath_dir.cleanup()
@@ -89,12 +89,20 @@ class TestLarkParser(TestCaseMicroSuite):
89
89
  JacParser.TreeToAST.__base__, value.__name__, False
90
90
  ):
91
91
  parse_funcs.append(name)
92
- for i in rules:
93
- self.assertIn(i, parse_funcs)
94
- for i in parse_funcs:
95
- if i in ["binary_expr_unwind", "ice", "nu"]:
92
+ for rule in rules:
93
+ self.assertIn(rule, parse_funcs)
94
+ for fn in parse_funcs:
95
+ if fn.startswith("_") or fn in [
96
+ "ice",
97
+ "match",
98
+ "consume",
99
+ "match_token",
100
+ "consume_token",
101
+ "match_many",
102
+ "consume_many",
103
+ ]:
96
104
  continue
97
- self.assertIn(i, rules)
105
+ self.assertIn(fn, rules)
98
106
 
99
107
  def test_all_ast_has_normalize(self) -> None:
100
108
  """Test for enter/exit name diffs with parser."""
jaclang/plugin/builtin.py CHANGED
@@ -7,6 +7,12 @@ from typing import Optional
7
7
  from jaclang.plugin.feature import JacFeature as Jac
8
8
  from jaclang.runtimelib.constructs import Architype, NodeArchitype
9
9
 
10
+ __all__ = [
11
+ "dotgen",
12
+ "jid",
13
+ "jobj",
14
+ ]
15
+
10
16
 
11
17
  def dotgen(
12
18
  node: Optional[NodeArchitype] = None,
@@ -44,3 +50,8 @@ def dotgen(
44
50
  def jid(obj: Architype) -> str:
45
51
  """Get the id of the object."""
46
52
  return Jac.object_ref(obj)
53
+
54
+
55
+ def jobj(id: str) -> Architype | None:
56
+ """Get the object from the id."""
57
+ return Jac.get_object(id)
jaclang/plugin/default.py CHANGED
@@ -179,14 +179,12 @@ class JacAccessValidationImpl:
179
179
  if to_root.access.all > access_level:
180
180
  access_level = to_root.access.all
181
181
 
182
- level = to_root.access.roots.check(str(jroot.id))
183
- if level > AccessLevel.NO_ACCESS and access_level == AccessLevel.NO_ACCESS:
182
+ if (level := to_root.access.roots.check(str(jroot.id))) is not None:
184
183
  access_level = level
185
184
 
186
185
  # if target anchor have set allowed roots
187
186
  # if current root is allowed to target anchor
188
- level = to_access.roots.check(str(jroot.id))
189
- if level > AccessLevel.NO_ACCESS and access_level == AccessLevel.NO_ACCESS:
187
+ if (level := to_access.roots.check(str(jroot.id))) is not None:
190
188
  access_level = level
191
189
 
192
190
  return access_level
@@ -662,11 +660,15 @@ class JacFeatureImpl(
662
660
  if not hasattr(cls, "_jac_entry_funcs_") or not hasattr(
663
661
  cls, "_jac_exit_funcs_"
664
662
  ):
665
- # Saving the module path and reassign it after creating cls
666
- # So the jac modules are part of the correct module
667
- cur_module = cls.__module__
668
- cls = type(cls.__name__, (cls, arch_base), {})
669
- cls.__module__ = cur_module
663
+ # If a class only inherit from object (ie. Doesn't inherit from a class), we cannot modify
664
+ # the __bases__ property of it, so it's necessary to make sure the class is not a direct child of object.
665
+ assert cls.__bases__ != (object,)
666
+ bases = (
667
+ (cls.__bases__ + (arch_base,))
668
+ if arch_base not in cls.__bases__
669
+ else cls.__bases__
670
+ )
671
+ cls.__bases__ = bases
670
672
  cls._jac_entry_funcs_ = on_entry # type: ignore
671
673
  cls._jac_exit_funcs_ = on_exit # type: ignore
672
674
  else:
@@ -726,6 +728,22 @@ class JacFeatureImpl(
726
728
 
727
729
  return decorator
728
730
 
731
+ @staticmethod
732
+ @hookimpl
733
+ def make_root(
734
+ on_entry: list[DSFunc], on_exit: list[DSFunc]
735
+ ) -> Callable[[type], type]:
736
+ """Create a obj architype."""
737
+
738
+ def decorator(cls: Type[Architype]) -> Type[Architype]:
739
+ """Decorate class."""
740
+ cls = Jac.make_architype(
741
+ cls=cls, arch_base=Root, on_entry=on_entry, on_exit=on_exit
742
+ )
743
+ return cls
744
+
745
+ return decorator
746
+
729
747
  @staticmethod
730
748
  @hookimpl
731
749
  def make_edge(
@@ -742,6 +760,25 @@ class JacFeatureImpl(
742
760
 
743
761
  return decorator
744
762
 
763
+ @staticmethod
764
+ @hookimpl
765
+ def make_generic_edge(
766
+ on_entry: list[DSFunc], on_exit: list[DSFunc]
767
+ ) -> Callable[[type], type]:
768
+ """Create a edge architype."""
769
+
770
+ def decorator(cls: Type[Architype]) -> Type[Architype]:
771
+ """Decorate class."""
772
+ cls = Jac.make_architype(
773
+ cls=cls,
774
+ arch_base=GenericEdge,
775
+ on_entry=on_entry,
776
+ on_exit=on_exit,
777
+ )
778
+ return cls
779
+
780
+ return decorator
781
+
745
782
  @staticmethod
746
783
  @hookimpl
747
784
  def make_walker(
@@ -920,12 +957,6 @@ class JacFeatureImpl(
920
957
 
921
958
  return ret_count
922
959
 
923
- @staticmethod
924
- @hookimpl
925
- def elvis(op1: Optional[T], op2: T) -> T:
926
- """Jac's elvis operator feature."""
927
- return ret if (ret := op1) is not None else op2
928
-
929
960
  @staticmethod
930
961
  @hookimpl
931
962
  def has_instance_default(gen_func: Callable[[], T]) -> T:
@@ -1032,7 +1063,7 @@ class JacFeatureImpl(
1032
1063
  dir in [EdgeDir.OUT, EdgeDir.ANY]
1033
1064
  and node == source
1034
1065
  and target.architype in right
1035
- and Jac.check_write_access(target)
1066
+ and Jac.check_connect_access(target)
1036
1067
  ):
1037
1068
  Jac.destroy(anchor) if anchor.persistent else Jac.detach(anchor)
1038
1069
  disconnect_occurred = True
@@ -1040,7 +1071,7 @@ class JacFeatureImpl(
1040
1071
  dir in [EdgeDir.IN, EdgeDir.ANY]
1041
1072
  and node == target
1042
1073
  and source.architype in right
1043
- and Jac.check_write_access(source)
1074
+ and Jac.check_connect_access(source)
1044
1075
  ):
1045
1076
  Jac.destroy(anchor) if anchor.persistent else Jac.detach(anchor)
1046
1077
  disconnect_occurred = True
@@ -1069,7 +1100,9 @@ class JacFeatureImpl(
1069
1100
  @hookimpl
1070
1101
  def get_root_type() -> Type[Root]:
1071
1102
  """Jac's root getter."""
1072
- return Root
1103
+ from jaclang import Root as JRoot
1104
+
1105
+ return cast(Type[Root], JRoot)
1073
1106
 
1074
1107
  @staticmethod
1075
1108
  @hookimpl
@@ -1079,10 +1112,12 @@ class JacFeatureImpl(
1079
1112
  conn_assign: Optional[tuple[tuple, tuple]],
1080
1113
  ) -> Callable[[NodeAnchor, NodeAnchor], EdgeArchitype]:
1081
1114
  """Jac's root getter."""
1082
- conn_type = conn_type if conn_type else GenericEdge
1115
+ from jaclang import GenericEdge
1116
+
1117
+ ct = conn_type if conn_type else GenericEdge
1083
1118
 
1084
1119
  def builder(source: NodeAnchor, target: NodeAnchor) -> EdgeArchitype:
1085
- edge = conn_type() if isinstance(conn_type, type) else conn_type
1120
+ edge = ct() if isinstance(ct, type) else ct
1086
1121
 
1087
1122
  eanch = edge.__jac__ = EdgeAnchor(
1088
1123
  architype=edge,
jaclang/plugin/feature.py CHANGED
@@ -305,6 +305,13 @@ class JacFeature(
305
305
  """Create a node architype."""
306
306
  return plugin_manager.hook.make_node(on_entry=on_entry, on_exit=on_exit)
307
307
 
308
+ @staticmethod
309
+ def make_root(
310
+ on_entry: list[DSFunc], on_exit: list[DSFunc]
311
+ ) -> Callable[[type], type]:
312
+ """Create a root node architype."""
313
+ return plugin_manager.hook.make_root(on_entry=on_entry, on_exit=on_exit)
314
+
308
315
  @staticmethod
309
316
  def make_edge(
310
317
  on_entry: list[DSFunc], on_exit: list[DSFunc]
@@ -312,6 +319,13 @@ class JacFeature(
312
319
  """Create a edge architype."""
313
320
  return plugin_manager.hook.make_edge(on_entry=on_entry, on_exit=on_exit)
314
321
 
322
+ @staticmethod
323
+ def make_generic_edge(
324
+ on_entry: list[DSFunc], on_exit: list[DSFunc]
325
+ ) -> Callable[[type], type]:
326
+ """Create a edge architype."""
327
+ return plugin_manager.hook.make_generic_edge(on_entry=on_entry, on_exit=on_exit)
328
+
315
329
  @staticmethod
316
330
  def make_walker(
317
331
  on_entry: list[DSFunc], on_exit: list[DSFunc]
@@ -377,11 +391,6 @@ class JacFeature(
377
391
  verbose=verbose,
378
392
  )
379
393
 
380
- @staticmethod
381
- def elvis(op1: Optional[T], op2: T) -> T:
382
- """Jac's elvis operator feature."""
383
- return plugin_manager.hook.elvis(op1=op1, op2=op2)
384
-
385
394
  @staticmethod
386
395
  def has_instance_default(gen_func: Callable[[], T]) -> T:
387
396
  """Jac's has container default feature."""
jaclang/plugin/spec.py CHANGED
@@ -295,6 +295,14 @@ class JacFeatureSpec(
295
295
  """Create a node architype."""
296
296
  raise NotImplementedError
297
297
 
298
+ @staticmethod
299
+ @hookspec(firstresult=True)
300
+ def make_root(
301
+ on_entry: list[DSFunc], on_exit: list[DSFunc]
302
+ ) -> Callable[[type], type]:
303
+ """Create a root node architype."""
304
+ raise NotImplementedError
305
+
298
306
  @staticmethod
299
307
  @hookspec(firstresult=True)
300
308
  def make_edge(
@@ -303,6 +311,14 @@ class JacFeatureSpec(
303
311
  """Create a edge architype."""
304
312
  raise NotImplementedError
305
313
 
314
+ @staticmethod
315
+ @hookspec(firstresult=True)
316
+ def make_generic_edge(
317
+ on_entry: list[DSFunc], on_exit: list[DSFunc]
318
+ ) -> Callable[[type], type]:
319
+ """Create a generic edge architype."""
320
+ raise NotImplementedError
321
+
306
322
  @staticmethod
307
323
  @hookspec(firstresult=True)
308
324
  def make_walker(
@@ -355,12 +371,6 @@ class JacFeatureSpec(
355
371
  """Run the test suite in the specified .jac file."""
356
372
  raise NotImplementedError
357
373
 
358
- @staticmethod
359
- @hookspec(firstresult=True)
360
- def elvis(op1: Optional[T], op2: T) -> T:
361
- """Jac's elvis operator feature."""
362
- raise NotImplementedError
363
-
364
374
  @staticmethod
365
375
  @hookspec(firstresult=True)
366
376
  def has_instance_default(gen_func: Callable[[], T]) -> T:
@@ -1,3 +1,5 @@
1
+ import:py from jaclang.plugin.feature {JacFeature as Jac}
2
+
1
3
  node A {
2
4
  has id: int;
3
5
  }
@@ -1,3 +1,4 @@
1
+ import:py from jaclang.plugin.feature {JacFeature as Jac}
1
2
  import:py from jaclang.runtimelib.architype {Anchor}
2
3
  import:py from uuid {UUID}
3
4
 
@@ -1,3 +1,5 @@
1
+ import:py from jaclang.plugin.feature {JacFeature as Jac}
2
+
1
3
  enum Enum {
2
4
  A = "a",
3
5
  B = "b",
@@ -748,7 +748,7 @@ class TestJaseciPlugin(TestCase):
748
748
  def test_savable_object(self) -> None:
749
749
  """Test ObjectAnchor save."""
750
750
  global session
751
- session = self.fixture_abs_path("other_root_access.session")
751
+ session = self.fixture_abs_path("savable_object.session")
752
752
 
753
753
  self._output2buffer()
754
754
 
@@ -12,6 +12,7 @@ from types import UnionType
12
12
  from typing import Any, Callable, ClassVar, Optional, TypeVar
13
13
  from uuid import UUID, uuid4
14
14
 
15
+
15
16
  logger = getLogger(__name__)
16
17
 
17
18
  TARCH = TypeVar("TARCH", bound="Architype")
@@ -44,9 +45,9 @@ class Access:
44
45
 
45
46
  anchors: dict[str, AccessLevel] = field(default_factory=dict)
46
47
 
47
- def check(self, anchor: str) -> AccessLevel:
48
+ def check(self, anchor: str) -> AccessLevel | None:
48
49
  """Validate access."""
49
- return self.anchors.get(anchor, AccessLevel.NO_ACCESS)
50
+ return self.anchors.get(anchor)
50
51
 
51
52
 
52
53
  @dataclass
@@ -285,21 +286,13 @@ class ObjectArchitype(Architype):
285
286
  self.__jac__ = ObjectAnchor(architype=self)
286
287
 
287
288
 
288
- @dataclass(eq=False)
289
289
  class GenericEdge(EdgeArchitype):
290
- """Generic Root Node."""
291
-
292
- _jac_entry_funcs_: ClassVar[list[DSFunc]] = []
293
- _jac_exit_funcs_: ClassVar[list[DSFunc]] = []
290
+ """Generic Edge."""
294
291
 
295
292
 
296
- @dataclass(eq=False)
297
293
  class Root(NodeArchitype):
298
294
  """Generic Root Node."""
299
295
 
300
- _jac_entry_funcs_: ClassVar[list[DSFunc]] = []
301
- _jac_exit_funcs_: ClassVar[list[DSFunc]] = []
302
-
303
296
  def __init__(self) -> None:
304
297
  """Create root node."""
305
298
  self.__jac__ = NodeAnchor(architype=self, persistent=True, edges=[])
@@ -315,16 +308,13 @@ class DSFunc:
315
308
  @cached_property
316
309
  def trigger(self) -> type | UnionType | tuple[type | UnionType, ...] | None:
317
310
  """Get function parameter annotations."""
318
- t = (
319
- (
320
- inspect.signature(self.func, eval_str=True)
321
- .parameters["_jac_here_"]
322
- .annotation
323
- )
324
- if self.func
325
- else None
326
- )
327
- return None if t is inspect._empty else t
311
+ if self.func:
312
+ parameters = inspect.signature(self.func, eval_str=True).parameters
313
+ if len(parameters) >= 2:
314
+ second_param = list(parameters.values())[1]
315
+ ty = second_param.annotation
316
+ return ty if ty != inspect._empty else None
317
+ return None
328
318
 
329
319
  def resolve(self, cls: type) -> None:
330
320
  """Resolve the function."""
@@ -5,7 +5,7 @@ from __future__ import annotations
5
5
  import unittest
6
6
  from contextvars import ContextVar
7
7
  from dataclasses import MISSING
8
- from typing import Any, Callable, Optional, cast
8
+ from typing import Any, Callable, ClassVar, Optional, cast
9
9
  from uuid import UUID
10
10
 
11
11
  from .architype import NodeAnchor, Root
@@ -13,13 +13,7 @@ from .memory import Memory, ShelfStorage
13
13
 
14
14
 
15
15
  EXECUTION_CONTEXT = ContextVar[Optional["ExecutionContext"]]("ExecutionContext")
16
-
17
16
  SUPER_ROOT_UUID = UUID("00000000-0000-0000-0000-000000000000")
18
- SUPER_ROOT_ARCHITYPE = object.__new__(Root)
19
- SUPER_ROOT_ANCHOR = NodeAnchor(
20
- id=SUPER_ROOT_UUID, architype=SUPER_ROOT_ARCHITYPE, persistent=False, edges=[]
21
- )
22
- SUPER_ROOT_ARCHITYPE.__jac__ = SUPER_ROOT_ANCHOR
23
17
 
24
18
 
25
19
  class ExecutionContext:
@@ -32,6 +26,9 @@ class ExecutionContext:
32
26
  root: NodeAnchor
33
27
  entry_node: NodeAnchor
34
28
 
29
+ # A context change event subscription list, those who want to listen ctx change will register here.
30
+ on_ctx_change: ClassVar[list[Callable[[ExecutionContext | None], None]]] = []
31
+
35
32
  def init_anchor(
36
33
  self,
37
34
  anchor_id: str | None,
@@ -52,6 +49,8 @@ class ExecutionContext:
52
49
  """Close current ExecutionContext."""
53
50
  self.mem.close()
54
51
  EXECUTION_CONTEXT.set(None)
52
+ for func in ExecutionContext.on_ctx_change:
53
+ func(EXECUTION_CONTEXT.get(None))
55
54
 
56
55
  @staticmethod
57
56
  def create(
@@ -60,6 +59,8 @@ class ExecutionContext:
60
59
  auto_close: bool = True,
61
60
  ) -> ExecutionContext:
62
61
  """Create ExecutionContext."""
62
+ from jaclang import Root
63
+
63
64
  ctx = ExecutionContext()
64
65
  ctx.mem = ShelfStorage(session)
65
66
  ctx.reports = []
@@ -67,7 +68,7 @@ class ExecutionContext:
67
68
  if not isinstance(
68
69
  system_root := ctx.mem.find_by_id(SUPER_ROOT_UUID), NodeAnchor
69
70
  ):
70
- system_root = Root().__jac__
71
+ system_root = cast(NodeAnchor, Root().__jac__) # type: ignore[attr-defined]
71
72
  system_root.id = SUPER_ROOT_UUID
72
73
  ctx.mem.set(system_root.id, system_root)
73
74
 
@@ -79,6 +80,8 @@ class ExecutionContext:
79
80
  old_ctx.close()
80
81
 
81
82
  EXECUTION_CONTEXT.set(ctx)
83
+ for func in ExecutionContext.on_ctx_change:
84
+ func(EXECUTION_CONTEXT.get(None))
82
85
 
83
86
  return ctx
84
87
 
@@ -95,7 +98,20 @@ class ExecutionContext:
95
98
  if ctx := EXECUTION_CONTEXT.get(None):
96
99
  return cast(Root, ctx.root.architype)
97
100
 
98
- return SUPER_ROOT_ARCHITYPE
101
+ return cast(Root, ExecutionContext.global_system_root().architype)
102
+
103
+ @staticmethod
104
+ def global_system_root() -> NodeAnchor:
105
+ """Get global system root."""
106
+ from jaclang import Root
107
+
108
+ if not (sr_anch := getattr(ExecutionContext, "system_root", None)):
109
+ sr_arch = Root()
110
+ sr_anch = sr_arch.__jac__ # type: ignore[attr-defined]
111
+ sr_anch.id = SUPER_ROOT_UUID
112
+ sr_anch.persistent = False
113
+ ExecutionContext.system_root = sr_anch
114
+ return sr_anch
99
115
 
100
116
 
101
117
  class JacTestResult(unittest.TextTestResult):
@@ -158,9 +158,6 @@ class ImportReturn:
158
158
  return getattr(new_module, name, new_module)
159
159
  except ImportError as e:
160
160
  logger.error(dump_traceback(e))
161
- # logger.error(
162
- # f"Failed to load {name} from {jac_file_path} in {module.__name__}: {str(e)}"
163
- # )
164
161
  return None
165
162
 
166
163
 
@@ -319,6 +316,32 @@ class JacImporter(Importer):
319
316
  """Run the import process for Jac modules."""
320
317
  unique_loaded_items: list[types.ModuleType] = []
321
318
  module = None
319
+ # Gather all possible search paths
320
+ jacpaths = os.environ.get("JACPATH", "")
321
+ search_paths = [spec.caller_dir]
322
+ if jacpaths:
323
+ for p in jacpaths.split(os.pathsep):
324
+ p = p.strip()
325
+ if p and p not in search_paths:
326
+ search_paths.append(p)
327
+
328
+ # Attempt to locate the module file or directory
329
+ found_path = None
330
+ target_path_components = spec.target.split(".")
331
+ for search_path in search_paths:
332
+ candidate = os.path.join(search_path, "/".join(target_path_components))
333
+ # Check if the candidate is a directory or a .jac file
334
+ if (os.path.isdir(candidate)) or (os.path.isfile(candidate + ".jac")):
335
+ found_path = candidate
336
+ break
337
+
338
+ # If a suitable path was found, update spec.full_target; otherwise, raise an error
339
+ if found_path:
340
+ spec.full_target = os.path.abspath(found_path)
341
+ else:
342
+ raise ImportError(
343
+ f"Unable to locate module '{spec.target}' in {search_paths}"
344
+ )
322
345
  if os.path.isfile(spec.full_target + ".jac"):
323
346
  module_name = self.get_sys_mod_name(spec.full_target + ".jac")
324
347
  module_name = spec.override_name if spec.override_name else module_name
@@ -38,6 +38,7 @@ class JacMachine:
38
38
  self.loaded_modules: dict[str, types.ModuleType] = {}
39
39
  if not base_path:
40
40
  base_path = os.getcwd()
41
+ # Ensure the base_path is a list rather than a string
41
42
  self.base_path = base_path
42
43
  self.base_path_dir = (
43
44
  os.path.dirname(base_path)
@@ -306,12 +307,11 @@ class JacProgram:
306
307
  return marshal.load(f)
307
308
 
308
309
  result = compile_jac(full_target, cache_result=cachable)
309
- if result.errors_had or not result.ir.gen.py_bytecode:
310
+ if result.errors_had:
310
311
  for alrt in result.errors_had:
311
312
  # We're not logging here, it already gets logged as the errors were added to the errors_had list.
312
313
  # Regardless of the logging, this needs to be sent to the end user, so we'll printing it to stderr.
313
314
  logger.error(alrt.pretty_print())
314
- return None
315
315
  if result.ir.gen.py_bytecode is not None:
316
316
  return marshal.loads(result.ir.gen.py_bytecode)
317
317
  else:
jaclang/settings.py CHANGED
@@ -23,6 +23,8 @@ class Settings:
23
23
  disable_mtllm: bool = False
24
24
  ignore_test_annex: bool = False
25
25
  allow_import_from: bool = False
26
+ pyout_jaclib_import_all: bool = True
27
+ pyout_jaclib_alias = "jl"
26
28
 
27
29
  # Formatter configuration
28
30
  max_line_length: int = 88
@@ -13,7 +13,7 @@ print("Dynamic Node Value:", f'{self.value}');
13
13
  glob walker_code = """
14
14
  walker dynamic_walker {
15
15
  can visit_nodes with entry {
16
- visit [-->](`?dynamic_node);
16
+ visit [-->];
17
17
  }
18
18
  }
19
19
  """;