diff --git a/tests/snippets/function_args.py b/tests/snippets/function_args.py index d1754a62f0..eb4f94b8a3 100644 --- a/tests/snippets/function_args.py +++ b/tests/snippets/function_args.py @@ -41,7 +41,6 @@ def va2(*args, **kwargs): assert args == (5, 4) assert len(kwargs) == 0 - va2(5, 4) x = (5, 4) va2(*x) @@ -114,3 +113,19 @@ def f(a): y = {'a': 2} with assert_raises(TypeError): f(**x, **y) + + +def f(a, b, /, c, d, *, e, f): + return a + b + c + d + e + f + +assert f(1,2,3,4,e=5,f=6) == 21 +assert f(1,2,3,d=4,e=5,f=6) == 21 +assert f(1,2,c=3,d=4,e=5,f=6) == 21 +with assert_raises(TypeError): + f(1,b=2,c=3,d=4,e=5,f=6) +with assert_raises(TypeError): + f(a=1,b=2,c=3,d=4,e=5,f=6) +with assert_raises(TypeError): + f(1,2,3,4,5,f=6) +with assert_raises(TypeError): + f(1,2,3,4,5,6) diff --git a/vm/src/obj/objfunction.rs b/vm/src/obj/objfunction.rs index 703c8bfae8..c363dd5281 100644 --- a/vm/src/obj/objfunction.rs +++ b/vm/src/obj/objfunction.rs @@ -15,7 +15,8 @@ use crate::pyobject::{ }; use crate::scope::Scope; use crate::slots::{SlotCall, SlotDescriptor}; -use crate::vm::VirtualMachine; +use crate::VirtualMachine; +use itertools::Itertools; pub type PyFunctionRef = PyRef; @@ -72,6 +73,7 @@ impl PyFunction { ) -> PyResult<()> { let nargs = func_args.args.len(); let nexpected_args = code_object.arg_names.len(); + let posonly_args = &code_object.arg_names[..code_object.posonlyarg_count]; // This parses the arguments from args and kwargs into // the proper variables keeping into account default values @@ -126,12 +128,16 @@ impl PyFunction { bytecode::Varargs::None => None, }; + let mut posonly_passed_as_kwarg = Vec::new(); // Handle keyword arguments for (name, value) in func_args.kwargs { // Check if we have a parameter with this name: if code_object.arg_names.contains(&name) || code_object.kwonlyarg_names.contains(&name) { - if locals.contains_key(&name, vm) { + if posonly_args.contains(&name) { + posonly_passed_as_kwarg.push(name); + continue; + } else if locals.contains_key(&name, vm) { return Err( vm.new_type_error(format!("Got multiple values for argument '{}'", name)) ); @@ -146,6 +152,13 @@ impl PyFunction { ); } } + if !posonly_passed_as_kwarg.is_empty() { + return Err(vm.new_type_error(format!( + "{}() got some positional-only arguments passed as keyword arguments: '{}'", + &code_object.obj_name, + posonly_passed_as_kwarg.into_iter().format(", "), + ))); + } // Add missing positional arguments, if we have fewer positional arguments than the // function definition calls for