jaclang 0.7.26__py3-none-any.whl → 0.7.28__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 (47) hide show
  1. jaclang/cli/cli.py +3 -0
  2. jaclang/compiler/absyntree.py +18 -3
  3. jaclang/compiler/passes/main/__init__.py +1 -1
  4. jaclang/compiler/passes/main/access_modifier_pass.py +1 -1
  5. jaclang/compiler/passes/main/fuse_typeinfo_pass.py +62 -33
  6. jaclang/compiler/passes/main/import_pass.py +284 -63
  7. jaclang/compiler/passes/main/inheritance_pass.py +103 -0
  8. jaclang/compiler/passes/main/py_collect_dep_pass.py +5 -5
  9. jaclang/compiler/passes/main/pyast_load_pass.py +1 -1
  10. jaclang/compiler/passes/main/schedules.py +2 -0
  11. jaclang/compiler/passes/main/sym_tab_build_pass.py +17 -0
  12. jaclang/compiler/passes/main/tests/fixtures/data_spatial_types.jac +130 -0
  13. jaclang/compiler/passes/main/tests/fixtures/pygame_mock/__init__.py +3 -3
  14. jaclang/compiler/passes/main/tests/fixtures/pygame_mock/__init__.pyi +3 -3
  15. jaclang/compiler/passes/main/tests/test_import_pass.py +13 -17
  16. jaclang/compiler/passes/main/tests/test_type_check_pass.py +24 -0
  17. jaclang/compiler/passes/main/type_check_pass.py +3 -2
  18. jaclang/compiler/py_info.py +22 -0
  19. jaclang/compiler/symtable.py +9 -2
  20. jaclang/langserve/tests/test_server.py +2 -2
  21. jaclang/plugin/default.py +28 -11
  22. jaclang/plugin/feature.py +8 -1
  23. jaclang/plugin/spec.py +3 -2
  24. jaclang/plugin/tests/fixtures/other_root_access.jac +10 -0
  25. jaclang/plugin/tests/fixtures/savable_object.jac +84 -0
  26. jaclang/plugin/tests/test_jaseci.py +88 -0
  27. jaclang/runtimelib/architype.py +17 -0
  28. jaclang/runtimelib/memory.py +22 -7
  29. jaclang/runtimelib/test.py +59 -4
  30. jaclang/runtimelib/utils.py +16 -0
  31. jaclang/settings.py +3 -0
  32. jaclang/tests/fixtures/base_class1.jac +11 -0
  33. jaclang/tests/fixtures/base_class2.jac +11 -0
  34. jaclang/tests/fixtures/import_all.jac +7 -0
  35. jaclang/tests/fixtures/import_all_py.py +8 -0
  36. jaclang/tests/fixtures/jactest_imported.jac +6 -0
  37. jaclang/tests/fixtures/jactest_main.jac +22 -0
  38. jaclang/tests/fixtures/multi_dim_array_split.jac +2 -6
  39. jaclang/tests/fixtures/test_py.py +12 -0
  40. jaclang/tests/test_cli.py +82 -0
  41. jaclang/tests/test_language.py +7 -8
  42. jaclang/utils/helpers.py +9 -1
  43. jaclang/utils/treeprinter.py +6 -3
  44. {jaclang-0.7.26.dist-info → jaclang-0.7.28.dist-info}/METADATA +2 -2
  45. {jaclang-0.7.26.dist-info → jaclang-0.7.28.dist-info}/RECORD +47 -36
  46. {jaclang-0.7.26.dist-info → jaclang-0.7.28.dist-info}/WHEEL +1 -1
  47. {jaclang-0.7.26.dist-info → jaclang-0.7.28.dist-info}/entry_points.txt +0 -0
@@ -23,6 +23,16 @@ walker update_node {
23
23
  }
24
24
  }
25
25
 
26
+ walker update_target_node {
27
+ has val: int;
28
+ has node_id: str;
29
+
30
+ can enter with `root entry {
31
+ target_node = &(self.node_id);
32
+ target_node.val = self.val;
33
+ }
34
+ }
35
+
26
36
  walker update_node_forced {
27
37
  has val: int;
28
38
 
@@ -0,0 +1,84 @@
1
+ enum Enum {
2
+ A = "a",
3
+ B = "b",
4
+ C = "c"
5
+ }
6
+
7
+ obj Child {
8
+ has val: int, arr: list[int], json: dict[str, int], enum_field: Enum;
9
+ }
10
+
11
+ obj Parent:Child: {
12
+ has child: Child;
13
+ }
14
+
15
+ obj SavableObject {
16
+ has val: int, arr: list[int], json: dict[str, int], parent: Parent, enum_field: Enum;
17
+ }
18
+
19
+ walker create_custom_object {
20
+ can enter1 with `root entry {
21
+ o = SavableObject(
22
+ val=0,
23
+ arr=[],
24
+ json={},
25
+ parent=Parent(
26
+ val=1,
27
+ arr=[1],
28
+ json={"a": 1},
29
+ child=Child(
30
+ val=2,
31
+ arr=[1, 2],
32
+ json={"a": 1, "b": 2},
33
+ enum_field = Enum.C
34
+ ),
35
+ enum_field = Enum.B
36
+ ),
37
+ enum_field = Enum.A
38
+ );
39
+ Jac.save(o);
40
+ print(jid(o));
41
+ print(o);
42
+ }
43
+ }
44
+
45
+ walker get_custom_object {
46
+ has object_id: str;
47
+
48
+ can enter1 with `root entry {
49
+ try {
50
+ print(&(self.object_id));
51
+ } except Exception as e {
52
+ print(None);
53
+ }
54
+ }
55
+ }
56
+
57
+ walker update_custom_object {
58
+ has object_id: str;
59
+
60
+ can enter1 with `root entry {
61
+ savable_object = &(self.object_id);
62
+ savable_object.parent.child.json["c"] = 3;
63
+ savable_object.parent.child.arr.append(3);
64
+ savable_object.parent.child.val = 3;
65
+ savable_object.parent.child.enum_field = Enum.A;
66
+ savable_object.parent.json["b"] = 2;
67
+ savable_object.parent.arr.append(2);
68
+ savable_object.parent.val = 2;
69
+ savable_object.parent.enum_field = Enum.C;
70
+ savable_object.json["a"] = 1;
71
+ savable_object.arr.append(1);
72
+ savable_object.val = 1;
73
+ savable_object.enum_field = Enum.B;
74
+ print(savable_object);
75
+ }
76
+ }
77
+
78
+ walker delete_custom_object {
79
+ has object_id: str;
80
+
81
+ can enter1 with `root entry {
82
+ Jac.destroy(&(self.object_id));
83
+ }
84
+ }
@@ -385,6 +385,22 @@ class TestJaseciPlugin(TestCase):
385
385
  node=self.nodes[0],
386
386
  )
387
387
 
388
+ cli.enter(
389
+ filename=self.fixture_abs_path("other_root_access.jac"),
390
+ entrypoint="update_target_node",
391
+ args=[20, self.nodes[1]],
392
+ session=session,
393
+ root=self.roots[0],
394
+ )
395
+
396
+ cli.enter(
397
+ filename=self.fixture_abs_path("other_root_access.jac"),
398
+ entrypoint="update_target_node",
399
+ args=[10, self.nodes[0]],
400
+ session=session,
401
+ root=self.roots[1],
402
+ )
403
+
388
404
  cli.enter(
389
405
  filename=self.fixture_abs_path("other_root_access.jac"),
390
406
  entrypoint="check_node",
@@ -728,3 +744,75 @@ class TestJaseciPlugin(TestCase):
728
744
  )
729
745
 
730
746
  self._del_session(session)
747
+
748
+ def test_savable_object(self) -> None:
749
+ """Test ObjectAnchor save."""
750
+ global session
751
+ session = self.fixture_abs_path("other_root_access.session")
752
+
753
+ self._output2buffer()
754
+
755
+ cli.enter(
756
+ filename=self.fixture_abs_path("savable_object.jac"),
757
+ entrypoint="create_custom_object",
758
+ args=[],
759
+ session=session,
760
+ )
761
+
762
+ prints = self.capturedOutput.getvalue().strip().split("\n")
763
+ id = prints[0]
764
+
765
+ self.assertEqual(
766
+ "SavableObject(val=0, arr=[], json={}, parent=Parent(val=1, arr=[1], json"
767
+ "={'a': 1}, enum_field=<Enum.B: 'b'>, child=Child(val=2, arr=[1, 2], json"
768
+ "={'a': 1, 'b': 2}, enum_field=<Enum.C: 'c'>)), enum_field=<Enum.A: 'a'>)",
769
+ prints[1],
770
+ )
771
+
772
+ self._output2buffer()
773
+
774
+ cli.enter(
775
+ filename=self.fixture_abs_path("savable_object.jac"),
776
+ entrypoint="get_custom_object",
777
+ args=[id],
778
+ session=session,
779
+ )
780
+ self.assertEqual(
781
+ "SavableObject(val=0, arr=[], json={}, parent=Parent(val=1, arr=[1], json"
782
+ "={'a': 1}, enum_field=<Enum.B: 'b'>, child=Child(val=2, arr=[1, 2], json"
783
+ "={'a': 1, 'b': 2}, enum_field=<Enum.C: 'c'>)), enum_field=<Enum.A: 'a'>)",
784
+ self.capturedOutput.getvalue().strip(),
785
+ )
786
+
787
+ self._output2buffer()
788
+
789
+ cli.enter(
790
+ filename=self.fixture_abs_path("savable_object.jac"),
791
+ entrypoint="update_custom_object",
792
+ args=[id],
793
+ session=session,
794
+ )
795
+
796
+ self.assertEqual(
797
+ "SavableObject(val=1, arr=[1], json={'a': 1}, parent=Parent(val=2, arr=[1, 2], json"
798
+ "={'a': 1, 'b': 2}, enum_field=<Enum.C: 'c'>, child=Child(val=3, arr=[1, 2, 3], json"
799
+ "={'a': 1, 'b': 2, 'c': 3}, enum_field=<Enum.A: 'a'>)), enum_field=<Enum.B: 'b'>)",
800
+ self.capturedOutput.getvalue().strip(),
801
+ )
802
+
803
+ self._output2buffer()
804
+
805
+ cli.enter(
806
+ filename=self.fixture_abs_path("savable_object.jac"),
807
+ entrypoint="delete_custom_object",
808
+ args=[id],
809
+ session=session,
810
+ )
811
+
812
+ cli.enter(
813
+ filename=self.fixture_abs_path("savable_object.jac"),
814
+ entrypoint="get_custom_object",
815
+ args=[id],
816
+ session=session,
817
+ )
818
+ self.assertEqual("None", self.capturedOutput.getvalue().strip())
@@ -226,6 +226,13 @@ class WalkerAnchor(Anchor):
226
226
  disengaged: bool = False
227
227
 
228
228
 
229
+ @dataclass(eq=False, repr=False, kw_only=True)
230
+ class ObjectAnchor(Anchor):
231
+ """Edge Anchor."""
232
+
233
+ architype: ObjectArchitype
234
+
235
+
229
236
  class Architype:
230
237
  """Architype Protocol."""
231
238
 
@@ -267,6 +274,16 @@ class WalkerArchitype(Architype):
267
274
  self.__jac__ = WalkerAnchor(architype=self)
268
275
 
269
276
 
277
+ class ObjectArchitype(Architype):
278
+ """Walker Architype Protocol."""
279
+
280
+ __jac__: ObjectAnchor
281
+
282
+ def __init__(self) -> None:
283
+ """Create walker architype."""
284
+ self.__jac__ = ObjectAnchor(architype=self)
285
+
286
+
270
287
  @dataclass(eq=False)
271
288
  class GenericEdge(EdgeArchitype):
272
289
  """Generic Root Node."""
@@ -84,14 +84,32 @@ class ShelfStorage(Memory[UUID, Anchor]):
84
84
  def close(self) -> None:
85
85
  """Close memory handler."""
86
86
  if isinstance(self.__shelf__, Shelf):
87
- from jaclang.plugin.feature import JacFeature as Jac
88
-
89
87
  for anchor in self.__gc__:
90
88
  self.__shelf__.pop(str(anchor.id), None)
91
89
  self.__mem__.pop(anchor.id, None)
92
90
 
93
- for d in self.__mem__.values():
94
- if d.persistent and d.hash != hash(dumps(d)):
91
+ keys = set(self.__mem__.keys())
92
+
93
+ # current memory
94
+ self.sync_mem_to_db(keys)
95
+
96
+ # additional after memory sync
97
+ self.sync_mem_to_db(set(self.__mem__.keys() - keys))
98
+
99
+ self.__shelf__.close()
100
+ super().close()
101
+
102
+ def sync_mem_to_db(self, keys: Iterable[UUID]) -> None:
103
+ """Manually sync memory to db."""
104
+ from jaclang.plugin.feature import JacFeature as Jac
105
+
106
+ if isinstance(self.__shelf__, Shelf):
107
+ for key in keys:
108
+ if (
109
+ (d := self.__mem__.get(key))
110
+ and d.persistent
111
+ and d.hash != hash(dumps(d))
112
+ ):
95
113
  _id = str(d.id)
96
114
  if p_d := self.__shelf__.get(_id):
97
115
  if (
@@ -119,9 +137,6 @@ class ShelfStorage(Memory[UUID, Anchor]):
119
137
  ):
120
138
  self.__shelf__[_id] = d
121
139
 
122
- self.__shelf__.close()
123
- super().close()
124
-
125
140
  def find(
126
141
  self,
127
142
  ids: UUID | Iterable[UUID],
@@ -3,6 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import unittest
6
+ from dataclasses import dataclass
6
7
  from typing import Callable, Optional
7
8
 
8
9
 
@@ -56,6 +57,16 @@ class JacTestCheck:
56
57
 
57
58
  test_case = unittest.TestCase()
58
59
  test_suite = unittest.TestSuite()
60
+
61
+ @dataclass
62
+ class TestSuite:
63
+ """Test Suite."""
64
+
65
+ test_case: unittest.FunctionTestCase
66
+ func_name: str
67
+
68
+ test_suite_path: dict[str, list[TestSuite]] = {}
69
+
59
70
  breaker = False
60
71
  failcount = 0
61
72
 
@@ -64,13 +75,45 @@ class JacTestCheck:
64
75
  """Clear the test suite."""
65
76
  JacTestCheck.test_case = unittest.TestCase()
66
77
  JacTestCheck.test_suite = unittest.TestSuite()
78
+ JacTestCheck.test_suite_path = {}
67
79
 
68
80
  @staticmethod
69
- def run_test(xit: bool, maxfail: int | None, verbose: bool) -> None:
81
+ def run_test(
82
+ xit: bool,
83
+ maxfail: int | None,
84
+ verbose: bool,
85
+ filepath: str | None,
86
+ func_name: str | None,
87
+ ) -> None:
70
88
  """Run the test suite."""
71
89
  verb = 2 if verbose else 1
90
+ test_suite = JacTestCheck.test_suite
91
+
92
+ if filepath and filepath.endswith(".test.jac"):
93
+ filepath = filepath[:-9]
94
+ elif filepath and filepath.endswith(".jac"):
95
+ filepath = filepath[:-4]
96
+
97
+ if filepath:
98
+ test_cases = JacTestCheck.test_suite_path.get(filepath)
99
+ if test_cases is not None:
100
+ test_suite = unittest.TestSuite()
101
+ for test_case in test_cases:
102
+ if func_name:
103
+ if test_case.func_name == func_name:
104
+ test_suite.addTest(test_case.test_case)
105
+ else:
106
+ test_suite.addTest(test_case.test_case)
107
+
108
+ elif func_name:
109
+ test_suite = unittest.TestSuite()
110
+ for test_cases in JacTestCheck.test_suite_path.values():
111
+ for test_case in test_cases:
112
+ if test_case.func_name == func_name:
113
+ test_suite.addTest(test_case.test_case)
114
+
72
115
  runner = JacTextTestRunner(max_failures=maxfail, failfast=xit, verbosity=verb)
73
- result = runner.run(JacTestCheck.test_suite)
116
+ result = runner.run(test_suite)
74
117
  if result.wasSuccessful():
75
118
  print("Passed successfully.")
76
119
  else:
@@ -81,9 +124,21 @@ class JacTestCheck:
81
124
  )
82
125
 
83
126
  @staticmethod
84
- def add_test(test_fun: Callable) -> None:
127
+ def add_test(filepath: str, func_name: str, test_func: Callable) -> None:
85
128
  """Create a new test."""
86
- JacTestCheck.test_suite.addTest(unittest.FunctionTestCase(test_fun))
129
+ if filepath and filepath.endswith(".test.jac"):
130
+ filepath = filepath[:-9]
131
+ elif filepath and filepath.endswith(".jac"):
132
+ filepath = filepath[:-4]
133
+
134
+ if filepath not in JacTestCheck.test_suite_path:
135
+ JacTestCheck.test_suite_path[filepath] = []
136
+
137
+ test_case = unittest.FunctionTestCase(test_func)
138
+ JacTestCheck.test_suite_path[filepath].append(
139
+ JacTestCheck.TestSuite(test_case=test_case, func_name=func_name)
140
+ )
141
+ JacTestCheck.test_suite.addTest(test_case)
87
142
 
88
143
  def __getattr__(self, name: str) -> object:
89
144
  """Make convenient check.Equal(...) etc."""
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
  import ast as ast3
6
6
  import sys
7
7
  from contextlib import contextmanager
8
+ from types import UnionType
8
9
  from typing import Callable, Iterator, TYPE_CHECKING
9
10
 
10
11
  import jaclang.compiler.absyntree as ast
@@ -215,3 +216,18 @@ def extract_params(
215
216
  )
216
217
  exclude_info.append((var_name, i.gen.py_ast[0]))
217
218
  return model_params, include_info, exclude_info
219
+
220
+
221
+ def is_instance(
222
+ obj: object, target: type | UnionType | tuple[type | UnionType, ...]
223
+ ) -> bool:
224
+ """Check if object is instance of target type."""
225
+ match target:
226
+ case UnionType():
227
+ return any((is_instance(obj, trg) for trg in target.__args__))
228
+ case tuple():
229
+ return any((is_instance(obj, trg) for trg in target))
230
+ case type():
231
+ return isinstance(obj, target)
232
+ case _:
233
+ return False
jaclang/settings.py CHANGED
@@ -16,10 +16,13 @@ class Settings:
16
16
  pass_timer: bool = False
17
17
  collect_py_dep_debug: bool = False
18
18
  print_py_raised_ast: bool = False
19
+ py_import_pass_debug: bool = False
20
+ inherit_pass_debug: bool = False
19
21
 
20
22
  # Compiler configuration
21
23
  disable_mtllm: bool = False
22
24
  ignore_test_annex: bool = False
25
+ allow_import_from: bool = False
23
26
 
24
27
  # Formatter configuration
25
28
  max_line_length: int = 88
@@ -0,0 +1,11 @@
1
+ import:py test_py;
2
+
3
+ class B :test_py.A: {}
4
+
5
+ with entry {
6
+ a = test_py.A();
7
+ b = B();
8
+
9
+ a.start();
10
+ b.start();
11
+ }
@@ -0,0 +1,11 @@
1
+ import:py from test_py { A }
2
+
3
+ class B :A: {}
4
+
5
+ with entry {
6
+ a = A();
7
+ b = B();
8
+
9
+ a.start();
10
+ b.start();
11
+ }
@@ -0,0 +1,7 @@
1
+ import:py import_all_py;
2
+
3
+ with entry {
4
+ print(import_all_py.custom_func(11));
5
+ print(import_all_py.pi);
6
+ print(import_all_py.floor(0.5));
7
+ }
@@ -0,0 +1,8 @@
1
+ """Module used for testing from x import * in jac."""
2
+
3
+ from math import * # noqa
4
+
5
+
6
+ def custom_func(x: int) -> str:
7
+ """Dummy custom function for testing purposes.""" # noqa
8
+ return str(x)
@@ -0,0 +1,6 @@
1
+
2
+
3
+ test this_should_not_run {
4
+ print("This test should not run after import.");
5
+ assert False;
6
+ }
@@ -0,0 +1,22 @@
1
+ import jactest_imported;
2
+
3
+ can fib(n: int) -> int {
4
+ if n <= 1 {
5
+ return n;
6
+ }
7
+ return fib(n - 1) + fib(n - 2);
8
+ }
9
+
10
+
11
+ test first_two {
12
+ print("Testing first 2 fibonacci numbers.");
13
+ assert fib(0) == 0;
14
+ assert fib(1) == 0;
15
+ }
16
+
17
+ test from_2_to_10 {
18
+ print("Testing fibonacci numbers from 2 to 10.");
19
+ for i in range(2, 10) {
20
+ assert fib(i) == fib(i - 1) + fib(i - 2);
21
+ }
22
+ }
@@ -1,13 +1,9 @@
1
- import:py numpy as np;
2
-
3
1
  with entry {
4
- arr = np.array(
5
- [[1, 2, 3, 4, 5],
2
+ arr = [[1, 2, 3, 4, 5],
6
3
  [6, 7, 8, 9, 10],
7
4
  [11, 12, 13, 14, 15],
8
5
  [16, 17, 18, 19, 20],
9
- [21, 22, 23, 24, 25]]
10
- );
6
+ [21, 22, 23, 24, 25]];
11
7
 
12
8
  print('Original Array:');
13
9
  print(arr);
@@ -0,0 +1,12 @@
1
+ """Test file for subclass issue."""
2
+
3
+ p = 5
4
+ g = 6
5
+
6
+
7
+ class A:
8
+ """Dummy class to test the base class issue."""
9
+
10
+ def start(self) -> int:
11
+ """Return 0."""
12
+ return 0
jaclang/tests/test_cli.py CHANGED
@@ -233,6 +233,67 @@ class JacCliTests(TestCase):
233
233
  r"13\:12 \- 13\:18.*Name - append - .*SymbolPath: builtins_test.builtins.list.append",
234
234
  )
235
235
 
236
+ def test_import_all(self) -> None:
237
+ """Testing for print AstTool."""
238
+ from jaclang.settings import settings
239
+
240
+ settings.ast_symbol_info_detailed = True
241
+ captured_output = io.StringIO()
242
+ sys.stdout = captured_output
243
+
244
+ cli.tool("ir", ["ast", f"{self.fixture_abs_path('import_all.jac')}"])
245
+
246
+ sys.stdout = sys.__stdout__
247
+ stdout_value = captured_output.getvalue()
248
+ settings.ast_symbol_info_detailed = False
249
+
250
+ self.assertRegex(
251
+ stdout_value,
252
+ r"6\:25 - 6\:30.*Name - floor -.*SymbolPath: import_all.import_all_py.floor",
253
+ )
254
+ self.assertRegex(
255
+ stdout_value,
256
+ r"5\:25 - 5\:27.*Name - pi -.*SymbolPath: import_all.import_all_py.pi",
257
+ )
258
+
259
+ def test_sub_class_symbol_table_fix_1(self) -> None:
260
+ """Testing for print AstTool."""
261
+ from jaclang.settings import settings
262
+
263
+ settings.ast_symbol_info_detailed = True
264
+ captured_output = io.StringIO()
265
+ sys.stdout = captured_output
266
+
267
+ cli.tool("ir", ["ast", f"{self.fixture_abs_path('base_class1.jac')}"])
268
+
269
+ sys.stdout = sys.__stdout__
270
+ stdout_value = captured_output.getvalue()
271
+ settings.ast_symbol_info_detailed = False
272
+
273
+ self.assertRegex(
274
+ stdout_value,
275
+ r"10:7 - 10:12.*Name - start - Type.*SymbolPath: base_class1.B.start",
276
+ )
277
+
278
+ def test_sub_class_symbol_table_fix_2(self) -> None:
279
+ """Testing for print AstTool."""
280
+ from jaclang.settings import settings
281
+
282
+ settings.ast_symbol_info_detailed = True
283
+ captured_output = io.StringIO()
284
+ sys.stdout = captured_output
285
+
286
+ cli.tool("ir", ["ast", f"{self.fixture_abs_path('base_class2.jac')}"])
287
+
288
+ sys.stdout = sys.__stdout__
289
+ stdout_value = captured_output.getvalue()
290
+ settings.ast_symbol_info_detailed = False
291
+
292
+ self.assertRegex(
293
+ stdout_value,
294
+ r"10:7 - 10:12.*Name - start - Type.*SymbolPath: base_class2.B.start",
295
+ )
296
+
236
297
  def test_expr_types(self) -> None:
237
298
  """Testing for print AstTool."""
238
299
  captured_output = io.StringIO()
@@ -393,6 +454,27 @@ class JacCliTests(TestCase):
393
454
  self.assertIn("...F", stderr)
394
455
  self.assertIn("F.F", stderr)
395
456
 
457
+ def test_run_specific_test_only(self) -> None:
458
+ """Test a specific test case."""
459
+ process = subprocess.Popen(
460
+ [
461
+ "jac",
462
+ "test",
463
+ "-t",
464
+ "from_2_to_10",
465
+ self.fixture_abs_path("jactest_main.jac"),
466
+ ],
467
+ stdin=subprocess.PIPE,
468
+ stdout=subprocess.PIPE,
469
+ stderr=subprocess.PIPE,
470
+ text=True,
471
+ )
472
+ stdout, stderr = process.communicate()
473
+ self.assertIn("Ran 1 test", stderr)
474
+ self.assertIn("Testing fibonacci numbers from 2 to 10.", stdout)
475
+ self.assertNotIn("Testing first 2 fibonacci numbers.", stdout)
476
+ self.assertNotIn("This test should not run after import.", stdout)
477
+
396
478
  def test_graph_coverage(self) -> None:
397
479
  """Test for coverage of graph cmd."""
398
480
  graph_params = set(inspect.signature(cli.dot).parameters.keys())
@@ -117,11 +117,10 @@ class JacLanguageTests(TestCase):
117
117
  sys.stdout = sys.__stdout__
118
118
  stdout_value = captured_output.getvalue()
119
119
 
120
- # print(arr[1:3, 1::2]);
121
120
  expected_outputs = [
122
- "+-- AtomTrailer - Type: Any",
123
- " +-- Name - arr - Type: Any, SymbolTable: None",
124
- " +-- IndexSlice - [IndexSlice] - Type: builtins.slice, SymbolTable: None",
121
+ "+-- AtomTrailer - Type: builtins.list[builtins.int]",
122
+ " +-- Name - arr - Type: builtins.list[builtins.list[builtins.int]], SymbolTable: list",
123
+ " +-- IndexSlice - [IndexSlice] - Type: builtins.list[builtins.list[builtins.int]], SymbolTable: None",
125
124
  " +-- Token - [,",
126
125
  " +-- Int - 1 - Type: Literal[1]?, SymbolTable: None",
127
126
  " +-- Token - :,",
@@ -549,7 +548,7 @@ class JacLanguageTests(TestCase):
549
548
  return f"Error While Jac to Py AST conversion: {e}"
550
549
 
551
550
  ir = jac_pass_to_pass(py_ast_build_pass, schedule=py_code_gen_typed).ir
552
- self.assertEqual(len(ir.get_all_sub_nodes(ast.Architype)), 7)
551
+ self.assertEqual(len(ir.get_all_sub_nodes(ast.Architype)), 21)
553
552
  captured_output = io.StringIO()
554
553
  sys.stdout = captured_output
555
554
  jac_import("needs_import_1", base_path=self.fixture_abs_path("./"))
@@ -612,7 +611,7 @@ class JacLanguageTests(TestCase):
612
611
 
613
612
  ir = jac_pass_to_pass(py_ast_build_pass, schedule=py_code_gen_typed).ir
614
613
  self.assertEqual(
615
- len(ir.get_all_sub_nodes(ast.Architype)), 8
614
+ len(ir.get_all_sub_nodes(ast.Architype)), 27
616
615
  ) # Because of the Architype from math
617
616
  captured_output = io.StringIO()
618
617
  sys.stdout = captured_output
@@ -666,7 +665,7 @@ class JacLanguageTests(TestCase):
666
665
 
667
666
  ir = jac_pass_to_pass(py_ast_build_pass, schedule=py_code_gen_typed).ir
668
667
  self.assertEqual(
669
- len(ir.get_all_sub_nodes(ast.Architype)), 38
668
+ len(ir.get_all_sub_nodes(ast.Architype)), 75
670
669
  ) # Because of the Architype from other imports
671
670
  captured_output = io.StringIO()
672
671
  sys.stdout = captured_output
@@ -874,7 +873,7 @@ class JacLanguageTests(TestCase):
874
873
  ir = jac_pass_to_pass(py_ast_build_pass, schedule=py_code_gen_typed).ir
875
874
  jac_ast = ir.pp()
876
875
  self.assertIn(" | +-- String - 'Loop completed normally{}'", jac_ast)
877
- self.assertEqual(len(ir.get_all_sub_nodes(ast.SubNodeList)), 269)
876
+ self.assertEqual(len(ir.get_all_sub_nodes(ast.SubNodeList)), 586)
878
877
  captured_output = io.StringIO()
879
878
  sys.stdout = captured_output
880
879
  jac_import("deep_convert", base_path=self.fixture_abs_path("./"))
jaclang/utils/helpers.py CHANGED
@@ -165,7 +165,15 @@ def dump_traceback(e: Exception) -> str:
165
165
  (frame.lineno is not None) and frame.line and frame.line.strip() != ""
166
166
  ):
167
167
 
168
- line_o = frame._original_line.rstrip() # type: ignore [attr-defined]
168
+ # Note: This is CPython internals we're trying to get since python doesn't provide
169
+ # the frames original line but the stripped version so we had to do this.
170
+ line_o = frame.line # Fallback line.
171
+ if hasattr(frame, "_original_line"):
172
+ line_o = frame._original_line.rstrip() # type: ignore [attr-defined]
173
+ elif hasattr(frame, "_original_lines"):
174
+ # https://github.com/python/cpython/issues/106922
175
+ line_o = frame._original_lines.split("\n")[0].rstrip() # type: ignore [attr-defined]
176
+
169
177
  if frame.colno is not None and frame.end_colno is not None:
170
178
  off_start = byte_offset_to_char_offset(line_o, frame.colno) - 1
171
179
  off_end = byte_offset_to_char_offset(line_o, frame.end_colno) - 1