diff --git a/src/derive_utils.rs b/src/derive_utils.rs index 110e4a9a8e8..8773f06b803 100644 --- a/src/derive_utils.rs +++ b/src/derive_utils.rs @@ -45,7 +45,7 @@ pub fn parse_fn_args<'p>( ) -> PyResult<()> { let nargs = args.len(); let nkeywords = kwargs.map_or(0, |d| d.len()); - if !accept_args && (nargs + nkeywords > params.len()) { + if !accept_args && !accept_kwargs && (nargs + nkeywords > params.len()) { return Err(TypeError::py_err(format!( "{}{} takes at most {} argument{} ({} given)", fname.unwrap_or("function"), diff --git a/tests/test_variable_arguments.rs b/tests/test_variable_arguments.rs new file mode 100644 index 00000000000..5dde81cc7f6 --- /dev/null +++ b/tests/test_variable_arguments.rs @@ -0,0 +1,52 @@ +#![feature(custom_attribute)] +#![feature(specialization)] + +extern crate pyo3; + +use pyo3::prelude::*; +use pyo3::types::{PyDict, PyTuple}; + +#[macro_use] +mod common; + +#[pyclass] +struct MyClass {} + +#[pymethods] +impl MyClass { + #[staticmethod] + #[args(args = "*")] + fn test_args(args: &PyTuple) -> PyResult<&PyTuple> { + Ok(args) + } + + #[staticmethod] + #[args(kwargs = "**")] + fn test_kwargs(kwargs: Option<&PyDict>) -> PyResult> { + Ok(kwargs) + } +} + +#[test] +fn variable_args() { + let gil = Python::acquire_gil(); + let py = gil.python(); + let my_obj = py.get_type::(); + py_assert!(py, my_obj, "my_obj.test_args() == ()"); + py_assert!(py, my_obj, "my_obj.test_args(1) == (1,)"); + py_assert!(py, my_obj, "my_obj.test_args(1, 2) == (1, 2)"); +} + +#[test] +fn variable_kwargs() { + let gil = Python::acquire_gil(); + let py = gil.python(); + let my_obj = py.get_type::(); + py_assert!(py, my_obj, "my_obj.test_kwargs() == None"); + py_assert!(py, my_obj, "my_obj.test_kwargs(test=1) == {'test': 1}"); + py_assert!( + py, + my_obj, + "my_obj.test_kwargs(test1=1, test2=2) == {'test1':1, 'test2':2}" + ); +}