jaclang 0.7.14__py3-none-any.whl → 0.7.16__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 +15 -10
- jaclang/cli/cmdreg.py +9 -12
- jaclang/compiler/__init__.py +19 -53
- jaclang/compiler/absyntree.py +86 -13
- jaclang/compiler/jac.lark +4 -3
- jaclang/compiler/parser.py +31 -23
- jaclang/compiler/passes/ir_pass.py +4 -13
- jaclang/compiler/passes/main/access_modifier_pass.py +1 -1
- jaclang/compiler/passes/main/fuse_typeinfo_pass.py +4 -5
- jaclang/compiler/passes/main/import_pass.py +18 -23
- jaclang/compiler/passes/main/pyast_gen_pass.py +307 -559
- jaclang/compiler/passes/main/pyast_load_pass.py +32 -6
- jaclang/compiler/passes/main/registry_pass.py +37 -3
- jaclang/compiler/passes/main/sym_tab_build_pass.py +1 -1
- jaclang/compiler/passes/main/tests/__init__.py +1 -1
- jaclang/compiler/passes/main/type_check_pass.py +7 -0
- jaclang/compiler/passes/tool/jac_formatter_pass.py +124 -86
- jaclang/compiler/passes/tool/tests/fixtures/corelib_fmt.jac +0 -1
- jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/architype_test.jac +13 -0
- jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/comment_alignment.jac +11 -0
- jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/comments.jac +13 -0
- jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/decorator_stack.jac +37 -0
- jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/esc_keywords.jac +5 -0
- jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/long_names.jac +19 -0
- jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/triple_quoted_string.jac +6 -0
- jaclang/compiler/passes/tool/tests/test_jac_format_pass.py +11 -0
- jaclang/compiler/passes/tool/tests/test_unparse_validate.py +33 -39
- jaclang/compiler/passes/transform.py +4 -0
- jaclang/compiler/semtable.py +31 -7
- jaclang/compiler/tests/test_importer.py +12 -5
- jaclang/langserve/engine.py +65 -118
- jaclang/langserve/sem_manager.py +379 -0
- jaclang/langserve/server.py +8 -10
- jaclang/langserve/tests/fixtures/base_module_structure.jac +27 -6
- jaclang/langserve/tests/fixtures/circle.jac +3 -3
- jaclang/langserve/tests/fixtures/circle_err.jac +3 -3
- jaclang/langserve/tests/fixtures/circle_pure.test.jac +3 -3
- jaclang/langserve/tests/fixtures/import_include_statements.jac +1 -1
- jaclang/langserve/tests/test_sem_tokens.py +277 -0
- jaclang/langserve/tests/test_server.py +72 -16
- jaclang/langserve/utils.py +163 -96
- jaclang/plugin/builtin.py +1 -1
- jaclang/plugin/default.py +212 -24
- jaclang/plugin/feature.py +59 -11
- jaclang/plugin/spec.py +58 -6
- jaclang/{core → runtimelib}/architype.py +1 -1
- jaclang/{core → runtimelib}/context.py +8 -1
- jaclang/runtimelib/importer.py +361 -0
- jaclang/runtimelib/machine.py +94 -0
- jaclang/{core → runtimelib}/utils.py +13 -5
- jaclang/settings.py +4 -1
- jaclang/tests/fixtures/abc.jac +3 -3
- jaclang/tests/fixtures/byllmissue.jac +1 -5
- jaclang/tests/fixtures/chandra_bugs2.jac +11 -10
- jaclang/tests/fixtures/cls_method.jac +41 -0
- jaclang/tests/fixtures/dblhello.jac +6 -0
- jaclang/tests/fixtures/deep/one_lev.jac +3 -3
- jaclang/tests/fixtures/deep/one_lev_dup.jac +2 -3
- jaclang/tests/fixtures/deep_import_mods.jac +13 -0
- jaclang/tests/fixtures/err.impl.jac +3 -0
- jaclang/tests/fixtures/err.jac +4 -2
- jaclang/tests/fixtures/err.test.jac +3 -0
- jaclang/tests/fixtures/err_runtime.jac +15 -0
- jaclang/tests/fixtures/game1.jac +1 -1
- jaclang/tests/fixtures/hello.jac +4 -0
- jaclang/tests/fixtures/impl_grab.impl.jac +2 -1
- jaclang/tests/fixtures/impl_grab.jac +4 -1
- jaclang/tests/fixtures/jp_importer_auto.jac +14 -0
- jaclang/tests/fixtures/maxfail_run_test.jac +4 -4
- jaclang/tests/fixtures/needs_import.jac +2 -2
- jaclang/tests/fixtures/pyfunc_2.py +3 -0
- jaclang/tests/fixtures/registry.jac +9 -0
- jaclang/tests/fixtures/run_test.jac +4 -4
- jaclang/tests/fixtures/semstr.jac +1 -4
- jaclang/tests/fixtures/simple_archs.jac +1 -1
- jaclang/tests/test_cli.py +65 -2
- jaclang/tests/test_language.py +74 -7
- jaclang/tests/test_reference.py +6 -0
- jaclang/utils/helpers.py +45 -21
- jaclang/utils/test.py +9 -0
- jaclang/utils/treeprinter.py +0 -4
- {jaclang-0.7.14.dist-info → jaclang-0.7.16.dist-info}/METADATA +3 -2
- {jaclang-0.7.14.dist-info → jaclang-0.7.16.dist-info}/RECORD +89 -74
- jaclang/core/importer.py +0 -344
- jaclang/tests/fixtures/aott_raise.jac +0 -25
- jaclang/tests/fixtures/package_import.jac +0 -6
- /jaclang/{core → runtimelib}/__init__.py +0 -0
- /jaclang/{core → runtimelib}/constructs.py +0 -0
- /jaclang/{core → runtimelib}/memory.py +0 -0
- /jaclang/{core → runtimelib}/test.py +0 -0
- {jaclang-0.7.14.dist-info → jaclang-0.7.16.dist-info}/WHEEL +0 -0
- {jaclang-0.7.14.dist-info → jaclang-0.7.16.dist-info}/entry_points.txt +0 -0
jaclang/plugin/feature.py
CHANGED
|
@@ -2,11 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import ast as ast3
|
|
5
6
|
import types
|
|
6
|
-
from typing import Any, Callable, Optional, Type, TypeAlias, Union
|
|
7
|
+
from typing import Any, Callable, Mapping, Optional, Sequence, Type, TypeAlias, Union
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
from jaclang.
|
|
9
|
+
import jaclang.compiler.absyntree as ast
|
|
10
|
+
from jaclang.compiler.passes.main.pyast_gen_pass import PyastGenPass
|
|
11
|
+
from jaclang.plugin.default import ExecutionContext
|
|
12
|
+
from jaclang.plugin.spec import JacBuiltin, JacCmdSpec, JacFeatureSpec, P, T
|
|
13
|
+
from jaclang.runtimelib.constructs import (
|
|
10
14
|
Architype,
|
|
11
15
|
EdgeArchitype,
|
|
12
16
|
Memory,
|
|
@@ -14,9 +18,6 @@ from jaclang.core.constructs import (
|
|
|
14
18
|
Root,
|
|
15
19
|
WalkerArchitype,
|
|
16
20
|
)
|
|
17
|
-
from jaclang.plugin.default import ExecutionContext
|
|
18
|
-
from jaclang.plugin.spec import JacBuiltin, JacCmdSpec, JacFeatureSpec, T
|
|
19
|
-
|
|
20
21
|
|
|
21
22
|
import pluggy
|
|
22
23
|
|
|
@@ -29,10 +30,11 @@ pm.add_hookspecs(JacBuiltin)
|
|
|
29
30
|
class JacFeature:
|
|
30
31
|
"""Jac Feature."""
|
|
31
32
|
|
|
32
|
-
import
|
|
33
|
-
from jaclang.
|
|
34
|
-
from jaclang.core.constructs import DSFunc
|
|
33
|
+
from jaclang.compiler.constant import EdgeDir as EdgeDirType
|
|
34
|
+
from jaclang.runtimelib.constructs import DSFunc as DSFuncType
|
|
35
35
|
|
|
36
|
+
EdgeDir: TypeAlias = EdgeDirType
|
|
37
|
+
DSFunc: TypeAlias = DSFuncType
|
|
36
38
|
RootType: TypeAlias = Root
|
|
37
39
|
Obj: TypeAlias = Architype
|
|
38
40
|
Node: TypeAlias = NodeArchitype
|
|
@@ -94,6 +96,13 @@ class JacFeature:
|
|
|
94
96
|
"""Create a walker architype."""
|
|
95
97
|
return pm.hook.make_walker(on_entry=on_entry, on_exit=on_exit)
|
|
96
98
|
|
|
99
|
+
@staticmethod
|
|
100
|
+
def impl_patch_filename(
|
|
101
|
+
file_loc: str,
|
|
102
|
+
) -> Callable[[Callable[P, T]], Callable[P, T]]:
|
|
103
|
+
"""Update impl file location."""
|
|
104
|
+
return pm.hook.impl_patch_filename(file_loc=file_loc)
|
|
105
|
+
|
|
97
106
|
@staticmethod
|
|
98
107
|
def jac_import(
|
|
99
108
|
target: str,
|
|
@@ -102,9 +111,9 @@ class JacFeature:
|
|
|
102
111
|
cachable: bool = True,
|
|
103
112
|
mdl_alias: Optional[str] = None,
|
|
104
113
|
override_name: Optional[str] = None,
|
|
105
|
-
mod_bundle: Optional[Module | str] = None,
|
|
106
114
|
lng: Optional[str] = "jac",
|
|
107
115
|
items: Optional[dict[str, Union[str, Optional[str]]]] = None,
|
|
116
|
+
reload_module: Optional[bool] = False,
|
|
108
117
|
) -> tuple[types.ModuleType, ...]:
|
|
109
118
|
"""Core Import Process."""
|
|
110
119
|
return pm.hook.jac_import(
|
|
@@ -114,9 +123,9 @@ class JacFeature:
|
|
|
114
123
|
cachable=cachable,
|
|
115
124
|
mdl_alias=mdl_alias,
|
|
116
125
|
override_name=override_name,
|
|
117
|
-
mod_bundle=mod_bundle,
|
|
118
126
|
lng=lng,
|
|
119
127
|
items=items,
|
|
128
|
+
reload_module=reload_module,
|
|
120
129
|
)
|
|
121
130
|
|
|
122
131
|
@staticmethod
|
|
@@ -301,6 +310,8 @@ class JacFeature:
|
|
|
301
310
|
inputs: list[tuple[str, str, str, Any]],
|
|
302
311
|
outputs: tuple,
|
|
303
312
|
action: str,
|
|
313
|
+
_globals: dict,
|
|
314
|
+
_locals: Mapping,
|
|
304
315
|
) -> Any: # noqa: ANN401
|
|
305
316
|
"""Jac's with_llm feature."""
|
|
306
317
|
return pm.hook.with_llm(
|
|
@@ -313,8 +324,45 @@ class JacFeature:
|
|
|
313
324
|
inputs=inputs,
|
|
314
325
|
outputs=outputs,
|
|
315
326
|
action=action,
|
|
327
|
+
_globals=_globals,
|
|
328
|
+
_locals=_locals,
|
|
316
329
|
)
|
|
317
330
|
|
|
331
|
+
@staticmethod
|
|
332
|
+
def gen_llm_body(_pass: PyastGenPass, node: ast.Ability) -> list[ast3.AST]:
|
|
333
|
+
"""Generate the by LLM body."""
|
|
334
|
+
return pm.hook.gen_llm_body(_pass=_pass, node=node)
|
|
335
|
+
|
|
336
|
+
@staticmethod
|
|
337
|
+
def by_llm_call(
|
|
338
|
+
_pass: PyastGenPass,
|
|
339
|
+
model: ast3.AST,
|
|
340
|
+
model_params: dict[str, ast.Expr],
|
|
341
|
+
scope: ast3.AST,
|
|
342
|
+
inputs: Sequence[Optional[ast3.AST]],
|
|
343
|
+
outputs: Sequence[Optional[ast3.AST]] | ast3.Call,
|
|
344
|
+
action: Optional[ast3.AST],
|
|
345
|
+
include_info: list[tuple[str, ast3.AST]],
|
|
346
|
+
exclude_info: list[tuple[str, ast3.AST]],
|
|
347
|
+
) -> ast3.Call:
|
|
348
|
+
"""Return the LLM Call, e.g. _Jac.with_llm()."""
|
|
349
|
+
return pm.hook.by_llm_call(
|
|
350
|
+
_pass=_pass,
|
|
351
|
+
model=model,
|
|
352
|
+
model_params=model_params,
|
|
353
|
+
scope=scope,
|
|
354
|
+
inputs=inputs,
|
|
355
|
+
outputs=outputs,
|
|
356
|
+
action=action,
|
|
357
|
+
include_info=include_info,
|
|
358
|
+
exclude_info=exclude_info,
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
@staticmethod
|
|
362
|
+
def get_by_llm_call_args(_pass: PyastGenPass, node: ast.FuncCall) -> dict:
|
|
363
|
+
"""Get the by LLM call args."""
|
|
364
|
+
return pm.hook.get_by_llm_call_args(_pass=_pass, node=node)
|
|
365
|
+
|
|
318
366
|
|
|
319
367
|
class JacCmd:
|
|
320
368
|
"""Jac CLI command."""
|
jaclang/plugin/spec.py
CHANGED
|
@@ -2,13 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import ast as ast3
|
|
5
6
|
import types
|
|
6
|
-
from typing import
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
from typing import (
|
|
8
|
+
Any,
|
|
9
|
+
Callable,
|
|
10
|
+
Mapping,
|
|
11
|
+
Optional,
|
|
12
|
+
ParamSpec,
|
|
13
|
+
Sequence,
|
|
14
|
+
TYPE_CHECKING,
|
|
15
|
+
Type,
|
|
16
|
+
TypeVar,
|
|
17
|
+
Union,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
import jaclang.compiler.absyntree as ast
|
|
21
|
+
from jaclang.compiler.passes.main.pyast_gen_pass import PyastGenPass
|
|
9
22
|
|
|
10
23
|
if TYPE_CHECKING:
|
|
11
|
-
from jaclang.
|
|
24
|
+
from jaclang.runtimelib.constructs import EdgeArchitype, NodeArchitype
|
|
12
25
|
from jaclang.plugin.default import (
|
|
13
26
|
Architype,
|
|
14
27
|
EdgeDir,
|
|
@@ -17,13 +30,14 @@ if TYPE_CHECKING:
|
|
|
17
30
|
Root,
|
|
18
31
|
DSFunc,
|
|
19
32
|
)
|
|
20
|
-
from jaclang.
|
|
33
|
+
from jaclang.runtimelib.memory import Memory
|
|
21
34
|
|
|
22
35
|
import pluggy
|
|
23
36
|
|
|
24
37
|
hookspec = pluggy.HookspecMarker("jac")
|
|
25
38
|
|
|
26
39
|
T = TypeVar("T")
|
|
40
|
+
P = ParamSpec("P")
|
|
27
41
|
|
|
28
42
|
|
|
29
43
|
class JacFeatureSpec:
|
|
@@ -90,6 +104,14 @@ class JacFeatureSpec:
|
|
|
90
104
|
"""Create a walker architype."""
|
|
91
105
|
raise NotImplementedError
|
|
92
106
|
|
|
107
|
+
@staticmethod
|
|
108
|
+
@hookspec(firstresult=True)
|
|
109
|
+
def impl_patch_filename(
|
|
110
|
+
file_loc: str,
|
|
111
|
+
) -> Callable[[Callable[P, T]], Callable[P, T]]:
|
|
112
|
+
"""Update impl file location."""
|
|
113
|
+
raise NotImplementedError
|
|
114
|
+
|
|
93
115
|
@staticmethod
|
|
94
116
|
@hookspec(firstresult=True)
|
|
95
117
|
def jac_import(
|
|
@@ -99,9 +121,9 @@ class JacFeatureSpec:
|
|
|
99
121
|
cachable: bool,
|
|
100
122
|
mdl_alias: Optional[str],
|
|
101
123
|
override_name: Optional[str],
|
|
102
|
-
mod_bundle: Optional[Module | str],
|
|
103
124
|
lng: Optional[str],
|
|
104
125
|
items: Optional[dict[str, Union[str, Optional[str]]]],
|
|
126
|
+
reload_module: Optional[bool],
|
|
105
127
|
) -> tuple[types.ModuleType, ...]:
|
|
106
128
|
"""Core Import Process."""
|
|
107
129
|
raise NotImplementedError
|
|
@@ -283,10 +305,40 @@ class JacFeatureSpec:
|
|
|
283
305
|
inputs: list[tuple[str, str, str, Any]],
|
|
284
306
|
outputs: tuple,
|
|
285
307
|
action: str,
|
|
308
|
+
_globals: dict,
|
|
309
|
+
_locals: Mapping,
|
|
286
310
|
) -> Any: # noqa: ANN401
|
|
287
311
|
"""Jac's with_llm stmt feature."""
|
|
288
312
|
raise NotImplementedError
|
|
289
313
|
|
|
314
|
+
@staticmethod
|
|
315
|
+
@hookspec(firstresult=True)
|
|
316
|
+
def gen_llm_body(_pass: PyastGenPass, node: ast.Ability) -> list[ast3.AST]:
|
|
317
|
+
"""Generate the by LLM body."""
|
|
318
|
+
raise NotImplementedError
|
|
319
|
+
|
|
320
|
+
@staticmethod
|
|
321
|
+
@hookspec(firstresult=True)
|
|
322
|
+
def by_llm_call(
|
|
323
|
+
_pass: PyastGenPass,
|
|
324
|
+
model: ast3.AST,
|
|
325
|
+
model_params: dict[str, ast.Expr],
|
|
326
|
+
scope: ast3.AST,
|
|
327
|
+
inputs: Sequence[Optional[ast3.AST]],
|
|
328
|
+
outputs: Sequence[Optional[ast3.AST]] | ast3.Call,
|
|
329
|
+
action: Optional[ast3.AST],
|
|
330
|
+
include_info: list[tuple[str, ast3.AST]],
|
|
331
|
+
exclude_info: list[tuple[str, ast3.AST]],
|
|
332
|
+
) -> ast3.Call:
|
|
333
|
+
"""Return the LLM Call, e.g. _Jac.with_llm()."""
|
|
334
|
+
raise NotImplementedError
|
|
335
|
+
|
|
336
|
+
@staticmethod
|
|
337
|
+
@hookspec(firstresult=True)
|
|
338
|
+
def get_by_llm_call_args(_pass: PyastGenPass, node: ast.FuncCall) -> dict:
|
|
339
|
+
"""Get the by LLM call args."""
|
|
340
|
+
raise NotImplementedError
|
|
341
|
+
|
|
290
342
|
|
|
291
343
|
class JacBuiltin:
|
|
292
344
|
"""Jac Builtins."""
|
|
@@ -8,7 +8,7 @@ from typing import Any, Callable, Optional
|
|
|
8
8
|
from uuid import UUID, uuid4
|
|
9
9
|
|
|
10
10
|
from jaclang.compiler.constant import EdgeDir
|
|
11
|
-
from jaclang.
|
|
11
|
+
from jaclang.runtimelib.utils import collect_node_connections
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
@dataclass(eq=False)
|
|
@@ -8,6 +8,7 @@ from typing import Callable, Optional
|
|
|
8
8
|
from uuid import UUID
|
|
9
9
|
|
|
10
10
|
from .architype import Architype, Root
|
|
11
|
+
from .machine import JacMachine, JacProgram
|
|
11
12
|
from .memory import Memory, ShelveStorage
|
|
12
13
|
|
|
13
14
|
|
|
@@ -22,13 +23,19 @@ class ExecutionContext:
|
|
|
22
23
|
super().__init__()
|
|
23
24
|
self.mem = ShelveStorage()
|
|
24
25
|
self.root = None
|
|
26
|
+
self.jac_machine = JacMachine()
|
|
27
|
+
jac_program = JacProgram(mod_bundle=None, bytecode=None)
|
|
28
|
+
self.jac_machine.attach_program(jac_program)
|
|
25
29
|
|
|
26
|
-
def init_memory(self, session: str = "") -> None:
|
|
30
|
+
def init_memory(self, base_path: str = "", session: str = "") -> None:
|
|
27
31
|
"""Initialize memory."""
|
|
28
32
|
if session:
|
|
29
33
|
self.mem = ShelveStorage(session)
|
|
30
34
|
else:
|
|
31
35
|
self.mem = Memory()
|
|
36
|
+
self.jac_machine = JacMachine(base_path)
|
|
37
|
+
jac_program = JacProgram(mod_bundle=None, bytecode=None)
|
|
38
|
+
self.jac_machine.attach_program(jac_program)
|
|
32
39
|
|
|
33
40
|
def get_root(self) -> Root:
|
|
34
41
|
"""Get the root object."""
|
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
"""Special Imports for Jac Code."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import importlib
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
import types
|
|
9
|
+
from os import getcwd, path
|
|
10
|
+
from typing import Optional, Union
|
|
11
|
+
|
|
12
|
+
from jaclang.runtimelib.machine import JacMachine
|
|
13
|
+
from jaclang.runtimelib.utils import sys_path_context
|
|
14
|
+
from jaclang.utils.helpers import dump_traceback
|
|
15
|
+
from jaclang.utils.log import logging
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ImportPathSpec:
|
|
21
|
+
"""Import Specification."""
|
|
22
|
+
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
target: str,
|
|
26
|
+
base_path: str,
|
|
27
|
+
absorb: bool,
|
|
28
|
+
cachable: bool,
|
|
29
|
+
mdl_alias: Optional[str],
|
|
30
|
+
override_name: Optional[str],
|
|
31
|
+
lng: Optional[str],
|
|
32
|
+
items: Optional[dict[str, Union[str, Optional[str]]]],
|
|
33
|
+
) -> None:
|
|
34
|
+
"""Initialize the ImportPathSpec object."""
|
|
35
|
+
self.target = target
|
|
36
|
+
self.base_path = base_path
|
|
37
|
+
self.absorb = absorb
|
|
38
|
+
self.cachable = cachable
|
|
39
|
+
self.mdl_alias = mdl_alias
|
|
40
|
+
self.override_name = override_name
|
|
41
|
+
self.language = lng
|
|
42
|
+
self.items = items
|
|
43
|
+
self.dir_path, self.file_name = path.split(path.join(*(target.split("."))))
|
|
44
|
+
self.module_name = path.splitext(self.file_name)[0]
|
|
45
|
+
self.package_path = self.dir_path.replace(path.sep, ".")
|
|
46
|
+
self.caller_dir = self.get_caller_dir()
|
|
47
|
+
self.full_target = path.abspath(path.join(self.caller_dir, self.file_name))
|
|
48
|
+
|
|
49
|
+
def get_caller_dir(self) -> str:
|
|
50
|
+
"""Get the directory of the caller."""
|
|
51
|
+
caller_dir = (
|
|
52
|
+
self.base_path
|
|
53
|
+
if path.isdir(self.base_path)
|
|
54
|
+
else path.dirname(self.base_path)
|
|
55
|
+
)
|
|
56
|
+
caller_dir = caller_dir if caller_dir else getcwd()
|
|
57
|
+
chomp_target = self.target
|
|
58
|
+
if chomp_target.startswith("."):
|
|
59
|
+
chomp_target = chomp_target[1:]
|
|
60
|
+
while chomp_target.startswith("."):
|
|
61
|
+
caller_dir = path.dirname(caller_dir)
|
|
62
|
+
chomp_target = chomp_target[1:]
|
|
63
|
+
return path.join(caller_dir, self.dir_path)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class ImportReturn:
|
|
67
|
+
"""Import Return Object."""
|
|
68
|
+
|
|
69
|
+
def __init__(
|
|
70
|
+
self,
|
|
71
|
+
ret_mod: types.ModuleType,
|
|
72
|
+
ret_items: list[types.ModuleType],
|
|
73
|
+
importer: Importer,
|
|
74
|
+
) -> None:
|
|
75
|
+
"""Initialize the ImportReturn object."""
|
|
76
|
+
self.ret_mod = ret_mod
|
|
77
|
+
self.ret_items = ret_items
|
|
78
|
+
self.importer = importer
|
|
79
|
+
|
|
80
|
+
def process_items(
|
|
81
|
+
self,
|
|
82
|
+
module: types.ModuleType,
|
|
83
|
+
items: dict[str, Union[str, Optional[str]]],
|
|
84
|
+
caller_dir: str,
|
|
85
|
+
lang: Optional[str],
|
|
86
|
+
cachable: bool = True,
|
|
87
|
+
) -> None:
|
|
88
|
+
"""Process items within a module by handling renaming and potentially loading missing attributes."""
|
|
89
|
+
|
|
90
|
+
def handle_item_loading(
|
|
91
|
+
item: types.ModuleType, alias: Union[str, Optional[str]]
|
|
92
|
+
) -> None:
|
|
93
|
+
if item:
|
|
94
|
+
self.ret_items.append(item)
|
|
95
|
+
setattr(module, name, item)
|
|
96
|
+
if alias and alias != name and not isinstance(alias, bool):
|
|
97
|
+
setattr(module, alias, item)
|
|
98
|
+
|
|
99
|
+
for name, alias in items.items():
|
|
100
|
+
try:
|
|
101
|
+
item = getattr(module, name)
|
|
102
|
+
handle_item_loading(item, alias)
|
|
103
|
+
except AttributeError:
|
|
104
|
+
if lang == "jac":
|
|
105
|
+
jac_file_path = (
|
|
106
|
+
os.path.join(module.__path__[0], f"{name}.jac")
|
|
107
|
+
if hasattr(module, "__path__")
|
|
108
|
+
else module.__file__
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
if jac_file_path and os.path.isfile(jac_file_path):
|
|
112
|
+
item = self.load_jac_mod_as_item(
|
|
113
|
+
module=module,
|
|
114
|
+
name=name,
|
|
115
|
+
jac_file_path=jac_file_path,
|
|
116
|
+
cachable=cachable,
|
|
117
|
+
caller_dir=caller_dir,
|
|
118
|
+
)
|
|
119
|
+
handle_item_loading(item, alias)
|
|
120
|
+
else:
|
|
121
|
+
if hasattr(module, "__path__"):
|
|
122
|
+
full_module_name = f"{module.__name__}.{name}"
|
|
123
|
+
item = importlib.import_module(full_module_name)
|
|
124
|
+
handle_item_loading(item, alias)
|
|
125
|
+
|
|
126
|
+
def load_jac_mod_as_item(
|
|
127
|
+
self,
|
|
128
|
+
module: types.ModuleType,
|
|
129
|
+
name: str,
|
|
130
|
+
jac_file_path: str,
|
|
131
|
+
cachable: bool,
|
|
132
|
+
caller_dir: str,
|
|
133
|
+
) -> Optional[types.ModuleType]:
|
|
134
|
+
"""Load a single .jac file into the specified module component."""
|
|
135
|
+
try:
|
|
136
|
+
package_name = (
|
|
137
|
+
f"{module.__name__}.{name}"
|
|
138
|
+
if hasattr(module, "__path__")
|
|
139
|
+
else module.__name__
|
|
140
|
+
)
|
|
141
|
+
if isinstance(self.importer, JacImporter):
|
|
142
|
+
new_module = sys.modules.get(
|
|
143
|
+
package_name,
|
|
144
|
+
self.importer.create_jac_py_module(
|
|
145
|
+
self.importer.get_sys_mod_name(jac_file_path),
|
|
146
|
+
module.__name__,
|
|
147
|
+
jac_file_path,
|
|
148
|
+
),
|
|
149
|
+
)
|
|
150
|
+
codeobj = self.importer.jac_machine.get_bytecode(
|
|
151
|
+
name, jac_file_path, caller_dir=caller_dir, cachable=cachable
|
|
152
|
+
)
|
|
153
|
+
if not codeobj:
|
|
154
|
+
raise ImportError(f"No bytecode found for {jac_file_path}")
|
|
155
|
+
|
|
156
|
+
exec(codeobj, new_module.__dict__)
|
|
157
|
+
return getattr(new_module, name, new_module)
|
|
158
|
+
except ImportError as e:
|
|
159
|
+
logger.error(dump_traceback(e))
|
|
160
|
+
# logger.error(
|
|
161
|
+
# f"Failed to load {name} from {jac_file_path} in {module.__name__}: {str(e)}"
|
|
162
|
+
# )
|
|
163
|
+
return None
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class Importer:
|
|
167
|
+
"""Abstract base class for all importers."""
|
|
168
|
+
|
|
169
|
+
def __init__(self, jac_machine: JacMachine) -> None:
|
|
170
|
+
"""Initialize the Importer object."""
|
|
171
|
+
self.jac_machine = jac_machine
|
|
172
|
+
self.result: Optional[ImportReturn] = None
|
|
173
|
+
|
|
174
|
+
def run_import(self, spec: ImportPathSpec) -> ImportReturn:
|
|
175
|
+
"""Run the import process."""
|
|
176
|
+
raise NotImplementedError
|
|
177
|
+
|
|
178
|
+
def update_sys(self, module: types.ModuleType, spec: ImportPathSpec) -> None:
|
|
179
|
+
"""Update sys.modules with the newly imported module."""
|
|
180
|
+
if spec.module_name not in sys.modules:
|
|
181
|
+
sys.modules[spec.module_name] = module
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
class PythonImporter(Importer):
|
|
185
|
+
"""Importer for Python modules."""
|
|
186
|
+
|
|
187
|
+
def __init__(self, jac_machine: JacMachine) -> None:
|
|
188
|
+
"""Initialize the PythonImporter object."""
|
|
189
|
+
self.jac_machine = jac_machine
|
|
190
|
+
|
|
191
|
+
def run_import(self, spec: ImportPathSpec) -> ImportReturn:
|
|
192
|
+
"""Run the import process for Python modules."""
|
|
193
|
+
try:
|
|
194
|
+
loaded_items: list = []
|
|
195
|
+
if spec.target.startswith("."):
|
|
196
|
+
spec.target = spec.target.lstrip(".")
|
|
197
|
+
full_target = path.normpath(path.join(spec.caller_dir, spec.target))
|
|
198
|
+
imp_spec = importlib.util.spec_from_file_location(
|
|
199
|
+
spec.target, full_target + ".py"
|
|
200
|
+
)
|
|
201
|
+
if imp_spec and imp_spec.loader:
|
|
202
|
+
imported_module = importlib.util.module_from_spec(imp_spec)
|
|
203
|
+
sys.modules[imp_spec.name] = imported_module
|
|
204
|
+
imp_spec.loader.exec_module(imported_module)
|
|
205
|
+
else:
|
|
206
|
+
raise ImportError(
|
|
207
|
+
f"Cannot find module {spec.target} at {full_target}"
|
|
208
|
+
)
|
|
209
|
+
else:
|
|
210
|
+
imported_module = importlib.import_module(name=spec.target)
|
|
211
|
+
|
|
212
|
+
main_module = __import__("__main__")
|
|
213
|
+
if spec.absorb:
|
|
214
|
+
for name in dir(imported_module):
|
|
215
|
+
if not name.startswith("_"):
|
|
216
|
+
setattr(main_module, name, getattr(imported_module, name))
|
|
217
|
+
|
|
218
|
+
elif spec.items:
|
|
219
|
+
for name, alias in spec.items.items():
|
|
220
|
+
if isinstance(alias, bool):
|
|
221
|
+
alias = name
|
|
222
|
+
try:
|
|
223
|
+
item = getattr(imported_module, name)
|
|
224
|
+
if item not in loaded_items:
|
|
225
|
+
setattr(
|
|
226
|
+
main_module,
|
|
227
|
+
alias if isinstance(alias, str) else name,
|
|
228
|
+
item,
|
|
229
|
+
)
|
|
230
|
+
loaded_items.append(item)
|
|
231
|
+
except AttributeError as e:
|
|
232
|
+
if hasattr(imported_module, "__path__"):
|
|
233
|
+
item = importlib.import_module(f"{spec.target}.{name}")
|
|
234
|
+
if item not in loaded_items:
|
|
235
|
+
setattr(
|
|
236
|
+
main_module,
|
|
237
|
+
alias if isinstance(alias, str) else name,
|
|
238
|
+
item,
|
|
239
|
+
)
|
|
240
|
+
loaded_items.append(item)
|
|
241
|
+
else:
|
|
242
|
+
raise e
|
|
243
|
+
|
|
244
|
+
else:
|
|
245
|
+
setattr(
|
|
246
|
+
__import__("__main__"),
|
|
247
|
+
spec.mdl_alias if isinstance(spec.mdl_alias, str) else spec.target,
|
|
248
|
+
imported_module,
|
|
249
|
+
)
|
|
250
|
+
self.result = ImportReturn(imported_module, loaded_items, self)
|
|
251
|
+
return self.result
|
|
252
|
+
|
|
253
|
+
except ImportError as e:
|
|
254
|
+
raise e
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
class JacImporter(Importer):
|
|
258
|
+
"""Importer for Jac modules."""
|
|
259
|
+
|
|
260
|
+
def __init__(self, jac_machine: JacMachine) -> None:
|
|
261
|
+
"""Initialize the JacImporter object."""
|
|
262
|
+
self.jac_machine = jac_machine
|
|
263
|
+
|
|
264
|
+
def get_sys_mod_name(self, full_target: str) -> str:
|
|
265
|
+
"""Generate the system module name based on full target path and package path."""
|
|
266
|
+
if full_target == self.jac_machine.base_path_dir:
|
|
267
|
+
return path.basename(self.jac_machine.base_path_dir)
|
|
268
|
+
relative_path = path.relpath(full_target, start=self.jac_machine.base_path_dir)
|
|
269
|
+
base_name = path.splitext(relative_path)[0]
|
|
270
|
+
sys_mod_name = base_name.replace(os.sep, ".").strip(".")
|
|
271
|
+
return sys_mod_name
|
|
272
|
+
|
|
273
|
+
def handle_directory(
|
|
274
|
+
self, module_name: str, full_mod_path: str
|
|
275
|
+
) -> types.ModuleType:
|
|
276
|
+
"""Import from a directory that potentially contains multiple Jac modules."""
|
|
277
|
+
module_name = self.get_sys_mod_name(full_mod_path)
|
|
278
|
+
module = types.ModuleType(module_name)
|
|
279
|
+
module.__name__ = module_name
|
|
280
|
+
module.__path__ = [full_mod_path]
|
|
281
|
+
module.__file__ = None
|
|
282
|
+
|
|
283
|
+
if module_name not in sys.modules:
|
|
284
|
+
sys.modules[module_name] = module
|
|
285
|
+
return module
|
|
286
|
+
|
|
287
|
+
def create_jac_py_module(
|
|
288
|
+
self,
|
|
289
|
+
module_name: str,
|
|
290
|
+
package_path: str,
|
|
291
|
+
full_target: str,
|
|
292
|
+
) -> types.ModuleType:
|
|
293
|
+
"""Create a module."""
|
|
294
|
+
module = types.ModuleType(module_name)
|
|
295
|
+
module.__file__ = full_target
|
|
296
|
+
module.__name__ = module_name
|
|
297
|
+
if package_path:
|
|
298
|
+
base_path = full_target.split(package_path.replace(".", path.sep))[0]
|
|
299
|
+
parts = package_path.split(".")
|
|
300
|
+
for i in range(len(parts)):
|
|
301
|
+
package_name = ".".join(parts[: i + 1])
|
|
302
|
+
if package_name not in sys.modules:
|
|
303
|
+
full_mod_path = path.join(
|
|
304
|
+
base_path, package_name.replace(".", path.sep)
|
|
305
|
+
)
|
|
306
|
+
self.handle_directory(
|
|
307
|
+
module_name=package_name,
|
|
308
|
+
full_mod_path=full_mod_path,
|
|
309
|
+
)
|
|
310
|
+
sys.modules[module_name] = module
|
|
311
|
+
return module
|
|
312
|
+
|
|
313
|
+
def run_import(
|
|
314
|
+
self, spec: ImportPathSpec, reload: Optional[bool] = False
|
|
315
|
+
) -> ImportReturn:
|
|
316
|
+
"""Run the import process for Jac modules."""
|
|
317
|
+
unique_loaded_items: list[types.ModuleType] = []
|
|
318
|
+
module = None
|
|
319
|
+
if os.path.isfile(spec.full_target + ".jac"):
|
|
320
|
+
module_name = self.get_sys_mod_name(spec.full_target + ".jac")
|
|
321
|
+
module_name = spec.override_name if spec.override_name else module_name
|
|
322
|
+
else:
|
|
323
|
+
module_name = self.get_sys_mod_name(spec.full_target)
|
|
324
|
+
|
|
325
|
+
module = sys.modules.get(module_name)
|
|
326
|
+
|
|
327
|
+
if not module or module.__name__ == "__main__" or reload:
|
|
328
|
+
if os.path.isdir(spec.full_target):
|
|
329
|
+
module = self.handle_directory(spec.module_name, spec.full_target)
|
|
330
|
+
else:
|
|
331
|
+
spec.full_target += ".jac" if spec.language == "jac" else ".py"
|
|
332
|
+
module = self.create_jac_py_module(
|
|
333
|
+
module_name,
|
|
334
|
+
spec.package_path,
|
|
335
|
+
spec.full_target,
|
|
336
|
+
)
|
|
337
|
+
codeobj = self.jac_machine.get_bytecode(
|
|
338
|
+
module_name,
|
|
339
|
+
spec.full_target,
|
|
340
|
+
caller_dir=spec.caller_dir,
|
|
341
|
+
cachable=spec.cachable,
|
|
342
|
+
)
|
|
343
|
+
if not codeobj:
|
|
344
|
+
raise ImportError(f"No bytecode found for {spec.full_target}")
|
|
345
|
+
with sys_path_context(spec.caller_dir):
|
|
346
|
+
try:
|
|
347
|
+
exec(codeobj, module.__dict__)
|
|
348
|
+
except Exception as e:
|
|
349
|
+
logger.error(dump_traceback(e))
|
|
350
|
+
raise e
|
|
351
|
+
import_return = ImportReturn(module, unique_loaded_items, self)
|
|
352
|
+
if spec.items:
|
|
353
|
+
import_return.process_items(
|
|
354
|
+
module=module,
|
|
355
|
+
items=spec.items,
|
|
356
|
+
caller_dir=spec.caller_dir,
|
|
357
|
+
cachable=spec.cachable,
|
|
358
|
+
lang=spec.language,
|
|
359
|
+
)
|
|
360
|
+
self.result = import_return
|
|
361
|
+
return self.result
|