jaclang 0.7.9__py3-none-any.whl → 0.7.13__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 +13 -3
- jaclang/compiler/absyntree.py +10 -2
- jaclang/compiler/parser.py +2 -1
- jaclang/compiler/passes/ir_pass.py +9 -0
- jaclang/compiler/passes/main/import_pass.py +15 -2
- jaclang/compiler/passes/main/pyast_gen_pass.py +238 -32
- jaclang/compiler/passes/main/pyast_load_pass.py +3 -1
- jaclang/compiler/passes/main/tests/test_import_pass.py +13 -0
- jaclang/compiler/passes/main/tests/test_pyast_gen_pass.py +2 -2
- jaclang/compiler/passes/main/tests/test_sub_node_pass.py +1 -3
- jaclang/compiler/passes/main/type_check_pass.py +0 -17
- jaclang/compiler/passes/tool/tests/test_unparse_validate.py +1 -2
- jaclang/compiler/tests/test_importer.py +1 -1
- jaclang/core/importer.py +233 -62
- jaclang/langserve/engine.py +182 -136
- jaclang/langserve/server.py +29 -7
- jaclang/langserve/tests/fixtures/base_module_structure.jac +28 -2
- jaclang/langserve/tests/fixtures/import_include_statements.jac +1 -1
- jaclang/langserve/tests/test_server.py +73 -66
- jaclang/langserve/utils.py +266 -0
- jaclang/plugin/default.py +7 -4
- jaclang/plugin/feature.py +3 -3
- jaclang/plugin/spec.py +3 -3
- jaclang/settings.py +1 -0
- jaclang/tests/fixtures/deep/one_lev.jac +6 -4
- jaclang/tests/fixtures/needs_import.jac +1 -1
- jaclang/tests/test_cli.py +7 -9
- jaclang/tests/test_language.py +9 -13
- jaclang/tests/test_man_code.py +8 -10
- jaclang/utils/helpers.py +3 -3
- jaclang/utils/test.py +8 -0
- {jaclang-0.7.9.dist-info → jaclang-0.7.13.dist-info}/METADATA +2 -2
- {jaclang-0.7.9.dist-info → jaclang-0.7.13.dist-info}/RECORD +35 -35
- {jaclang-0.7.9.dist-info → jaclang-0.7.13.dist-info}/WHEEL +0 -0
- {jaclang-0.7.9.dist-info → jaclang-0.7.13.dist-info}/entry_points.txt +0 -0
jaclang/core/importer.py
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import importlib
|
|
4
4
|
import marshal
|
|
5
|
+
import os
|
|
5
6
|
import sys
|
|
6
7
|
import types
|
|
7
8
|
from os import getcwd, path
|
|
@@ -13,6 +14,137 @@ from jaclang.compiler.constant import Constants as Con
|
|
|
13
14
|
from jaclang.core.utils import sys_path_context
|
|
14
15
|
|
|
15
16
|
|
|
17
|
+
def handle_directory(
|
|
18
|
+
module_name: str, full_mod_path: str, mod_bundle: Optional[Module]
|
|
19
|
+
) -> types.ModuleType:
|
|
20
|
+
"""Import from a directory that potentially contains multiple Jac modules."""
|
|
21
|
+
module = types.ModuleType(module_name)
|
|
22
|
+
module.__name__ = module_name
|
|
23
|
+
module.__path__ = [full_mod_path]
|
|
24
|
+
module.__file__ = None
|
|
25
|
+
module.__dict__["__jac_mod_bundle__"] = mod_bundle
|
|
26
|
+
if module_name not in sys.modules:
|
|
27
|
+
sys.modules[module_name] = module
|
|
28
|
+
return module
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def process_items(
|
|
32
|
+
module: types.ModuleType,
|
|
33
|
+
items: dict[str, Union[str, Optional[str]]],
|
|
34
|
+
caller_dir: str,
|
|
35
|
+
lang: Optional[str],
|
|
36
|
+
mod_bundle: Optional[Module] = None,
|
|
37
|
+
cachable: bool = True,
|
|
38
|
+
) -> list:
|
|
39
|
+
"""Process items within a module by handling renaming and potentially loading missing attributes."""
|
|
40
|
+
unique_loaded_items = []
|
|
41
|
+
|
|
42
|
+
def handle_item_loading(item: str, alias: Union[str, Optional[str]]) -> None:
|
|
43
|
+
if item:
|
|
44
|
+
unique_loaded_items.append(item)
|
|
45
|
+
setattr(module, name, item)
|
|
46
|
+
if alias and alias != name and not isinstance(alias, bool):
|
|
47
|
+
setattr(module, alias, item)
|
|
48
|
+
|
|
49
|
+
for name, alias in items.items():
|
|
50
|
+
try:
|
|
51
|
+
item = getattr(module, name)
|
|
52
|
+
handle_item_loading(item, alias)
|
|
53
|
+
except AttributeError:
|
|
54
|
+
if lang == "jac":
|
|
55
|
+
jac_file_path = (
|
|
56
|
+
os.path.join(module.__path__[0], f"{name}.jac")
|
|
57
|
+
if hasattr(module, "__path__")
|
|
58
|
+
else module.__file__
|
|
59
|
+
)
|
|
60
|
+
if jac_file_path and os.path.isfile(jac_file_path):
|
|
61
|
+
item = load_jac_file(
|
|
62
|
+
module=module,
|
|
63
|
+
name=name,
|
|
64
|
+
jac_file_path=jac_file_path,
|
|
65
|
+
mod_bundle=mod_bundle,
|
|
66
|
+
cachable=cachable,
|
|
67
|
+
caller_dir=caller_dir,
|
|
68
|
+
)
|
|
69
|
+
handle_item_loading(item, alias)
|
|
70
|
+
elif jac_file_path and os.path.isdir(jac_file_path[:-4]):
|
|
71
|
+
item = handle_directory(name, jac_file_path[:-4], mod_bundle)
|
|
72
|
+
handle_item_loading(item, alias)
|
|
73
|
+
else:
|
|
74
|
+
if hasattr(module, "__path__"):
|
|
75
|
+
full_module_name = f"{module.__name__}.{name}"
|
|
76
|
+
item = importlib.import_module(full_module_name)
|
|
77
|
+
handle_item_loading(item, alias)
|
|
78
|
+
return unique_loaded_items
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def load_jac_file(
|
|
82
|
+
module: types.ModuleType,
|
|
83
|
+
name: str,
|
|
84
|
+
jac_file_path: str,
|
|
85
|
+
mod_bundle: Optional[Module],
|
|
86
|
+
cachable: bool,
|
|
87
|
+
caller_dir: str,
|
|
88
|
+
) -> Optional[types.ModuleType]:
|
|
89
|
+
"""Load a single .jac file into the specified module component."""
|
|
90
|
+
try:
|
|
91
|
+
package_name = (
|
|
92
|
+
f"{module.__name__}.{name}"
|
|
93
|
+
if hasattr(module, "__path__")
|
|
94
|
+
else module.__name__
|
|
95
|
+
)
|
|
96
|
+
new_module = sys.modules.get(
|
|
97
|
+
package_name,
|
|
98
|
+
create_jac_py_module(mod_bundle, name, module.__name__, jac_file_path),
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
codeobj = get_codeobj(
|
|
102
|
+
full_target=jac_file_path,
|
|
103
|
+
module_name=name,
|
|
104
|
+
mod_bundle=mod_bundle,
|
|
105
|
+
cachable=cachable,
|
|
106
|
+
caller_dir=caller_dir,
|
|
107
|
+
)
|
|
108
|
+
if not codeobj:
|
|
109
|
+
raise ImportError(f"No bytecode found for {jac_file_path}")
|
|
110
|
+
|
|
111
|
+
exec(codeobj, new_module.__dict__)
|
|
112
|
+
return getattr(new_module, name, new_module)
|
|
113
|
+
except ImportError as e:
|
|
114
|
+
print(
|
|
115
|
+
f"Failed to load {name} from {jac_file_path} in {module.__name__}: {str(e)}"
|
|
116
|
+
)
|
|
117
|
+
return None
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def get_codeobj(
|
|
121
|
+
full_target: str,
|
|
122
|
+
module_name: str,
|
|
123
|
+
mod_bundle: Optional[Module],
|
|
124
|
+
cachable: bool,
|
|
125
|
+
caller_dir: str,
|
|
126
|
+
) -> Optional[types.CodeType]:
|
|
127
|
+
"""Execcutes the code for a given module."""
|
|
128
|
+
if mod_bundle:
|
|
129
|
+
codeobj = mod_bundle.mod_deps[full_target].gen.py_bytecode
|
|
130
|
+
return marshal.loads(codeobj) if isinstance(codeobj, bytes) else None
|
|
131
|
+
gen_dir = os.path.join(caller_dir, Con.JAC_GEN_DIR)
|
|
132
|
+
pyc_file_path = os.path.join(gen_dir, module_name + ".jbc")
|
|
133
|
+
if cachable and os.path.exists(pyc_file_path):
|
|
134
|
+
with open(pyc_file_path, "rb") as f:
|
|
135
|
+
return marshal.load(f)
|
|
136
|
+
|
|
137
|
+
result = compile_jac(full_target, cache_result=cachable)
|
|
138
|
+
if result.errors_had or not result.ir.gen.py_bytecode:
|
|
139
|
+
for e in result.errors_had:
|
|
140
|
+
print(e)
|
|
141
|
+
return None
|
|
142
|
+
if result.ir.gen.py_bytecode is not None:
|
|
143
|
+
return marshal.loads(result.ir.gen.py_bytecode)
|
|
144
|
+
else:
|
|
145
|
+
return None
|
|
146
|
+
|
|
147
|
+
|
|
16
148
|
def jac_importer(
|
|
17
149
|
target: str,
|
|
18
150
|
base_path: str,
|
|
@@ -22,24 +154,23 @@ def jac_importer(
|
|
|
22
154
|
override_name: Optional[str] = None,
|
|
23
155
|
mod_bundle: Optional[Module | str] = None,
|
|
24
156
|
lng: Optional[str] = "jac",
|
|
25
|
-
items: Optional[dict[str, Union[str,
|
|
26
|
-
) ->
|
|
157
|
+
items: Optional[dict[str, Union[str, Optional[str]]]] = None,
|
|
158
|
+
) -> tuple[types.ModuleType, ...]:
|
|
27
159
|
"""Core Import Process."""
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
)
|
|
160
|
+
unique_loaded_items = []
|
|
161
|
+
dir_path, file_name = path.split(path.join(*(target.split("."))))
|
|
31
162
|
module_name = path.splitext(file_name)[0]
|
|
32
163
|
package_path = dir_path.replace(path.sep, ".")
|
|
33
|
-
|
|
34
164
|
if (
|
|
35
165
|
not override_name
|
|
36
166
|
and package_path
|
|
37
167
|
and f"{package_path}.{module_name}" in sys.modules
|
|
38
168
|
):
|
|
39
|
-
|
|
169
|
+
module = sys.modules[f"{package_path}.{module_name}"]
|
|
40
170
|
elif not override_name and not package_path and module_name in sys.modules:
|
|
41
|
-
|
|
42
|
-
|
|
171
|
+
module = sys.modules[module_name]
|
|
172
|
+
else:
|
|
173
|
+
module = None
|
|
43
174
|
valid_mod_bundle = (
|
|
44
175
|
sys.modules[mod_bundle].__jac_mod_bundle__
|
|
45
176
|
if isinstance(mod_bundle, str)
|
|
@@ -50,47 +181,61 @@ def jac_importer(
|
|
|
50
181
|
|
|
51
182
|
caller_dir = get_caller_dir(target, base_path, dir_path)
|
|
52
183
|
full_target = path.normpath(path.join(caller_dir, file_name))
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
target=target, items=items, absorb=absorb, mdl_alias=mdl_alias
|
|
57
|
-
)
|
|
58
|
-
else:
|
|
59
|
-
module_name = override_name if override_name else module_name
|
|
60
|
-
module = create_jac_py_module(
|
|
61
|
-
valid_mod_bundle, module_name, package_path, full_target
|
|
62
|
-
)
|
|
63
|
-
if valid_mod_bundle:
|
|
64
|
-
codeobj = valid_mod_bundle.mod_deps[full_target].gen.py_bytecode
|
|
65
|
-
codeobj = marshal.loads(codeobj) if isinstance(codeobj, bytes) else None
|
|
184
|
+
if not module:
|
|
185
|
+
if os.path.isdir(full_target):
|
|
186
|
+
module = handle_directory(module_name, full_target, valid_mod_bundle)
|
|
66
187
|
else:
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
cachable
|
|
71
|
-
and path.exists(pyc_file_path)
|
|
72
|
-
and path.getmtime(pyc_file_path) > path.getmtime(full_target)
|
|
73
|
-
):
|
|
74
|
-
with open(pyc_file_path, "rb") as f:
|
|
75
|
-
codeobj = marshal.load(f)
|
|
76
|
-
else:
|
|
77
|
-
result = compile_jac(full_target, cache_result=cachable)
|
|
78
|
-
if result.errors_had or not result.ir.gen.py_bytecode:
|
|
79
|
-
# for e in result.errors_had:
|
|
80
|
-
# print(e)
|
|
81
|
-
return None
|
|
82
|
-
else:
|
|
83
|
-
codeobj = marshal.loads(result.ir.gen.py_bytecode)
|
|
84
|
-
if not codeobj:
|
|
85
|
-
raise ImportError(f"No bytecode found for {full_target}")
|
|
86
|
-
with sys_path_context(caller_dir):
|
|
87
|
-
exec(codeobj, module.__dict__)
|
|
188
|
+
full_target += ".jac" if lng == "jac" else ".py"
|
|
189
|
+
module_name = path.splitext(file_name)[0]
|
|
190
|
+
package_path = dir_path.replace(path.sep, ".")
|
|
88
191
|
|
|
89
|
-
|
|
192
|
+
if lng == "py":
|
|
193
|
+
module, *unique_loaded_items = py_import(
|
|
194
|
+
target=target,
|
|
195
|
+
items=items,
|
|
196
|
+
absorb=absorb,
|
|
197
|
+
mdl_alias=mdl_alias,
|
|
198
|
+
caller_dir=caller_dir,
|
|
199
|
+
)
|
|
200
|
+
else:
|
|
201
|
+
module_name = override_name if override_name else module_name
|
|
202
|
+
module = create_jac_py_module(
|
|
203
|
+
valid_mod_bundle,
|
|
204
|
+
module_name,
|
|
205
|
+
package_path,
|
|
206
|
+
full_target,
|
|
207
|
+
)
|
|
208
|
+
codeobj = get_codeobj(
|
|
209
|
+
full_target,
|
|
210
|
+
module_name,
|
|
211
|
+
valid_mod_bundle,
|
|
212
|
+
cachable,
|
|
213
|
+
caller_dir=caller_dir,
|
|
214
|
+
)
|
|
215
|
+
try:
|
|
216
|
+
if not codeobj:
|
|
217
|
+
raise ImportError(f"No bytecode found for {full_target}")
|
|
218
|
+
with sys_path_context(caller_dir):
|
|
219
|
+
exec(codeobj, module.__dict__)
|
|
220
|
+
except Exception as e:
|
|
221
|
+
raise ImportError(f"Error importing {full_target}: {str(e)}")
|
|
222
|
+
unique_loaded_items = (
|
|
223
|
+
process_items(
|
|
224
|
+
module=module,
|
|
225
|
+
items=items,
|
|
226
|
+
caller_dir=caller_dir,
|
|
227
|
+
mod_bundle=valid_mod_bundle,
|
|
228
|
+
cachable=cachable,
|
|
229
|
+
lang=lng,
|
|
230
|
+
)
|
|
231
|
+
if items
|
|
232
|
+
else []
|
|
233
|
+
)
|
|
234
|
+
return (module,) if absorb or not items else tuple(unique_loaded_items)
|
|
90
235
|
|
|
91
236
|
|
|
92
237
|
def create_jac_py_module(
|
|
93
|
-
mod_bundle: Optional[Module
|
|
238
|
+
mod_bundle: Optional[Module],
|
|
94
239
|
module_name: str,
|
|
95
240
|
package_path: str,
|
|
96
241
|
full_target: str,
|
|
@@ -102,10 +247,18 @@ def create_jac_py_module(
|
|
|
102
247
|
module.__dict__["__jac_mod_bundle__"] = mod_bundle
|
|
103
248
|
if package_path:
|
|
104
249
|
parts = package_path.split(".")
|
|
250
|
+
base_path = full_target.split(package_path.replace(".", path.sep))[0]
|
|
105
251
|
for i in range(len(parts)):
|
|
106
252
|
package_name = ".".join(parts[: i + 1])
|
|
107
253
|
if package_name not in sys.modules:
|
|
108
|
-
|
|
254
|
+
full_mod_path = path.join(
|
|
255
|
+
base_path, package_name.replace(".", path.sep)
|
|
256
|
+
)
|
|
257
|
+
handle_directory(
|
|
258
|
+
module_name=package_name,
|
|
259
|
+
full_mod_path=full_mod_path,
|
|
260
|
+
mod_bundle=mod_bundle,
|
|
261
|
+
)
|
|
109
262
|
|
|
110
263
|
setattr(sys.modules[package_path], module_name, module)
|
|
111
264
|
sys.modules[f"{package_path}.{module_name}"] = module
|
|
@@ -129,14 +282,26 @@ def get_caller_dir(target: str, base_path: str, dir_path: str) -> str:
|
|
|
129
282
|
|
|
130
283
|
def py_import(
|
|
131
284
|
target: str,
|
|
132
|
-
|
|
285
|
+
caller_dir: str,
|
|
286
|
+
items: Optional[dict[str, Union[str, Optional[str]]]] = None,
|
|
133
287
|
absorb: bool = False,
|
|
134
288
|
mdl_alias: Optional[str] = None,
|
|
135
|
-
) -> types.ModuleType:
|
|
289
|
+
) -> tuple[types.ModuleType, ...]:
|
|
136
290
|
"""Import a Python module."""
|
|
137
291
|
try:
|
|
138
|
-
|
|
139
|
-
|
|
292
|
+
loaded_items: list = []
|
|
293
|
+
if target.startswith("."):
|
|
294
|
+
target = target.lstrip(".")
|
|
295
|
+
full_target = path.normpath(path.join(caller_dir, target))
|
|
296
|
+
spec = importlib.util.spec_from_file_location(target, full_target + ".py")
|
|
297
|
+
if spec and spec.loader:
|
|
298
|
+
imported_module = importlib.util.module_from_spec(spec)
|
|
299
|
+
sys.modules[spec.name] = imported_module
|
|
300
|
+
spec.loader.exec_module(imported_module)
|
|
301
|
+
else:
|
|
302
|
+
raise ImportError(f"Cannot find module {target} at {full_target}")
|
|
303
|
+
else:
|
|
304
|
+
imported_module = importlib.import_module(name=target)
|
|
140
305
|
main_module = __import__("__main__")
|
|
141
306
|
if absorb:
|
|
142
307
|
for name in dir(imported_module):
|
|
@@ -145,19 +310,25 @@ def py_import(
|
|
|
145
310
|
|
|
146
311
|
elif items:
|
|
147
312
|
for name, alias in items.items():
|
|
313
|
+
if isinstance(alias, bool):
|
|
314
|
+
alias = name
|
|
148
315
|
try:
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
alias if isinstance(alias, str) else name,
|
|
152
|
-
getattr(imported_module, name),
|
|
153
|
-
)
|
|
154
|
-
except AttributeError as e:
|
|
155
|
-
if hasattr(imported_module, "__path__"):
|
|
316
|
+
item = getattr(imported_module, name)
|
|
317
|
+
if item not in loaded_items:
|
|
156
318
|
setattr(
|
|
157
|
-
main_module,
|
|
158
|
-
alias if isinstance(alias, str) else name,
|
|
159
|
-
importlib.import_module(f"{target}.{name}"),
|
|
319
|
+
main_module, alias if isinstance(alias, str) else name, item
|
|
160
320
|
)
|
|
321
|
+
loaded_items.append(item)
|
|
322
|
+
except AttributeError as e:
|
|
323
|
+
if hasattr(imported_module, "__path__"):
|
|
324
|
+
item = importlib.import_module(f"{target}.{name}")
|
|
325
|
+
if item not in loaded_items:
|
|
326
|
+
setattr(
|
|
327
|
+
main_module,
|
|
328
|
+
alias if isinstance(alias, str) else name,
|
|
329
|
+
item,
|
|
330
|
+
)
|
|
331
|
+
loaded_items.append(item)
|
|
161
332
|
else:
|
|
162
333
|
raise e
|
|
163
334
|
|
|
@@ -167,7 +338,7 @@ def py_import(
|
|
|
167
338
|
mdl_alias if isinstance(mdl_alias, str) else target,
|
|
168
339
|
imported_module,
|
|
169
340
|
)
|
|
170
|
-
return imported_module
|
|
341
|
+
return (imported_module, *loaded_items)
|
|
342
|
+
|
|
171
343
|
except ImportError as e:
|
|
172
|
-
print(f"Failed to import module {target}")
|
|
173
344
|
raise e
|