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.
- jaclang/cli/cli.py +3 -0
- jaclang/compiler/absyntree.py +18 -3
- jaclang/compiler/passes/main/__init__.py +1 -1
- jaclang/compiler/passes/main/access_modifier_pass.py +1 -1
- jaclang/compiler/passes/main/fuse_typeinfo_pass.py +62 -33
- jaclang/compiler/passes/main/import_pass.py +284 -63
- jaclang/compiler/passes/main/inheritance_pass.py +103 -0
- jaclang/compiler/passes/main/py_collect_dep_pass.py +5 -5
- jaclang/compiler/passes/main/pyast_load_pass.py +1 -1
- jaclang/compiler/passes/main/schedules.py +2 -0
- jaclang/compiler/passes/main/sym_tab_build_pass.py +17 -0
- jaclang/compiler/passes/main/tests/fixtures/data_spatial_types.jac +130 -0
- jaclang/compiler/passes/main/tests/fixtures/pygame_mock/__init__.py +3 -3
- jaclang/compiler/passes/main/tests/fixtures/pygame_mock/__init__.pyi +3 -3
- jaclang/compiler/passes/main/tests/test_import_pass.py +13 -17
- jaclang/compiler/passes/main/tests/test_type_check_pass.py +24 -0
- jaclang/compiler/passes/main/type_check_pass.py +3 -2
- jaclang/compiler/py_info.py +22 -0
- jaclang/compiler/symtable.py +9 -2
- jaclang/langserve/tests/test_server.py +2 -2
- jaclang/plugin/default.py +28 -11
- jaclang/plugin/feature.py +8 -1
- jaclang/plugin/spec.py +3 -2
- jaclang/plugin/tests/fixtures/other_root_access.jac +10 -0
- jaclang/plugin/tests/fixtures/savable_object.jac +84 -0
- jaclang/plugin/tests/test_jaseci.py +88 -0
- jaclang/runtimelib/architype.py +17 -0
- jaclang/runtimelib/memory.py +22 -7
- jaclang/runtimelib/test.py +59 -4
- jaclang/runtimelib/utils.py +16 -0
- jaclang/settings.py +3 -0
- jaclang/tests/fixtures/base_class1.jac +11 -0
- jaclang/tests/fixtures/base_class2.jac +11 -0
- jaclang/tests/fixtures/import_all.jac +7 -0
- jaclang/tests/fixtures/import_all_py.py +8 -0
- jaclang/tests/fixtures/jactest_imported.jac +6 -0
- jaclang/tests/fixtures/jactest_main.jac +22 -0
- jaclang/tests/fixtures/multi_dim_array_split.jac +2 -6
- jaclang/tests/fixtures/test_py.py +12 -0
- jaclang/tests/test_cli.py +82 -0
- jaclang/tests/test_language.py +7 -8
- jaclang/utils/helpers.py +9 -1
- jaclang/utils/treeprinter.py +6 -3
- {jaclang-0.7.26.dist-info → jaclang-0.7.28.dist-info}/METADATA +2 -2
- {jaclang-0.7.26.dist-info → jaclang-0.7.28.dist-info}/RECORD +47 -36
- {jaclang-0.7.26.dist-info → jaclang-0.7.28.dist-info}/WHEEL +1 -1
- {jaclang-0.7.26.dist-info → jaclang-0.7.28.dist-info}/entry_points.txt +0 -0
jaclang/cli/cli.py
CHANGED
|
@@ -307,6 +307,7 @@ def enter(
|
|
|
307
307
|
@cmd_registry.register
|
|
308
308
|
def test(
|
|
309
309
|
filepath: str,
|
|
310
|
+
test_name: str = "",
|
|
310
311
|
filter: str = "",
|
|
311
312
|
xit: bool = False,
|
|
312
313
|
maxfail: int = None, # type:ignore
|
|
@@ -316,6 +317,7 @@ def test(
|
|
|
316
317
|
"""Run the test suite in the specified .jac file.
|
|
317
318
|
|
|
318
319
|
:param filepath: Path/to/file.jac
|
|
320
|
+
:param test_name: Run a specific test.
|
|
319
321
|
:param filter: Filter the files using Unix shell style conventions.
|
|
320
322
|
:param xit(exit): Stop(exit) running tests as soon as finds an error.
|
|
321
323
|
:param maxfail: Stop running tests after n failures.
|
|
@@ -328,6 +330,7 @@ def test(
|
|
|
328
330
|
|
|
329
331
|
failcount = Jac.run_test(
|
|
330
332
|
filepath=filepath,
|
|
333
|
+
func_name=("test_" + test_name) if test_name else None,
|
|
331
334
|
filter=filter,
|
|
332
335
|
xit=xit,
|
|
333
336
|
maxfail=maxfail,
|
jaclang/compiler/absyntree.py
CHANGED
|
@@ -30,6 +30,7 @@ from jaclang.compiler.constant import (
|
|
|
30
30
|
SymbolType,
|
|
31
31
|
)
|
|
32
32
|
from jaclang.compiler.constant import DELIM_MAP, SymbolAccess, Tokens as Tok
|
|
33
|
+
from jaclang.compiler.py_info import PyInfo
|
|
33
34
|
from jaclang.compiler.semtable import SemRegistry
|
|
34
35
|
from jaclang.utils.treeprinter import dotgen_ast_tree, print_ast_tree
|
|
35
36
|
|
|
@@ -636,11 +637,10 @@ class Module(AstDocNode):
|
|
|
636
637
|
self.impl_mod: list[Module] = []
|
|
637
638
|
self.test_mod: list[Module] = []
|
|
638
639
|
self.mod_deps: dict[str, Module] = {}
|
|
639
|
-
self.py_mod_dep_map: dict[str, str] = {}
|
|
640
|
-
self.py_raise_map: dict[str, str] = {}
|
|
641
640
|
self.registry = registry
|
|
642
641
|
self.terminals: list[Token] = terminals
|
|
643
|
-
self.
|
|
642
|
+
self.py_info: PyInfo = PyInfo()
|
|
643
|
+
|
|
644
644
|
AstNode.__init__(self, kid=kid)
|
|
645
645
|
AstDocNode.__init__(self, doc=doc)
|
|
646
646
|
|
|
@@ -693,6 +693,21 @@ class Module(AstDocNode):
|
|
|
693
693
|
super().unparse()
|
|
694
694
|
return self.format()
|
|
695
695
|
|
|
696
|
+
@staticmethod
|
|
697
|
+
def get_href_path(node: AstNode) -> str:
|
|
698
|
+
"""Return the full path of the module that contains this node."""
|
|
699
|
+
parent = node.find_parent_of_type(Module)
|
|
700
|
+
mod_list: list[Module | Architype] = []
|
|
701
|
+
if isinstance(node, (Module, Architype)):
|
|
702
|
+
mod_list.append(node)
|
|
703
|
+
while parent is not None:
|
|
704
|
+
mod_list.append(parent)
|
|
705
|
+
parent = parent.find_parent_of_type(Module)
|
|
706
|
+
mod_list.reverse()
|
|
707
|
+
return ".".join(
|
|
708
|
+
p.name if isinstance(p, Module) else p.name.sym_name for p in mod_list
|
|
709
|
+
)
|
|
710
|
+
|
|
696
711
|
|
|
697
712
|
class GlobalVars(ElementStmt, AstAccessNode):
|
|
698
713
|
"""GlobalVars node type for Jac Ast."""
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
from .sub_node_tab_pass import SubNodeTabPass
|
|
4
4
|
from .sym_tab_build_pass import SymTabBuildPass # noqa: I100
|
|
5
|
+
from .def_use_pass import DefUsePass # noqa: I100
|
|
5
6
|
from .import_pass import JacImportPass, PyImportPass # noqa: I100
|
|
6
7
|
from .def_impl_match_pass import DeclImplMatchPass # noqa: I100
|
|
7
|
-
from .def_use_pass import DefUsePass # noqa: I100
|
|
8
8
|
from .pyout_pass import PyOutPass # noqa: I100
|
|
9
9
|
from .pyast_load_pass import PyastBuildPass # type: ignore # noqa: I100
|
|
10
10
|
from .pyast_gen_pass import PyastGenPass # noqa: I100
|
|
@@ -45,7 +45,7 @@ class AccessCheckPass(Pass):
|
|
|
45
45
|
settings.lsp_debug
|
|
46
46
|
and isinstance(node, ast.NameAtom)
|
|
47
47
|
and not node.sym
|
|
48
|
-
and not node.parent_of_type(ast.Module).is_raised_from_py
|
|
48
|
+
and not node.parent_of_type(ast.Module).py_info.is_raised_from_py
|
|
49
49
|
and not (
|
|
50
50
|
node.sym_name == "py"
|
|
51
51
|
and node.parent
|
|
@@ -10,6 +10,7 @@ import re
|
|
|
10
10
|
from typing import Callable, Optional, TypeVar
|
|
11
11
|
|
|
12
12
|
import jaclang.compiler.absyntree as ast
|
|
13
|
+
from jaclang.compiler.constant import Constants, Tokens
|
|
13
14
|
from jaclang.compiler.passes import Pass
|
|
14
15
|
from jaclang.compiler.symtable import SymbolTable
|
|
15
16
|
from jaclang.settings import settings
|
|
@@ -76,35 +77,6 @@ class FuseTypeInfoPass(Pass):
|
|
|
76
77
|
if typ_sym_table != self.ir.sym_tab:
|
|
77
78
|
node.name_spec.type_sym_tab = typ_sym_table
|
|
78
79
|
|
|
79
|
-
def __collect_python_dependencies(self, node: ast.AstNode) -> None:
|
|
80
|
-
assert isinstance(node, ast.AstSymbolNode)
|
|
81
|
-
assert isinstance(self.ir, ast.Module)
|
|
82
|
-
|
|
83
|
-
mypy_node = node.gen.mypy_ast[0]
|
|
84
|
-
|
|
85
|
-
if isinstance(mypy_node, MypyNodes.RefExpr) and mypy_node.node:
|
|
86
|
-
node_full_name = mypy_node.node.fullname
|
|
87
|
-
if "." in node_full_name:
|
|
88
|
-
mod_name = node_full_name[: node_full_name.rindex(".")]
|
|
89
|
-
else:
|
|
90
|
-
mod_name = node_full_name
|
|
91
|
-
|
|
92
|
-
if mod_name not in self.ir.py_mod_dep_map:
|
|
93
|
-
self.__debug_print(
|
|
94
|
-
f"Can't find a python file associated with {type(node)}::{node.loc}"
|
|
95
|
-
)
|
|
96
|
-
return
|
|
97
|
-
|
|
98
|
-
mode_path = self.ir.py_mod_dep_map[mod_name]
|
|
99
|
-
if mode_path.endswith(".jac"):
|
|
100
|
-
return
|
|
101
|
-
|
|
102
|
-
self.ir.py_raise_map[mod_name] = mode_path
|
|
103
|
-
else:
|
|
104
|
-
self.__debug_print(
|
|
105
|
-
f"Collect python dependencies is not supported in {type(node)}::{node.loc}"
|
|
106
|
-
)
|
|
107
|
-
|
|
108
80
|
@staticmethod
|
|
109
81
|
def __handle_node(
|
|
110
82
|
func: Callable[[FuseTypeInfoPass, T], None]
|
|
@@ -126,7 +98,6 @@ class FuseTypeInfoPass(Pass):
|
|
|
126
98
|
if len(node.gen.mypy_ast) == 1:
|
|
127
99
|
func(self, node)
|
|
128
100
|
self.__set_type_sym_table_link(node)
|
|
129
|
-
self.__collect_python_dependencies(node)
|
|
130
101
|
|
|
131
102
|
# Jac node has multiple mypy nodes linked to it
|
|
132
103
|
elif len(node.gen.mypy_ast) > 1:
|
|
@@ -150,7 +121,6 @@ class FuseTypeInfoPass(Pass):
|
|
|
150
121
|
)
|
|
151
122
|
func(self, node)
|
|
152
123
|
self.__set_type_sym_table_link(node)
|
|
153
|
-
self.__collect_python_dependencies(node)
|
|
154
124
|
|
|
155
125
|
# Special handing for BuiltinType
|
|
156
126
|
elif isinstance(node, ast.BuiltinType):
|
|
@@ -266,6 +236,43 @@ class FuseTypeInfoPass(Pass):
|
|
|
266
236
|
if len(node.gen.mypy_ast) == 0:
|
|
267
237
|
return
|
|
268
238
|
|
|
239
|
+
# No need to run this handling in case of a previous type
|
|
240
|
+
# was set during the pass, if not then we hope that the
|
|
241
|
+
# mypy node has a type associated to it
|
|
242
|
+
if node.expr_type != "NoType":
|
|
243
|
+
return
|
|
244
|
+
|
|
245
|
+
# Check if the expression is a data spatial expression
|
|
246
|
+
# Support disconnectOp
|
|
247
|
+
if isinstance(node, ast.BinaryExpr):
|
|
248
|
+
if isinstance(node.op, ast.DisconnectOp):
|
|
249
|
+
node.expr_type = "builtins.bool"
|
|
250
|
+
return
|
|
251
|
+
|
|
252
|
+
# Support spwan and connectOp
|
|
253
|
+
elif (
|
|
254
|
+
isinstance(node.op, ast.ConnectOp)
|
|
255
|
+
or node.op.name == Tokens.KW_SPAWN.value
|
|
256
|
+
):
|
|
257
|
+
if node.gen.mypy_ast[-1] in self.node_type_hash:
|
|
258
|
+
node.expr_type = (
|
|
259
|
+
self.__call_type_handler(
|
|
260
|
+
self.node_type_hash[node.gen.mypy_ast[-1]]
|
|
261
|
+
)
|
|
262
|
+
or node.expr_type
|
|
263
|
+
)
|
|
264
|
+
return
|
|
265
|
+
|
|
266
|
+
if isinstance(node, ast.EdgeRefTrailer) and any(
|
|
267
|
+
isinstance(k, ast.FilterCompr) for k in node.kid
|
|
268
|
+
):
|
|
269
|
+
if node.gen.mypy_ast[-1] in self.node_type_hash:
|
|
270
|
+
node.expr_type = (
|
|
271
|
+
self.__call_type_handler(self.node_type_hash[node.gen.mypy_ast[-1]])
|
|
272
|
+
or node.expr_type
|
|
273
|
+
)
|
|
274
|
+
return
|
|
275
|
+
|
|
269
276
|
# If the corrosponding mypy ast node type has stored here, get the values.
|
|
270
277
|
mypy_node = node.gen.mypy_ast[0]
|
|
271
278
|
if mypy_node in self.node_type_hash:
|
|
@@ -427,7 +434,14 @@ class FuseTypeInfoPass(Pass):
|
|
|
427
434
|
@__handle_node
|
|
428
435
|
def enter_special_var_ref(self, node: ast.SpecialVarRef) -> None:
|
|
429
436
|
"""Pass handler for SpecialVarRef nodes."""
|
|
430
|
-
|
|
437
|
+
if node.py_resolve_name() == Constants.ROOT:
|
|
438
|
+
if node.gen.mypy_ast[-1] in self.node_type_hash:
|
|
439
|
+
node.name_spec.expr_type = (
|
|
440
|
+
self.__call_type_handler(self.node_type_hash[node.gen.mypy_ast[-1]])
|
|
441
|
+
or node.name_spec.expr_type
|
|
442
|
+
)
|
|
443
|
+
else:
|
|
444
|
+
self.enter_name(node)
|
|
431
445
|
|
|
432
446
|
@__handle_node
|
|
433
447
|
def enter_edge_op_ref(self, node: ast.EdgeOpRef) -> None:
|
|
@@ -491,7 +505,7 @@ class FuseTypeInfoPass(Pass):
|
|
|
491
505
|
self, mypy_type: MypyTypes.Overloaded
|
|
492
506
|
) -> Optional[str]:
|
|
493
507
|
"""Get type info from mypy type Overloaded."""
|
|
494
|
-
return self.__call_type_handler(mypy_type.items[
|
|
508
|
+
return self.__call_type_handler(mypy_type.items[-1])
|
|
495
509
|
|
|
496
510
|
def get_type_from_none_type(self, mypy_type: MypyTypes.NoneType) -> Optional[str]:
|
|
497
511
|
"""Get type info from mypy type NoneType."""
|
|
@@ -509,6 +523,12 @@ class FuseTypeInfoPass(Pass):
|
|
|
509
523
|
"""Get type info from mypy type TypeType."""
|
|
510
524
|
return str(mypy_type.item)
|
|
511
525
|
|
|
526
|
+
def get_type_from_type_var_type(
|
|
527
|
+
self, mypy_type: MypyTypes.TypeVarType
|
|
528
|
+
) -> Optional[str]:
|
|
529
|
+
"""Get type info from mypy type TypeType."""
|
|
530
|
+
return str(mypy_type.name)
|
|
531
|
+
|
|
512
532
|
def exit_assignment(self, node: ast.Assignment) -> None:
|
|
513
533
|
"""Add new symbols in the symbol table in case of self."""
|
|
514
534
|
# This will fix adding new items to the class through self
|
|
@@ -590,6 +610,7 @@ class FuseTypeInfoPass(Pass):
|
|
|
590
610
|
# right index slice is a range then it's type is the same as left
|
|
591
611
|
if right.is_range:
|
|
592
612
|
right.expr_type = left.expr_type
|
|
613
|
+
right.parent_of_type(ast.AtomTrailer).expr_type = node_type
|
|
593
614
|
continue
|
|
594
615
|
|
|
595
616
|
# left type is a dictionary
|
|
@@ -603,6 +624,7 @@ class FuseTypeInfoPass(Pass):
|
|
|
603
624
|
continue
|
|
604
625
|
|
|
605
626
|
right.expr_type = node_type
|
|
627
|
+
right.parent_of_type(ast.AtomTrailer).expr_type = node_type
|
|
606
628
|
|
|
607
629
|
# Getting the correct symbol table and link it
|
|
608
630
|
type_symtab: Optional[SymbolTable] = self.ir.sym_tab
|
|
@@ -621,6 +643,13 @@ class FuseTypeInfoPass(Pass):
|
|
|
621
643
|
right.type_sym_tab = type_symtab
|
|
622
644
|
|
|
623
645
|
else:
|
|
646
|
+
# Fix the symbolTable linking in case of type annotations
|
|
647
|
+
if left.type_sym_tab is None and isinstance(node.parent, ast.SubTag):
|
|
648
|
+
assert isinstance(left, ast.AstSymbolNode)
|
|
649
|
+
left.name_spec.type_sym_tab = self.ir.sym_tab.find_scope(
|
|
650
|
+
left.sym_name
|
|
651
|
+
)
|
|
652
|
+
|
|
624
653
|
if left.type_sym_tab:
|
|
625
654
|
right.name_spec.sym = left.type_sym_tab.lookup(right.sym_name)
|
|
626
655
|
if right.name_spec.sym:
|
|
@@ -12,8 +12,11 @@ from typing import Optional
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
import jaclang.compiler.absyntree as ast
|
|
15
|
+
from jaclang.compiler.constant import SymbolType
|
|
15
16
|
from jaclang.compiler.passes import Pass
|
|
16
|
-
from jaclang.compiler.passes.main import SubNodeTabPass, SymTabBuildPass
|
|
17
|
+
from jaclang.compiler.passes.main import DefUsePass, SubNodeTabPass, SymTabBuildPass
|
|
18
|
+
from jaclang.compiler.passes.main.sym_tab_build_pass import PyInspectSymTabBuildPass
|
|
19
|
+
from jaclang.compiler.symtable import Symbol, SymbolTable
|
|
17
20
|
from jaclang.settings import settings
|
|
18
21
|
from jaclang.utils.log import logging
|
|
19
22
|
|
|
@@ -198,19 +201,20 @@ class JacImportPass(Pass):
|
|
|
198
201
|
class PyImportPass(JacImportPass):
|
|
199
202
|
"""Jac statically imports Python modules."""
|
|
200
203
|
|
|
204
|
+
def __debug_print(self, msg: str) -> None:
|
|
205
|
+
if settings.py_import_pass_debug:
|
|
206
|
+
self.log_info("[PyImportPass] " + msg)
|
|
207
|
+
|
|
201
208
|
def before_pass(self) -> None:
|
|
202
209
|
"""Only run pass if settings are set to raise python."""
|
|
210
|
+
self.import_from_build_list: list[tuple[ast.Import, ast.Module]] = []
|
|
203
211
|
super().before_pass()
|
|
204
212
|
self.__load_builtins()
|
|
205
213
|
|
|
206
|
-
def
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
mod_list.append(parent)
|
|
211
|
-
parent = parent.find_parent_of_type(ast.Module)
|
|
212
|
-
mod_list.reverse()
|
|
213
|
-
return ".".join(p.name for p in mod_list)
|
|
214
|
+
def after_pass(self) -> None:
|
|
215
|
+
"""Build symbol tables for import from nodes."""
|
|
216
|
+
self.__import_from_symbol_table_build()
|
|
217
|
+
return super().after_pass()
|
|
214
218
|
|
|
215
219
|
def process_import(self, i: ast.ModulePath) -> None:
|
|
216
220
|
"""Process an import."""
|
|
@@ -218,87 +222,273 @@ class PyImportPass(JacImportPass):
|
|
|
218
222
|
# This won't work with py imports as this will fail to import stuff in form of
|
|
219
223
|
# from a import b
|
|
220
224
|
# from a import (c, d, e)
|
|
221
|
-
# Solution to that is to get the import node and check the from loc then
|
|
225
|
+
# Solution to that is to get the import node and check the from loc `then`
|
|
222
226
|
# handle it based on if there a from loc or not
|
|
223
227
|
imp_node = i.parent_of_type(ast.Import)
|
|
228
|
+
|
|
224
229
|
if imp_node.is_py and not i.sub_module:
|
|
225
230
|
if imp_node.from_loc:
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
231
|
+
msg = "Processing import from node at href="
|
|
232
|
+
msg += ast.Module.get_href_path(imp_node)
|
|
233
|
+
msg += f' path="{imp_node.loc.mod_path}, {imp_node.loc}"'
|
|
234
|
+
self.__debug_print(msg)
|
|
235
|
+
self.__process_import_from(imp_node)
|
|
236
|
+
else:
|
|
237
|
+
msg = "Processing import node at href="
|
|
238
|
+
msg += ast.Module.get_href_path(imp_node)
|
|
239
|
+
msg += f' path="{imp_node.loc.mod_path}, {imp_node.loc}"'
|
|
240
|
+
self.__debug_print(msg)
|
|
241
|
+
self.__process_import(imp_node)
|
|
242
|
+
|
|
243
|
+
def __process_import_from(self, imp_node: ast.Import) -> None:
|
|
244
|
+
"""Process imports in the form of `from X import I`."""
|
|
245
|
+
assert isinstance(self.ir, ast.Module)
|
|
246
|
+
assert isinstance(imp_node.from_loc, ast.ModulePath)
|
|
247
|
+
|
|
248
|
+
self.__debug_print(f"\tTrying to import {imp_node.from_loc.dot_path_str}")
|
|
249
|
+
|
|
250
|
+
# Attempt to import the Python module X and process it
|
|
251
|
+
imported_mod = self.__import_py_module(
|
|
252
|
+
parent_node_path=ast.Module.get_href_path(imp_node),
|
|
253
|
+
mod_path=imp_node.from_loc.dot_path_str,
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
if imported_mod:
|
|
257
|
+
# Cyclic imports will happen in case of import sys
|
|
258
|
+
# sys stub file imports sys module which means that we need
|
|
259
|
+
# to import sys stub file again in the sys stub file and so on
|
|
260
|
+
# This can be detected by iterating over all the parents and make sure
|
|
261
|
+
# that the parent is in another file than the imported module
|
|
262
|
+
if self.__check_cyclic_imports(imp_node, imported_mod):
|
|
263
|
+
self.__debug_print(
|
|
264
|
+
f"\tCycled imports is found at {imp_node.loc.mod_path} {imp_node.loc}"
|
|
265
|
+
)
|
|
266
|
+
return
|
|
267
|
+
|
|
268
|
+
if imported_mod.name == "builtins":
|
|
269
|
+
self.__debug_print(
|
|
270
|
+
f"\tIgnoring attaching builtins {imp_node.loc.mod_path} {imp_node.loc}"
|
|
271
|
+
)
|
|
272
|
+
return
|
|
273
|
+
|
|
274
|
+
self.__debug_print(
|
|
275
|
+
f"\tAttaching {imported_mod.name} into {ast.Module.get_href_path(imp_node)}"
|
|
276
|
+
)
|
|
277
|
+
msg = f"\tRegistering module:{imported_mod.name} to "
|
|
278
|
+
msg += f"import_from handling with {imp_node.loc.mod_path}:{imp_node.loc}"
|
|
279
|
+
self.__debug_print(msg)
|
|
280
|
+
|
|
281
|
+
self.attach_mod_to_node(imp_node.from_loc, imported_mod)
|
|
282
|
+
self.import_from_build_list.append((imp_node, imported_mod))
|
|
283
|
+
if imported_mod._sym_tab is None:
|
|
284
|
+
self.__debug_print(
|
|
285
|
+
f"\tBuilding symbol table for module:{ast.Module.get_href_path(imported_mod)}"
|
|
286
|
+
)
|
|
236
287
|
else:
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
288
|
+
self.__debug_print(
|
|
289
|
+
f"\tRefreshing symbol table for module:{ast.Module.get_href_path(imported_mod)}"
|
|
290
|
+
)
|
|
291
|
+
PyInspectSymTabBuildPass(input_ir=imported_mod, prior=self)
|
|
292
|
+
DefUsePass(input_ir=imported_mod, prior=self)
|
|
293
|
+
|
|
294
|
+
def __import_from_symbol_table_build(self) -> None:
|
|
295
|
+
"""Build symbol tables for the imported python modules."""
|
|
296
|
+
is_symbol_tabled_refreshed: list[str] = []
|
|
297
|
+
self.import_from_build_list.reverse()
|
|
298
|
+
for imp_node, imported_mod in self.import_from_build_list:
|
|
299
|
+
# Need to build the symbol tables again to make sure that the
|
|
300
|
+
# complete symbol table is built.
|
|
301
|
+
#
|
|
302
|
+
# Complete symbol tables won't be built in case of another
|
|
303
|
+
# import from statements in the imported modules.
|
|
304
|
+
#
|
|
305
|
+
# A solution was to only build the symbol table here after the
|
|
306
|
+
# full ast is raised but this will cause an issue with symboltable
|
|
307
|
+
# building with normal imports
|
|
308
|
+
#
|
|
309
|
+
# TODO: Change normal imports to call symbolTable here too
|
|
310
|
+
|
|
311
|
+
if imported_mod.loc.mod_path not in is_symbol_tabled_refreshed:
|
|
312
|
+
self.__debug_print(
|
|
313
|
+
f"Refreshing symbol table for module:{ast.Module.get_href_path(imported_mod)}"
|
|
314
|
+
)
|
|
315
|
+
PyInspectSymTabBuildPass(input_ir=imported_mod, prior=self)
|
|
316
|
+
DefUsePass(input_ir=imported_mod, prior=self)
|
|
317
|
+
is_symbol_tabled_refreshed.append(imported_mod.loc.mod_path)
|
|
318
|
+
|
|
319
|
+
sym_tab = imported_mod.sym_tab
|
|
320
|
+
parent_sym_tab = imp_node.parent_of_type(ast.Module).sym_tab
|
|
321
|
+
|
|
322
|
+
if imp_node.is_absorb:
|
|
323
|
+
for symbol in sym_tab.tab.values():
|
|
324
|
+
if symbol.sym_type == SymbolType.MODULE:
|
|
325
|
+
continue
|
|
326
|
+
self.__import_from_sym_table_add_symbols(symbol, parent_sym_tab)
|
|
327
|
+
else:
|
|
328
|
+
for i in imp_node.items.items:
|
|
329
|
+
assert isinstance(i, ast.ModuleItem)
|
|
330
|
+
needed_sym = sym_tab.lookup(i.name.sym_name)
|
|
331
|
+
|
|
332
|
+
if needed_sym and needed_sym.defn[0].parent:
|
|
333
|
+
self.__import_from_sym_table_add_symbols(
|
|
334
|
+
needed_sym, parent_sym_tab
|
|
335
|
+
)
|
|
336
|
+
else:
|
|
337
|
+
self.__debug_print(
|
|
338
|
+
f"Can't find a symbol matching {i.name.sym_name} in {sym_tab.name}"
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
def __import_from_sym_table_add_symbols(
|
|
342
|
+
self, sym: Symbol, sym_table: SymbolTable
|
|
343
|
+
) -> None:
|
|
344
|
+
self.__debug_print(
|
|
345
|
+
f"\tAdding {sym.sym_type}:{sym.sym_name} into {sym_table.name}"
|
|
346
|
+
)
|
|
347
|
+
assert isinstance(sym.defn[0], ast.AstSymbolNode)
|
|
348
|
+
sym_table.def_insert(
|
|
349
|
+
node=sym.defn[0],
|
|
350
|
+
access_spec=sym.access,
|
|
351
|
+
force_overwrite=True,
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
if sym.fetch_sym_tab:
|
|
355
|
+
msg = f"\tAdding SymbolTable:{sym.fetch_sym_tab.name} into "
|
|
356
|
+
msg += f"SymbolTable:{sym_table.name} kids"
|
|
357
|
+
self.__debug_print(msg)
|
|
358
|
+
sym_table.kid.append(sym.fetch_sym_tab)
|
|
359
|
+
elif sym.sym_type not in (SymbolType.VAR, SymbolType.MOD_VAR):
|
|
360
|
+
raise AssertionError(
|
|
361
|
+
f"Unexpected symbol type '{sym.sym_type}' that doesn't have a symbl table"
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
def __process_import(self, imp_node: ast.Import) -> None:
|
|
365
|
+
"""Process the imports in form of `import X`."""
|
|
366
|
+
# Expected that each ImportStatement will import one item
|
|
367
|
+
# In case of this assertion fired then we need to revisit this item
|
|
368
|
+
assert len(imp_node.items.items) == 1
|
|
369
|
+
imported_item = imp_node.items.items[0]
|
|
370
|
+
assert isinstance(imported_item, ast.ModulePath)
|
|
371
|
+
|
|
372
|
+
self.__debug_print(f"\tTrying to import {imported_item.dot_path_str}")
|
|
373
|
+
imported_mod = self.__import_py_module(
|
|
374
|
+
parent_node_path=ast.Module.get_href_path(imported_item),
|
|
375
|
+
mod_path=imported_item.dot_path_str,
|
|
376
|
+
imported_mod_name=(
|
|
377
|
+
# TODO: Check this replace
|
|
378
|
+
imported_item.dot_path_str.replace(".", "")
|
|
379
|
+
if not imported_item.alias
|
|
380
|
+
else imported_item.alias.sym_name
|
|
381
|
+
),
|
|
382
|
+
)
|
|
383
|
+
if imported_mod:
|
|
384
|
+
if self.__check_cyclic_imports(imp_node, imported_mod):
|
|
385
|
+
self.__debug_print(
|
|
386
|
+
f"\tCycled imports is found at {imp_node.loc.mod_path} {imp_node.loc}"
|
|
387
|
+
)
|
|
388
|
+
return
|
|
389
|
+
elif imported_mod.name == "builtins":
|
|
390
|
+
self.__debug_print(
|
|
391
|
+
f"\tIgnoring attaching builtins {imp_node.loc.mod_path} {imp_node.loc}"
|
|
392
|
+
)
|
|
393
|
+
return
|
|
394
|
+
|
|
395
|
+
self.__debug_print(
|
|
396
|
+
f"\tAttaching {imported_mod.name} into {ast.Module.get_href_path(imp_node)}"
|
|
397
|
+
)
|
|
398
|
+
self.attach_mod_to_node(imported_item, imported_mod)
|
|
399
|
+
|
|
400
|
+
if imp_node.is_absorb:
|
|
401
|
+
msg = f"\tRegistering module:{imported_mod.name} to "
|
|
402
|
+
msg += f"import_from (import all) handling with {imp_node.loc.mod_path}:{imp_node.loc}"
|
|
403
|
+
self.__debug_print(msg)
|
|
404
|
+
|
|
405
|
+
self.import_from_build_list.append((imp_node, imported_mod))
|
|
406
|
+
if imported_mod._sym_tab is None:
|
|
407
|
+
self.__debug_print(
|
|
408
|
+
f"\tBuilding symbol table for module:{ast.Module.get_href_path(imported_mod)}"
|
|
409
|
+
)
|
|
410
|
+
else:
|
|
411
|
+
self.__debug_print(
|
|
412
|
+
f"\tRefreshing symbol table for module:{ast.Module.get_href_path(imported_mod)}"
|
|
247
413
|
)
|
|
414
|
+
PyInspectSymTabBuildPass(input_ir=imported_mod, prior=self)
|
|
415
|
+
DefUsePass(input_ir=imported_mod, prior=self)
|
|
416
|
+
|
|
417
|
+
else:
|
|
418
|
+
self.__debug_print(
|
|
419
|
+
f"\tBuilding symbol table for module:{ast.Module.get_href_path(imported_mod)}"
|
|
420
|
+
)
|
|
421
|
+
SymTabBuildPass(input_ir=imported_mod, prior=self)
|
|
248
422
|
|
|
249
|
-
def
|
|
423
|
+
def __import_py_module(
|
|
250
424
|
self,
|
|
251
|
-
|
|
252
|
-
imported_mod_name: str,
|
|
425
|
+
parent_node_path: str,
|
|
253
426
|
mod_path: str,
|
|
427
|
+
imported_mod_name: Optional[str] = None,
|
|
254
428
|
) -> Optional[ast.Module]:
|
|
255
|
-
"""Import a module."""
|
|
429
|
+
"""Import a python module."""
|
|
256
430
|
from jaclang.compiler.passes.main import PyastBuildPass
|
|
257
431
|
|
|
258
432
|
assert isinstance(self.ir, ast.Module)
|
|
259
433
|
|
|
260
|
-
python_raise_map = self.ir.py_raise_map
|
|
261
|
-
file_to_raise = None
|
|
434
|
+
python_raise_map = self.ir.py_info.py_raise_map
|
|
435
|
+
file_to_raise: Optional[str] = None
|
|
262
436
|
|
|
263
437
|
if mod_path in python_raise_map:
|
|
264
438
|
file_to_raise = python_raise_map[mod_path]
|
|
265
439
|
else:
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
)
|
|
269
|
-
assert isinstance(self.ir, ast.Module)
|
|
440
|
+
# TODO: Is it fine to use imported_mod_name or get it from mod_path?
|
|
441
|
+
resolved_mod_path = f"{parent_node_path}.{mod_path}"
|
|
442
|
+
resolved_mod_path = resolved_mod_path.replace("..", ".")
|
|
270
443
|
resolved_mod_path = resolved_mod_path.replace(f"{self.ir.name}.", "")
|
|
271
444
|
file_to_raise = python_raise_map.get(resolved_mod_path)
|
|
272
445
|
|
|
273
446
|
if file_to_raise is None:
|
|
447
|
+
self.__debug_print("\tNo file is found to do the import")
|
|
274
448
|
return None
|
|
275
449
|
|
|
450
|
+
self.__debug_print(f"\tFile used to do the import is {file_to_raise}")
|
|
451
|
+
|
|
276
452
|
try:
|
|
277
|
-
if file_to_raise
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
453
|
+
if file_to_raise in {None, "built-in", "frozen"}:
|
|
454
|
+
return None
|
|
455
|
+
|
|
456
|
+
if file_to_raise in self.import_table:
|
|
457
|
+
self.__debug_print(
|
|
458
|
+
f"\t{file_to_raise} was raised before, getting it from cache"
|
|
459
|
+
)
|
|
460
|
+
return self.import_table[file_to_raise]
|
|
461
|
+
|
|
462
|
+
with open(file_to_raise, "r", encoding="utf-8") as f:
|
|
463
|
+
file_source = f.read()
|
|
464
|
+
mod = PyastBuildPass(
|
|
465
|
+
input_ir=ast.PythonModuleAst(
|
|
466
|
+
py_ast.parse(file_source),
|
|
467
|
+
orig_src=ast.JacSource(file_source, file_to_raise),
|
|
468
|
+
),
|
|
469
|
+
).ir
|
|
470
|
+
SubNodeTabPass(input_ir=mod, prior=self)
|
|
471
|
+
|
|
472
|
+
if mod:
|
|
473
|
+
mod.name = imported_mod_name if imported_mod_name else mod.name
|
|
474
|
+
if mod.name == "__init__":
|
|
475
|
+
mod_name = mod.loc.mod_path.split("/")[-2]
|
|
476
|
+
self.__debug_print(
|
|
477
|
+
f"\tRaised the __init__ file and rename the mod to be {mod_name}"
|
|
478
|
+
)
|
|
479
|
+
mod.name = mod_name
|
|
480
|
+
self.import_table[file_to_raise] = mod
|
|
481
|
+
mod.py_info.is_raised_from_py = True
|
|
482
|
+
self.__debug_print(
|
|
483
|
+
f"\t{file_to_raise} is raised, adding it to the cache"
|
|
484
|
+
)
|
|
485
|
+
return mod
|
|
486
|
+
else:
|
|
487
|
+
raise self.ice(f"\tFailed to import python module {mod_path}")
|
|
488
|
+
|
|
298
489
|
except Exception as e:
|
|
299
|
-
self.error(f"
|
|
490
|
+
self.error(f"\tFailed to import python module {mod_path}")
|
|
300
491
|
raise e
|
|
301
|
-
return None
|
|
302
492
|
|
|
303
493
|
def __load_builtins(self) -> None:
|
|
304
494
|
"""Pyraise builtins to help with builtins auto complete."""
|
|
@@ -330,3 +520,34 @@ class PyImportPass(JacImportPass):
|
|
|
330
520
|
def annex_impl(self, node: ast.Module) -> None:
|
|
331
521
|
"""Annex impl and test modules."""
|
|
332
522
|
return None
|
|
523
|
+
|
|
524
|
+
def __handle_different_site_packages(self, mod_path: str) -> str:
|
|
525
|
+
if "site-packages" in mod_path:
|
|
526
|
+
mod_path = mod_path[mod_path.index("site-packages") :]
|
|
527
|
+
return mod_path
|
|
528
|
+
|
|
529
|
+
def __check_cyclic_imports(
|
|
530
|
+
self, imp_node: ast.AstNode, imported_module: ast.Module
|
|
531
|
+
) -> bool:
|
|
532
|
+
"""Check cyclic imports that might happen."""
|
|
533
|
+
# Example of cyclic imports is import os
|
|
534
|
+
# In the os stub file it imports the real os module which will cause os
|
|
535
|
+
# stub to be raised again and so on
|
|
536
|
+
# Another example is numpy. numpy init file imports multidim array file
|
|
537
|
+
# which imports again more items from numpy and so on.
|
|
538
|
+
imp_node_file = self.__handle_different_site_packages(imp_node.loc.mod_path)
|
|
539
|
+
imported_module_file = self.__handle_different_site_packages(
|
|
540
|
+
imported_module.loc.mod_path
|
|
541
|
+
)
|
|
542
|
+
if imp_node_file == imported_module_file:
|
|
543
|
+
return True
|
|
544
|
+
|
|
545
|
+
parent: Optional[ast.AstNode] = imp_node.parent
|
|
546
|
+
while parent is not None:
|
|
547
|
+
parent_file = self.__handle_different_site_packages(parent.loc.mod_path)
|
|
548
|
+
if parent_file == imported_module_file:
|
|
549
|
+
return True
|
|
550
|
+
else:
|
|
551
|
+
parent = parent.find_parent_of_type(ast.Module)
|
|
552
|
+
|
|
553
|
+
return False
|