jaclang 0.7.27__py3-none-any.whl → 0.7.29__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 (44) 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 +82 -50
  22. jaclang/plugin/feature.py +2 -0
  23. jaclang/plugin/spec.py +1 -0
  24. jaclang/runtimelib/architype.py +18 -14
  25. jaclang/runtimelib/test.py +59 -4
  26. jaclang/runtimelib/utils.py +15 -0
  27. jaclang/settings.py +3 -0
  28. jaclang/tests/fixtures/base_class1.jac +11 -0
  29. jaclang/tests/fixtures/base_class2.jac +11 -0
  30. jaclang/tests/fixtures/import_all.jac +7 -0
  31. jaclang/tests/fixtures/import_all_py.py +8 -0
  32. jaclang/tests/fixtures/jactest_imported.jac +6 -0
  33. jaclang/tests/fixtures/jactest_main.jac +22 -0
  34. jaclang/tests/fixtures/multi_dim_array_split.jac +2 -6
  35. jaclang/tests/fixtures/test_py.py +12 -0
  36. jaclang/tests/fixtures/visit_sequence.jac +50 -0
  37. jaclang/tests/test_cli.py +82 -0
  38. jaclang/tests/test_language.py +24 -9
  39. jaclang/utils/helpers.py +9 -1
  40. jaclang/utils/treeprinter.py +6 -3
  41. {jaclang-0.7.27.dist-info → jaclang-0.7.29.dist-info}/METADATA +2 -2
  42. {jaclang-0.7.27.dist-info → jaclang-0.7.29.dist-info}/RECORD +44 -33
  43. {jaclang-0.7.27.dist-info → jaclang-0.7.29.dist-info}/WHEEL +1 -1
  44. {jaclang-0.7.27.dist-info → jaclang-0.7.29.dist-info}/entry_points.txt +0 -0
jaclang/plugin/default.py CHANGED
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
  import ast as ast3
6
6
  import fnmatch
7
7
  import html
8
+ import inspect
8
9
  import os
9
10
  import types
10
11
  from collections import OrderedDict
@@ -42,8 +43,11 @@ from jaclang.runtimelib.constructs import (
42
43
  from jaclang.runtimelib.importer import ImportPathSpec, JacImporter, PythonImporter
43
44
  from jaclang.runtimelib.machine import JacMachine, JacProgram
44
45
  from jaclang.runtimelib.memory import Shelf, ShelfStorage
45
- from jaclang.runtimelib.utils import collect_node_connections, traverse_graph
46
-
46
+ from jaclang.runtimelib.utils import (
47
+ all_issubclass,
48
+ collect_node_connections,
49
+ traverse_graph,
50
+ )
47
51
 
48
52
  import pluggy
49
53
 
@@ -398,64 +402,85 @@ class JacWalkerImpl:
398
402
 
399
403
  walker.path = []
400
404
  walker.next = [node]
401
- if walker.next:
402
- current_node = walker.next[-1].architype
403
- for i in warch._jac_entry_funcs_:
404
- trigger = i.get_funcparam_annotations(i.func)
405
- if not trigger:
406
- if i.func:
407
- i.func(warch, current_node)
408
- else:
409
- raise ValueError(f"No function {i.name} to call.")
405
+ current_node = node.architype
406
+
407
+ # walker entry
408
+ for i in warch._jac_entry_funcs_:
409
+ if i.func and not i.trigger:
410
+ i.func(warch, current_node)
411
+ if walker.disengaged:
412
+ return warch
413
+
410
414
  while len(walker.next):
411
415
  if current_node := walker.next.pop(0).architype:
416
+ # walker entry with
417
+ for i in warch._jac_entry_funcs_:
418
+ if (
419
+ i.func
420
+ and i.trigger
421
+ and all_issubclass(i.trigger, NodeArchitype)
422
+ and isinstance(current_node, i.trigger)
423
+ ):
424
+ i.func(warch, current_node)
425
+ if walker.disengaged:
426
+ return warch
427
+
428
+ # node entry
412
429
  for i in current_node._jac_entry_funcs_:
413
- trigger = i.get_funcparam_annotations(i.func)
414
- if not trigger or isinstance(warch, trigger):
415
- if i.func:
416
- i.func(current_node, warch)
417
- else:
418
- raise ValueError(f"No function {i.name} to call.")
430
+ if i.func and not i.trigger:
431
+ i.func(current_node, warch)
419
432
  if walker.disengaged:
420
433
  return warch
421
- for i in warch._jac_entry_funcs_:
422
- trigger = i.get_funcparam_annotations(i.func)
423
- if not trigger or isinstance(current_node, trigger):
424
- if i.func and trigger:
425
- i.func(warch, current_node)
426
- elif not trigger:
427
- continue
428
- else:
429
- raise ValueError(f"No function {i.name} to call.")
434
+
435
+ # node entry with
436
+ for i in current_node._jac_entry_funcs_:
437
+ if (
438
+ i.func
439
+ and i.trigger
440
+ and all_issubclass(i.trigger, WalkerArchitype)
441
+ and isinstance(warch, i.trigger)
442
+ ):
443
+ i.func(current_node, warch)
430
444
  if walker.disengaged:
431
445
  return warch
432
- for i in warch._jac_exit_funcs_:
433
- trigger = i.get_funcparam_annotations(i.func)
434
- if not trigger or isinstance(current_node, trigger):
435
- if i.func and trigger:
436
- i.func(warch, current_node)
437
- elif not trigger:
438
- continue
439
- else:
440
- raise ValueError(f"No function {i.name} to call.")
446
+
447
+ # node exit with
448
+ for i in current_node._jac_exit_funcs_:
449
+ if (
450
+ i.func
451
+ and i.trigger
452
+ and all_issubclass(i.trigger, WalkerArchitype)
453
+ and isinstance(warch, i.trigger)
454
+ ):
455
+ i.func(current_node, warch)
441
456
  if walker.disengaged:
442
457
  return warch
458
+
459
+ # node exit
443
460
  for i in current_node._jac_exit_funcs_:
444
- trigger = i.get_funcparam_annotations(i.func)
445
- if not trigger or isinstance(warch, trigger):
446
- if i.func:
447
- i.func(current_node, warch)
448
- else:
449
- raise ValueError(f"No function {i.name} to call.")
461
+ if i.func and not i.trigger:
462
+ i.func(current_node, warch)
450
463
  if walker.disengaged:
451
464
  return warch
465
+
466
+ # walker exit with
467
+ for i in warch._jac_exit_funcs_:
468
+ if (
469
+ i.func
470
+ and i.trigger
471
+ and all_issubclass(i.trigger, NodeArchitype)
472
+ and isinstance(current_node, i.trigger)
473
+ ):
474
+ i.func(warch, current_node)
475
+ if walker.disengaged:
476
+ return warch
477
+ # walker exit
452
478
  for i in warch._jac_exit_funcs_:
453
- trigger = i.get_funcparam_annotations(i.func)
454
- if not trigger:
455
- if i.func:
456
- i.func(warch, current_node)
457
- else:
458
- raise ValueError(f"No function {i.name} to call.")
479
+ if i.func and not i.trigger:
480
+ i.func(warch, current_node)
481
+ if walker.disengaged:
482
+ return warch
483
+
459
484
  walker.ignores = []
460
485
  return warch
461
486
 
@@ -818,12 +843,14 @@ class JacFeatureImpl(
818
843
  @hookimpl
819
844
  def create_test(test_fun: Callable) -> Callable:
820
845
  """Create a new test."""
846
+ file_path = inspect.getfile(test_fun)
847
+ func_name = test_fun.__name__
821
848
 
822
849
  def test_deco() -> None:
823
850
  test_fun(JacTestCheck())
824
851
 
825
852
  test_deco.__name__ = test_fun.__name__
826
- JacTestCheck.add_test(test_deco)
853
+ JacTestCheck.add_test(file_path, func_name, test_deco)
827
854
 
828
855
  return test_deco
829
856
 
@@ -831,6 +858,7 @@ class JacFeatureImpl(
831
858
  @hookimpl
832
859
  def run_test(
833
860
  filepath: str,
861
+ func_name: Optional[str],
834
862
  filter: Optional[str],
835
863
  xit: bool,
836
864
  maxfail: Optional[int],
@@ -849,7 +877,9 @@ class JacFeatureImpl(
849
877
  mod_name = mod_name[:-5]
850
878
  JacTestCheck.reset()
851
879
  Jac.jac_import(target=mod_name, base_path=base, cachable=False)
852
- JacTestCheck.run_test(xit, maxfail, verbose)
880
+ JacTestCheck.run_test(
881
+ xit, maxfail, verbose, os.path.abspath(filepath), func_name
882
+ )
853
883
  ret_count = JacTestCheck.failcount
854
884
  else:
855
885
  print("Not a .jac file.")
@@ -875,7 +905,9 @@ class JacFeatureImpl(
875
905
  print(f"\n\n\t\t* Inside {root_dir}" + "/" + f"{file} *")
876
906
  JacTestCheck.reset()
877
907
  Jac.jac_import(target=file[:-4], base_path=root_dir)
878
- JacTestCheck.run_test(xit, maxfail, verbose)
908
+ JacTestCheck.run_test(
909
+ xit, maxfail, verbose, os.path.abspath(file), func_name
910
+ )
879
911
 
880
912
  if JacTestCheck.breaker and (xit or maxfail):
881
913
  break
jaclang/plugin/feature.py CHANGED
@@ -359,6 +359,7 @@ class JacFeature(
359
359
  @staticmethod
360
360
  def run_test(
361
361
  filepath: str,
362
+ func_name: Optional[str] = None,
362
363
  filter: Optional[str] = None,
363
364
  xit: bool = False,
364
365
  maxfail: Optional[int] = None,
@@ -368,6 +369,7 @@ class JacFeature(
368
369
  """Run the test suite in the specified .jac file."""
369
370
  return plugin_manager.hook.run_test(
370
371
  filepath=filepath,
372
+ func_name=func_name,
371
373
  filter=filter,
372
374
  xit=xit,
373
375
  maxfail=maxfail,
jaclang/plugin/spec.py CHANGED
@@ -345,6 +345,7 @@ class JacFeatureSpec(
345
345
  @hookspec(firstresult=True)
346
346
  def run_test(
347
347
  filepath: str,
348
+ func_name: Optional[str],
348
349
  filter: Optional[str],
349
350
  xit: bool,
350
351
  maxfail: Optional[int],
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
  import inspect
6
6
  from dataclasses import asdict, dataclass, field, fields, is_dataclass
7
7
  from enum import IntEnum
8
+ from functools import cached_property
8
9
  from logging import getLogger
9
10
  from pickle import dumps
10
11
  from types import UnionType
@@ -220,9 +221,9 @@ class WalkerAnchor(Anchor):
220
221
  """Walker Anchor."""
221
222
 
222
223
  architype: WalkerArchitype
223
- path: list[Anchor] = field(default_factory=list)
224
- next: list[Anchor] = field(default_factory=list)
225
- ignores: list[Anchor] = field(default_factory=list)
224
+ path: list[NodeAnchor] = field(default_factory=list)
225
+ next: list[NodeAnchor] = field(default_factory=list)
226
+ ignores: list[NodeAnchor] = field(default_factory=list)
226
227
  disengaged: bool = False
227
228
 
228
229
 
@@ -311,17 +312,20 @@ class DSFunc:
311
312
  name: str
312
313
  func: Callable[[Any, Any], Any] | None = None
313
314
 
315
+ @cached_property
316
+ def trigger(self) -> type | UnionType | tuple[type | UnionType, ...] | None:
317
+ """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
328
+
314
329
  def resolve(self, cls: type) -> None:
315
330
  """Resolve the function."""
316
331
  self.func = getattr(cls, self.name)
317
-
318
- def get_funcparam_annotations(
319
- self, func: Callable[[Any, Any], Any] | None
320
- ) -> type | UnionType | tuple[type | UnionType, ...] | None:
321
- """Get function parameter annotations."""
322
- if not func:
323
- return None
324
- annotation = (
325
- inspect.signature(func, eval_str=True).parameters["_jac_here_"].annotation
326
- )
327
- return annotation if annotation != inspect._empty else None
@@ -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."""
@@ -231,3 +231,18 @@ def is_instance(
231
231
  return isinstance(obj, target)
232
232
  case _:
233
233
  return False
234
+
235
+
236
+ def all_issubclass(
237
+ classes: type | UnionType | tuple[type | UnionType, ...], target: type
238
+ ) -> bool:
239
+ """Check if all classes is subclass of target type."""
240
+ match classes:
241
+ case type():
242
+ return issubclass(classes, target)
243
+ case UnionType():
244
+ return all((all_issubclass(cls, target) for cls in classes.__args__))
245
+ case tuple():
246
+ return all((all_issubclass(cls, target) for cls in classes))
247
+ case _:
248
+ 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
@@ -0,0 +1,50 @@
1
+ node Node {
2
+ has val: str;
3
+
4
+ can entry1 with entry {
5
+ print(f"{self.val}-2");
6
+ }
7
+
8
+ can entry2 with Walker entry {
9
+ print(f"{self.val}-3");
10
+ }
11
+
12
+ can exit1 with Walker exit {
13
+ print(f"{self.val}-4");
14
+ }
15
+
16
+ can exit2 with exit {
17
+ print(f"{self.val}-5");
18
+ }
19
+ }
20
+
21
+ walker Walker {
22
+ can entry1 with entry {
23
+ print("walker entry");
24
+ }
25
+
26
+ can entry2 with `root entry {
27
+ print("walker enter to root");
28
+ visit [-->];
29
+ }
30
+
31
+ can entry3 with Node entry {
32
+ print(f"{here.val}-1");
33
+ }
34
+
35
+ can exit1 with Node exit {
36
+ print(f"{here.val}-6");
37
+ }
38
+
39
+ can exit2 with exit {
40
+ print("walker exit");
41
+ }
42
+ }
43
+
44
+ with entry{
45
+ root ++> Node(val = "a");
46
+ root ++> Node(val = "b");
47
+ root ++> Node(val = "c");
48
+
49
+ Walker() spawn root;
50
+ }
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())