jaclang 0.8.6__py3-none-any.whl → 0.8.8__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.md +3 -3
- jaclang/cli/cli.py +37 -37
- jaclang/cli/cmdreg.py +45 -140
- jaclang/compiler/constant.py +0 -1
- jaclang/compiler/jac.lark +3 -6
- jaclang/compiler/larkparse/jac_parser.py +2 -2
- jaclang/compiler/parser.py +213 -34
- jaclang/compiler/passes/main/__init__.py +2 -4
- jaclang/compiler/passes/main/def_use_pass.py +0 -4
- jaclang/compiler/passes/main/predynamo_pass.py +221 -0
- jaclang/compiler/passes/main/pyast_gen_pass.py +83 -55
- jaclang/compiler/passes/main/pyast_load_pass.py +66 -40
- jaclang/compiler/passes/main/sym_tab_build_pass.py +1 -1
- jaclang/compiler/passes/main/tests/fixtures/checker/import_sym.jac +2 -0
- jaclang/compiler/passes/main/tests/fixtures/checker/import_sym_test.jac +6 -0
- jaclang/compiler/passes/main/tests/fixtures/checker/imported_sym.jac +5 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_arg_param_match.jac +37 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_arity.jac +18 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_binary_op.jac +21 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_call_expr_class.jac +12 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_cat_is_animal.jac +18 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_cyclic_symbol.jac +4 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_expr_call.jac +9 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_float.jac +7 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_import_missing_module.jac +13 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_magic_call.jac +17 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_mod_path.jac +8 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_param_types.jac +11 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_self_type.jac +9 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_sym_inherit.jac +42 -0
- jaclang/compiler/passes/main/tests/fixtures/predynamo_fix3.jac +43 -0
- jaclang/compiler/passes/main/tests/fixtures/predynamo_where_assign.jac +13 -0
- jaclang/compiler/passes/main/tests/fixtures/predynamo_where_return.jac +11 -0
- jaclang/compiler/passes/main/tests/test_checker_pass.py +265 -0
- jaclang/compiler/passes/main/tests/test_predynamo_pass.py +57 -0
- jaclang/compiler/passes/main/type_checker_pass.py +36 -61
- jaclang/compiler/passes/tool/doc_ir_gen_pass.py +204 -44
- jaclang/compiler/passes/tool/jac_formatter_pass.py +119 -69
- jaclang/compiler/passes/tool/tests/fixtures/corelib_fmt.jac +3 -3
- jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/triple_quoted_string.jac +4 -5
- jaclang/compiler/passes/tool/tests/fixtures/tagbreak.jac +171 -11
- jaclang/compiler/passes/transform.py +12 -8
- jaclang/compiler/program.py +14 -6
- jaclang/compiler/tests/fixtures/jac_import_py_files.py +4 -0
- jaclang/compiler/tests/fixtures/jac_module.jac +3 -0
- jaclang/compiler/tests/fixtures/multiple_syntax_errors.jac +10 -0
- jaclang/compiler/tests/fixtures/python_module.py +1 -0
- jaclang/compiler/tests/test_importer.py +39 -0
- jaclang/compiler/tests/test_parser.py +49 -0
- jaclang/compiler/type_system/operations.py +104 -0
- jaclang/compiler/type_system/type_evaluator.py +470 -47
- jaclang/compiler/type_system/type_utils.py +246 -0
- jaclang/compiler/type_system/types.py +58 -2
- jaclang/compiler/unitree.py +79 -94
- jaclang/langserve/engine.jac +253 -230
- jaclang/langserve/server.jac +46 -15
- 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/completion_test_err.jac +10 -0
- jaclang/langserve/tests/server_test/circle_template.jac +80 -0
- jaclang/langserve/tests/server_test/glob_template.jac +4 -0
- jaclang/langserve/tests/server_test/test_lang_serve.py +154 -312
- jaclang/langserve/tests/server_test/utils.py +153 -116
- jaclang/langserve/tests/test_dev_server.py +1 -1
- jaclang/langserve/tests/test_server.py +30 -86
- jaclang/langserve/utils.jac +56 -63
- jaclang/runtimelib/machine.py +7 -0
- jaclang/runtimelib/meta_importer.py +27 -1
- jaclang/runtimelib/tests/fixtures/custom_access_validation.jac +1 -1
- jaclang/runtimelib/tests/fixtures/savable_object.jac +2 -2
- jaclang/settings.py +18 -14
- jaclang/tests/fixtures/abc_check.jac +3 -3
- jaclang/tests/fixtures/arch_rel_import_creation.jac +12 -12
- jaclang/tests/fixtures/chandra_bugs2.jac +3 -3
- jaclang/tests/fixtures/create_dynamic_archetype.jac +13 -13
- jaclang/tests/fixtures/jac_run_py_bugs.py +18 -0
- jaclang/tests/fixtures/jac_run_py_import.py +13 -0
- jaclang/tests/fixtures/lambda_arg_annotation.jac +15 -0
- jaclang/tests/fixtures/lambda_self.jac +18 -0
- jaclang/tests/fixtures/maxfail_run_test.jac +4 -4
- jaclang/tests/fixtures/params/param_syntax_err.jac +9 -0
- jaclang/tests/fixtures/params/test_complex_params.jac +42 -0
- jaclang/tests/fixtures/params/test_failing_kwonly.jac +207 -0
- jaclang/tests/fixtures/params/test_failing_posonly.jac +116 -0
- jaclang/tests/fixtures/params/test_failing_varargs.jac +300 -0
- jaclang/tests/fixtures/params/test_kwonly_params.jac +29 -0
- jaclang/tests/fixtures/py2jac_params.py +8 -0
- jaclang/tests/fixtures/run_test.jac +4 -4
- jaclang/tests/test_cli.py +103 -18
- jaclang/tests/test_language.py +74 -16
- jaclang/utils/helpers.py +47 -2
- jaclang/utils/module_resolver.py +11 -1
- jaclang/utils/test.py +8 -0
- jaclang/utils/treeprinter.py +0 -18
- {jaclang-0.8.6.dist-info → jaclang-0.8.8.dist-info}/METADATA +3 -3
- {jaclang-0.8.6.dist-info → jaclang-0.8.8.dist-info}/RECORD +99 -62
- {jaclang-0.8.6.dist-info → jaclang-0.8.8.dist-info}/WHEEL +1 -1
- jaclang/compiler/passes/main/inheritance_pass.py +0 -131
- jaclang/langserve/dev_engine.jac +0 -645
- jaclang/langserve/dev_server.jac +0 -201
- jaclang/langserve/tests/server_test/code_test.py +0 -0
- {jaclang-0.8.6.dist-info → jaclang-0.8.8.dist-info}/entry_points.txt +0 -0
jaclang/runtimelib/machine.py
CHANGED
|
@@ -1132,6 +1132,13 @@ class JacBasics:
|
|
|
1132
1132
|
return JacMachineInterface.get_edges(origin, path.destinations[-1])
|
|
1133
1133
|
return origin
|
|
1134
1134
|
|
|
1135
|
+
@staticmethod
|
|
1136
|
+
async def arefs(
|
|
1137
|
+
path: DataSpatialPath | NodeArchetype | list[NodeArchetype],
|
|
1138
|
+
) -> None:
|
|
1139
|
+
"""Jac's apply_dir stmt feature."""
|
|
1140
|
+
pass
|
|
1141
|
+
|
|
1135
1142
|
@staticmethod
|
|
1136
1143
|
def filter(
|
|
1137
1144
|
items: list[Archetype],
|
|
@@ -9,7 +9,8 @@ from typing import Optional, Sequence
|
|
|
9
9
|
|
|
10
10
|
from jaclang.runtimelib.machine import JacMachine as Jac
|
|
11
11
|
from jaclang.runtimelib.machine import JacMachineInterface
|
|
12
|
-
from jaclang.
|
|
12
|
+
from jaclang.settings import settings
|
|
13
|
+
from jaclang.utils.module_resolver import get_jac_search_paths, get_py_search_paths
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
class JacMetaImporter(importlib.abc.MetaPathFinder, importlib.abc.Loader):
|
|
@@ -48,6 +49,30 @@ class JacMetaImporter(importlib.abc.MetaPathFinder, importlib.abc.Loader):
|
|
|
48
49
|
return importlib.util.spec_from_file_location(
|
|
49
50
|
fullname, candidate_path + ".jac", loader=self
|
|
50
51
|
)
|
|
52
|
+
|
|
53
|
+
# TODO: We can remove it once python modules are fully supported in jac
|
|
54
|
+
if path is None and settings.pyfile_raise:
|
|
55
|
+
if settings.pyfile_raise_full:
|
|
56
|
+
paths_to_search = get_jac_search_paths()
|
|
57
|
+
else:
|
|
58
|
+
paths_to_search = get_py_search_paths()
|
|
59
|
+
for search_path in paths_to_search:
|
|
60
|
+
candidate_path = os.path.join(search_path, *module_path_parts)
|
|
61
|
+
# Check for directory package
|
|
62
|
+
if os.path.isdir(candidate_path):
|
|
63
|
+
init_file = os.path.join(candidate_path, "__init__.py")
|
|
64
|
+
if os.path.isfile(init_file):
|
|
65
|
+
return importlib.util.spec_from_file_location(
|
|
66
|
+
fullname,
|
|
67
|
+
init_file,
|
|
68
|
+
loader=self,
|
|
69
|
+
submodule_search_locations=[candidate_path],
|
|
70
|
+
)
|
|
71
|
+
# Check for .py file
|
|
72
|
+
if os.path.isfile(candidate_path + ".py"):
|
|
73
|
+
return importlib.util.spec_from_file_location(
|
|
74
|
+
fullname, candidate_path + ".py", loader=self
|
|
75
|
+
)
|
|
51
76
|
return None
|
|
52
77
|
|
|
53
78
|
def create_module(
|
|
@@ -78,6 +103,7 @@ class JacMetaImporter(importlib.abc.MetaPathFinder, importlib.abc.Loader):
|
|
|
78
103
|
target=target,
|
|
79
104
|
base_path=base_path,
|
|
80
105
|
override_name=module.__name__,
|
|
106
|
+
lng="py" if file_path.endswith(".py") else "jac",
|
|
81
107
|
)
|
|
82
108
|
if ret:
|
|
83
109
|
loaded_module = ret[0]
|
|
@@ -44,7 +44,7 @@ walker create_custom_object {
|
|
|
44
44
|
|
|
45
45
|
can exit1 with `root exit {
|
|
46
46
|
# get directly from shelf
|
|
47
|
-
o =
|
|
47
|
+
o = _jl.get_context().mem.__shelf__.get(str(self.obj.__jac__.id)).archetype;
|
|
48
48
|
print(jid(o));
|
|
49
49
|
print(o);
|
|
50
50
|
}
|
|
@@ -87,6 +87,6 @@ walker delete_custom_object {
|
|
|
87
87
|
has object_id: str;
|
|
88
88
|
|
|
89
89
|
can enter1 with `root entry {
|
|
90
|
-
|
|
90
|
+
_jl.destroy([&(self.object_id)]);
|
|
91
91
|
}
|
|
92
92
|
}
|
jaclang/settings.py
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import configparser
|
|
4
4
|
import os
|
|
5
|
+
from argparse import Namespace
|
|
5
6
|
from dataclasses import dataclass, fields
|
|
6
7
|
|
|
7
8
|
|
|
@@ -19,11 +20,16 @@ class Settings:
|
|
|
19
20
|
# Compiler configuration
|
|
20
21
|
disable_mtllm: bool = False
|
|
21
22
|
ignore_test_annex: bool = False
|
|
22
|
-
pyout_jaclib_alias: str = "
|
|
23
|
+
pyout_jaclib_alias: str = "_jl"
|
|
24
|
+
pyfile_raise: bool = False
|
|
25
|
+
pyfile_raise_full: bool = False
|
|
23
26
|
|
|
24
27
|
# Formatter configuration
|
|
25
28
|
max_line_length: int = 88
|
|
26
29
|
|
|
30
|
+
# pytorch configuration
|
|
31
|
+
predynamo_pass: bool = False
|
|
32
|
+
|
|
27
33
|
# LSP configuration
|
|
28
34
|
lsp_debug: bool = False
|
|
29
35
|
|
|
@@ -42,6 +48,8 @@ class Settings:
|
|
|
42
48
|
"""Load settings from all available sources."""
|
|
43
49
|
self.load_config_file()
|
|
44
50
|
self.load_env_vars()
|
|
51
|
+
# CLI arguments are applied by the CLI after parsing, via
|
|
52
|
+
# `settings.load_command_line_arguments(args)` in start_cli.
|
|
45
53
|
|
|
46
54
|
def load_config_file(self) -> None:
|
|
47
55
|
"""Load settings from a configuration file."""
|
|
@@ -64,19 +72,15 @@ class Settings:
|
|
|
64
72
|
if env_value is not None:
|
|
65
73
|
setattr(self, key, self.convert_type(env_value))
|
|
66
74
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
# )
|
|
77
|
-
# parser.add_argument("--port", type=int, default=self.config["port"])
|
|
78
|
-
# parser.add_argument("--host", default=self.config["host"])
|
|
79
|
-
# args = parser.parse_args()
|
|
75
|
+
def load_command_line_arguments(self, args: Namespace) -> None:
|
|
76
|
+
"""Override settings from command-line arguments if provided."""
|
|
77
|
+
args_dict = vars(args) if not isinstance(args, dict) else args
|
|
78
|
+
for key in [f.name for f in fields(self)]:
|
|
79
|
+
if key in args_dict and args_dict[key] is not None:
|
|
80
|
+
val = args_dict[key]
|
|
81
|
+
if isinstance(val, str):
|
|
82
|
+
val = self.convert_type(val)
|
|
83
|
+
setattr(self, key, val)
|
|
80
84
|
|
|
81
85
|
def str_to_bool(self, value: str) -> bool:
|
|
82
86
|
"""Convert string to boolean."""
|
|
@@ -63,15 +63,15 @@ with entry:__main__ {
|
|
|
63
63
|
glob expected_area = 78.53981633974483;
|
|
64
64
|
|
|
65
65
|
test calc_area {
|
|
66
|
-
|
|
66
|
+
assert almostEqual(calculate_area(RAD), expected_area);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
test circle_area {
|
|
70
70
|
c = Circle(RAD);
|
|
71
|
-
|
|
71
|
+
assert almostEqual(c.area(), expected_area);
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
test circle_type {
|
|
75
75
|
c = Circle(RAD);
|
|
76
|
-
|
|
76
|
+
assert c.shape_type == ShapeType.CIRCLE;
|
|
77
77
|
}
|
|
@@ -3,20 +3,20 @@ import from jaclang.runtimelib.machine { JacMachine }
|
|
|
3
3
|
|
|
4
4
|
glob dynamic_module_source =
|
|
5
5
|
"""
|
|
6
|
-
|
|
6
|
+
import from arch_create_util {UtilityNode}
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
walker DynamicWalker {
|
|
9
|
+
can start with entry {
|
|
10
|
+
print("DynamicWalker Started");
|
|
11
|
+
here ++> UtilityNode(data=42);
|
|
12
|
+
visit [-->](`?UtilityNode);
|
|
13
|
+
}
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
def UtilityNode {
|
|
16
|
+
here.display_data();
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
""";
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
with entry {
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import re;
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
glob a
|
|
4
|
+
glob a: int = 5;
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
with entry {
|
|
8
8
|
arguments = { x : None for x in re.findall(r'\{([A-Za-z0-9_]+)\}', "Apple {apple} pineapple {pineapple}") };
|
|
9
|
-
a
|
|
9
|
+
a: int = 5;
|
|
10
10
|
if False {
|
|
11
|
-
with open(f"Apple{apple}.txt") as f
|
|
11
|
+
with open(f"Apple{apple}.txt") as f {
|
|
12
12
|
# Fix syntax highlighting
|
|
13
13
|
print(
|
|
14
14
|
f.read()
|
|
@@ -2,23 +2,23 @@ import from jaclang.runtimelib.machine { JacMachine }
|
|
|
2
2
|
# Dynamically create a node archetype
|
|
3
3
|
glob source_code =
|
|
4
4
|
"""
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
node dynamic_node {
|
|
6
|
+
has value:int;
|
|
7
|
+
can print_value with entry {
|
|
8
|
+
print("Dynamic Node Value:", f'{self.value}');
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
""";
|
|
12
12
|
|
|
13
13
|
# Create a new walker archetype dynamically
|
|
14
14
|
glob walker_code =
|
|
15
15
|
"""
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
walker dynamic_walker {
|
|
17
|
+
can visit_nodes with entry {
|
|
18
|
+
visit [-->];
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
""";
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
with entry {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from jaclang.tests.fixtures.jac_run_py_import import MyModule
|
|
2
|
+
|
|
3
|
+
class SimpleClass:
|
|
4
|
+
def __init__(self, name: str, age: int) -> None:
|
|
5
|
+
self.name = name
|
|
6
|
+
self.age = age
|
|
7
|
+
|
|
8
|
+
def greet(self):
|
|
9
|
+
return f"Hello, my name is {self.name} and I am {self.age} years old."
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# Create an object of the class
|
|
13
|
+
person = SimpleClass("Alice", 30)
|
|
14
|
+
|
|
15
|
+
# Run the greet method
|
|
16
|
+
print(person.greet())
|
|
17
|
+
|
|
18
|
+
MyModule.init()
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
walker Tourist {
|
|
4
|
+
can travel with City entry {
|
|
5
|
+
|
|
6
|
+
def foo(a:int){}
|
|
7
|
+
x = lambda a: int, b: int : b + a;
|
|
8
|
+
y = lambda : 567;
|
|
9
|
+
sorted_users = sorted(
|
|
10
|
+
users,
|
|
11
|
+
key=lambda x: dict: x["email"], reverse=True
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
}
|
|
15
|
+
def visit_city(c:City){
|
|
16
|
+
print("Visiting", c.name);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
glob x = 5, y = 2;
|
|
2
2
|
|
|
3
3
|
test a {
|
|
4
|
-
|
|
4
|
+
assert almostEqual(5, x);
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
test b {
|
|
8
|
-
|
|
8
|
+
assert "l" in "llm";
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
test c {
|
|
12
|
-
|
|
12
|
+
assert x - y == 3;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
test d {
|
|
16
|
-
|
|
16
|
+
assert 1 == 2;
|
|
17
17
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Test complex parameter combinations
|
|
2
|
+
|
|
3
|
+
def ultimate_signature(
|
|
4
|
+
pos_only: int,
|
|
5
|
+
pos_def: str = "def",
|
|
6
|
+
/,
|
|
7
|
+
reg_def: bool = True,
|
|
8
|
+
*args: int,
|
|
9
|
+
kw_req: str,
|
|
10
|
+
kw_opt: int = 100,
|
|
11
|
+
**kwargs: any
|
|
12
|
+
) -> str {
|
|
13
|
+
return f"{pos_only}|{pos_def}|{reg_def}|{len(args)}|{kw_req}|{kw_opt}|{len(kwargs)}";
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
def separators_only(/, *, x: int) -> int {
|
|
17
|
+
return x;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
def edge_case_mix(a: int, /, b: str, *args: float, c: bool, **kwargs: str) -> str {
|
|
21
|
+
return f"{a}-{b}-{len(args)}-{c}-{len(kwargs)}";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
def recursive_test(data: int, /, depth: int = 0, *, max_depth: int = 2) -> int {
|
|
25
|
+
if depth >= max_depth {
|
|
26
|
+
return data;
|
|
27
|
+
}
|
|
28
|
+
return recursive_test(data + 1, depth + 1, max_depth=max_depth);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
def validation_test(x: int, y: str = "test", /, z: float, *args: int, w: bool, **kwargs: str) -> str {
|
|
32
|
+
return f"x:{x},y:{y},z:{z},args:{len(args)},w:{w},kwargs:{len(kwargs)}";
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
with entry {
|
|
36
|
+
print("ULTIMATE_MIN:", ultimate_signature(1, reg_def=2.5, kw_req="test"));
|
|
37
|
+
print("ULTIMATE_FULL:", ultimate_signature(1, "custom", 3.14, False, 10, 20, kw_req="req", kw_opt=200, extra="data"));
|
|
38
|
+
print("SEPARATORS:", separators_only(x=42));
|
|
39
|
+
print("EDGE_MIX:", edge_case_mix(1, "test", 1.1, 2.2, c=True, name="edge"));
|
|
40
|
+
print("RECURSIVE:", recursive_test(5), recursive_test(10, max_depth=1));
|
|
41
|
+
print("VALIDATION:", validation_test(1, 2.5, 10, 20, w=True, debug="on"));
|
|
42
|
+
}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# Test failing keyword-only parameter cases
|
|
2
|
+
|
|
3
|
+
def strict_kwonly(*, x: int, y: str) -> str {
|
|
4
|
+
return f"{x}: {y}";
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
def kwonly_with_defaults(*, req: int, opt: str = "default") -> str {
|
|
8
|
+
return f"{req}-{opt}";
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
def mixed_kwonly(a: int, *, kw1: str, kw2: bool = False) -> dict {
|
|
12
|
+
return {"a": a, "kw1": kw1, "kw2": kw2};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
def complex_kwonly(pos: int, /, reg: str, *args: float, kw_req: bool, kw_opt: int = 10, **kwargs: any) -> dict {
|
|
16
|
+
return {"pos": pos, "reg": reg, "args": list(args), "kw_req": kw_req, "kw_opt": kw_opt, "kwargs": kwargs};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
def only_star_separator(*, x: int) -> int {
|
|
20
|
+
return x * 2;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
def nested_kwonly_issues(*, outer: dict, inner: list = []) -> int {
|
|
24
|
+
return len(outer) + len(inner);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
with entry {
|
|
28
|
+
print("=== TESTING KEYWORD-ONLY FAILURES ===");
|
|
29
|
+
|
|
30
|
+
# Test 1: Calling kw-only with positional arguments
|
|
31
|
+
try {
|
|
32
|
+
result = strict_kwonly(42, "test");
|
|
33
|
+
print("❌ FAIL: Should reject positional for kw-only");
|
|
34
|
+
} except Exception as e {
|
|
35
|
+
print("✅ PASS: Caught kw-only positional error:", type(e).__name__);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
# Test 2: Missing required keyword-only argument
|
|
39
|
+
try {
|
|
40
|
+
result = strict_kwonly(x=42);
|
|
41
|
+
print("❌ FAIL: Should reject missing required kw-only");
|
|
42
|
+
} except Exception as e {
|
|
43
|
+
print("✅ PASS: Caught missing kw-only error:", type(e).__name__);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
# Test 3: Missing required in defaults mix
|
|
47
|
+
try {
|
|
48
|
+
result = kwonly_with_defaults(opt="custom");
|
|
49
|
+
print("❌ FAIL: Should reject missing required kw-only");
|
|
50
|
+
} except Exception as e {
|
|
51
|
+
print("✅ PASS: Caught missing required in defaults:", type(e).__name__);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
# Test 4: Extra unknown keyword arguments (without **kwargs)
|
|
55
|
+
try {
|
|
56
|
+
result = strict_kwonly(x=42, y="test", extra="unknown");
|
|
57
|
+
print("❌ FAIL: Should reject unknown keyword");
|
|
58
|
+
} except Exception as e {
|
|
59
|
+
print("✅ PASS: Caught unknown keyword error:", type(e).__name__);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
# Test 5: Mixed signature - wrong positional count
|
|
63
|
+
try {
|
|
64
|
+
result = mixed_kwonly(1, 2, kw1="test");
|
|
65
|
+
print("❌ FAIL: Should reject too many positional");
|
|
66
|
+
} except Exception as e {
|
|
67
|
+
print("✅ PASS: Caught too many positional error:", type(e).__name__);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
# Test 6: Mixed signature - missing kw-only required
|
|
71
|
+
try {
|
|
72
|
+
result = mixed_kwonly(42);
|
|
73
|
+
print("❌ FAIL: Should reject missing kw-only in mixed");
|
|
74
|
+
} except Exception as e {
|
|
75
|
+
print("✅ PASS: Caught missing kw-only in mixed:", type(e).__name__);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
# Test 7: Complex signature - kw-only as positional
|
|
79
|
+
try {
|
|
80
|
+
result = complex_kwonly(1, "reg", 1.5, True);
|
|
81
|
+
print("❌ FAIL: Should reject kw-only as positional");
|
|
82
|
+
} except Exception as e {
|
|
83
|
+
print("✅ PASS: Caught kw-only as positional error:", type(e).__name__);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
# Test 8: Complex signature - missing required kw-only
|
|
87
|
+
try {
|
|
88
|
+
result = complex_kwonly(1, "reg", 1.5, 2.5);
|
|
89
|
+
print("❌ FAIL: Should reject missing kw-only in complex");
|
|
90
|
+
} except Exception as e {
|
|
91
|
+
print("✅ PASS: Caught missing kw-only in complex:", type(e).__name__);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
# Test 9: Only star separator - positional call
|
|
95
|
+
try {
|
|
96
|
+
result = only_star_separator(42);
|
|
97
|
+
print("❌ FAIL: Should reject positional for star-only");
|
|
98
|
+
} except Exception as e {
|
|
99
|
+
print("✅ PASS: Caught star-only positional error:", type(e).__name__);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
# Test 11: Mutable default issues
|
|
104
|
+
try {
|
|
105
|
+
result1 = nested_kwonly_issues(outer={"a": 1});
|
|
106
|
+
result2 = nested_kwonly_issues(outer={"b": 2});
|
|
107
|
+
print("✅ INFO: Mutable defaults - result1:", result1, "result2:", result2);
|
|
108
|
+
} except Exception as e {
|
|
109
|
+
print("❌ FAIL: Mutable defaults caused error:", type(e).__name__);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
# Test 12: Valid calls that should work
|
|
113
|
+
try {
|
|
114
|
+
result = strict_kwonly(x=42, y="valid");
|
|
115
|
+
print("✅ PASS: Valid kw-only call:", result);
|
|
116
|
+
} except Exception as e {
|
|
117
|
+
print("❌ FAIL: Valid kw-only call rejected:", type(e).__name__);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
try {
|
|
121
|
+
result = kwonly_with_defaults(req=100);
|
|
122
|
+
print("✅ PASS: Valid kw-only with defaults:", result);
|
|
123
|
+
} except Exception as e {
|
|
124
|
+
print("❌ FAIL: Valid defaults call rejected:", type(e).__name__);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
try {
|
|
128
|
+
result = mixed_kwonly(5, kw1="test");
|
|
129
|
+
print("✅ PASS: Valid mixed kw-only:", result);
|
|
130
|
+
} except Exception as e {
|
|
131
|
+
print("❌ FAIL: Valid mixed call rejected:", type(e).__name__);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
def additional_kwonly_edge_cases() -> None {
|
|
136
|
+
print("\n=== ADDITIONAL KEYWORD-ONLY EDGE CASES ===");
|
|
137
|
+
|
|
138
|
+
# Test 14: Keyword argument name conflicts with Python keywords
|
|
139
|
+
def python_keyword_conflicts(*, class_name: str, import_path: str = "default") -> str {
|
|
140
|
+
return f"{class_name}: {import_path}";
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
try {
|
|
144
|
+
result = python_keyword_conflicts(class_name="TestClass");
|
|
145
|
+
print("✅ PASS: Python keyword as param name:", result);
|
|
146
|
+
} except Exception as e {
|
|
147
|
+
print("❌ FAIL: Python keyword param failed:", type(e).__name__);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
# Test 15: Very long keyword-only parameter lists
|
|
151
|
+
def many_kwonly_params(
|
|
152
|
+
*,
|
|
153
|
+
p1: int, p2: int, p3: int, p4: int, p5: int,
|
|
154
|
+
p6: str = "d6", p7: str = "d7", p8: str = "d8"
|
|
155
|
+
) -> dict {
|
|
156
|
+
return {"sum": p1+p2+p3+p4+p5, "strings": [p6, p7, p8]};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
result = many_kwonly_params(p1=1, p2=2, p3=3, p4=4, p5=5);
|
|
161
|
+
print("✅ PASS: Many kw-only params:", result);
|
|
162
|
+
} except Exception as e {
|
|
163
|
+
print("❌ FAIL: Many kw-only params failed:", type(e).__name__);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
# Test 16: Nested function calls as keyword arguments
|
|
167
|
+
def helper_func() -> str {
|
|
168
|
+
return "helper_result";
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
def kwonly_with_function_calls(*, data: str, processed: bool = True) -> str {
|
|
172
|
+
return f"data={data}, processed={processed}";
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
try {
|
|
176
|
+
result = kwonly_with_function_calls(data=helper_func(), processed=False);
|
|
177
|
+
print("✅ PASS: Function calls as kw args:", result);
|
|
178
|
+
} except Exception as e {
|
|
179
|
+
print("❌ FAIL: Function calls as kw args failed:", type(e).__name__);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
# Test 17: Empty string and special values
|
|
183
|
+
try {
|
|
184
|
+
result = strict_kwonly(x=0, y="");
|
|
185
|
+
print("✅ PASS: Edge values (0, empty string):", result);
|
|
186
|
+
} except Exception as e {
|
|
187
|
+
print("❌ FAIL: Edge values failed:", type(e).__name__);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
# Test 18: Really test duplicate keyword detection properly
|
|
191
|
+
# Note: This might not be detectable at runtime in some languages
|
|
192
|
+
def test_duplicate_detection() -> None {
|
|
193
|
+
try {
|
|
194
|
+
# This should be a syntax error, but let's see what happens
|
|
195
|
+
call_string = "strict_kwonly(x=42, y='test', x=99)";
|
|
196
|
+
print("INFO: Would test duplicate keywords, but this is syntax-level");
|
|
197
|
+
} except Exception as e {
|
|
198
|
+
print("✅ PASS: Duplicate detection works:", type(e).__name__);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
test_duplicate_detection();
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
with entry {
|
|
206
|
+
additional_kwonly_edge_cases();
|
|
207
|
+
}
|