Skip to content

Commit 13354a8

Browse files
committed
added support for positional-only func arguments
1 parent 12b5047 commit 13354a8

File tree

2 files changed

+41
-7
lines changed

2 files changed

+41
-7
lines changed

custom_components/pyscript/eval.py

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,8 @@ def __init__(self, func_def, code_list, code_str, global_ctx):
319319
self.local_names = None
320320
self.local_sym_table = {}
321321
self.doc_string = ast.get_docstring(func_def)
322-
self.num_posn_arg = len(self.func_def.args.args) - len(self.defaults)
322+
self.num_posonly_arg = len(self.func_def.args.posonlyargs)
323+
self.num_posn_arg = self.num_posonly_arg + len(self.func_def.args.args) - len(self.defaults)
323324
self.code_list = code_list
324325
self.code_str = code_str
325326
self.exception = None
@@ -342,7 +343,7 @@ async def eval_defaults(self, ast_ctx):
342343
self.defaults = []
343344
for val in self.func_def.args.defaults:
344345
self.defaults.append(await ast_ctx.aeval(val))
345-
self.num_posn_arg = len(self.func_def.args.args) - len(self.defaults)
346+
self.num_posn_arg = self.num_posonly_arg + len(self.func_def.args.args) - len(self.defaults)
346347
self.kw_defaults = []
347348
for val in self.func_def.args.kw_defaults:
348349
self.kw_defaults.append({"ok": bool(val), "val": None if not val else await ast_ctx.aeval(val)})
@@ -669,7 +670,7 @@ def get_doc_string(self):
669670
def get_positional_args(self):
670671
"""Return the function positional arguments."""
671672
args = []
672-
for arg in self.func_def.args.args:
673+
for arg in self.func_def.args.posonlyargs + self.func_def.args.args:
673674
args.append(arg.arg)
674675
return args
675676

@@ -689,14 +690,17 @@ async def call(self, ast_ctx, *args, **kwargs):
689690
if args is None:
690691
args = []
691692
kwargs = kwargs.copy() if kwargs else {}
692-
for i, func_def_arg in enumerate(self.func_def.args.args):
693+
bad_kwargs = []
694+
for i, func_def_arg in enumerate(self.func_def.args.posonlyargs + self.func_def.args.args):
693695
var_name = func_def_arg.arg
694696
val = None
695697
if i < len(args):
696698
val = args[i]
697699
if var_name in kwargs:
698700
raise TypeError(f"{self.name}() got multiple values for argument '{var_name}'")
699701
elif var_name in kwargs:
702+
if i < self.num_posonly_arg:
703+
bad_kwargs.append(var_name)
700704
val = kwargs[var_name]
701705
del kwargs[var_name]
702706
elif self.num_posn_arg <= i < len(self.defaults) + self.num_posn_arg:
@@ -706,6 +710,11 @@ async def call(self, ast_ctx, *args, **kwargs):
706710
f"{self.name}() missing {self.num_posn_arg - i} required positional arguments"
707711
)
708712
sym_table[var_name] = val
713+
if len(bad_kwargs) > 0:
714+
raise TypeError(
715+
f"{self.name}() got some positional-only arguments passed as keyword arguments: '{', '.join(bad_kwargs)}'"
716+
)
717+
709718
for i, kwonlyarg in enumerate(self.func_def.args.kwonlyargs):
710719
var_name = kwonlyarg.arg
711720
if var_name in kwargs:
@@ -724,12 +733,13 @@ async def call(self, ast_ctx, *args, **kwargs):
724733
# since they could have non-trigger decorators too
725734
unexpected = ", ".join(sorted(set(kwargs.keys()) - TRIGGER_KWARGS))
726735
raise TypeError(f"{self.name}() called with unexpected keyword arguments: {unexpected}")
736+
num_posn = self.num_posonly_arg + len(self.func_def.args.args)
727737
if self.func_def.args.vararg:
728-
if len(args) > len(self.func_def.args.args):
729-
sym_table[self.func_def.args.vararg.arg] = tuple(args[len(self.func_def.args.args) :])
738+
if len(args) > num_posn:
739+
sym_table[self.func_def.args.vararg.arg] = tuple(args[num_posn:])
730740
else:
731741
sym_table[self.func_def.args.vararg.arg] = ()
732-
elif len(args) > len(self.func_def.args.args):
742+
elif len(args) > num_posn:
733743
raise TypeError(f"{self.name}() called with too many positional arguments")
734744
for name, value in self.local_sym_table.items():
735745
if name in sym_table:

tests/test_unit_eval.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,14 @@
221221
["x = 5; exec('x = 2 * x'); x", 10],
222222
[
223223
"""
224+
def f(x, /, y = 5):
225+
return x + y
226+
[f(2), f(2, 2), f(2, y=3)]
227+
""",
228+
[7, 4, 5],
229+
],
230+
[
231+
"""
224232
def func():
225233
x = 5
226234
exec('x = 2 * x')
@@ -1510,6 +1518,22 @@ def func(b=1):
15101518
""",
15111519
"Exception in test line 5 column 23: func() called with unexpected keyword arguments: a",
15121520
],
1521+
[
1522+
"""
1523+
def f(x, z, /, y = 5):
1524+
return x + y
1525+
f(x=4, z=2)
1526+
""",
1527+
"Exception in test line 4 column 9: f() got some positional-only arguments passed as keyword arguments: 'x, z'",
1528+
],
1529+
[
1530+
"""
1531+
def f(x, /, y = 5):
1532+
return x + y
1533+
f(x=4)
1534+
""",
1535+
"Exception in test line 4 column 4: f() got some positional-only arguments passed as keyword arguments: 'x'",
1536+
],
15131537
[
15141538
"from .xyz import abc",
15151539
"Exception in test line 1 column 0: attempted relative import with no known parent package",

0 commit comments

Comments
 (0)