From 652231a683bfa927472e5d5a3410e11c9484a684 Mon Sep 17 00:00:00 2001 From: Noah <33094578+coolreader18@users.noreply.github.com> Date: Fri, 13 Mar 2020 20:22:46 -0500 Subject: [PATCH 1/2] Error on positional-only parameters passed as keywords --- vm/src/obj/objfunction.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) 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 From aad212299f4012b0bca8ddcc8e3c6e81959415a3 Mon Sep 17 00:00:00 2001 From: Noah <33094578+coolreader18@users.noreply.github.com> Date: Fri, 13 Mar 2020 20:23:09 -0500 Subject: [PATCH 2/2] Add tests for positional/keyword only arguments --- tests/snippets/function_args.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) 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)