From 5c5527dfec1039b41b45fae313b703c938f3e156 Mon Sep 17 00:00:00 2001 From: David Hewitt <1939362+davidhewitt@users.noreply.github.com> Date: Sun, 17 May 2020 19:17:24 +0100 Subject: [PATCH] Fix passing explicit None to pyfunction with default --- CHANGELOG.md | 3 +++ pyo3-derive-backend/src/pymethod.rs | 10 +++++----- tests/test_pyfunction.rs | 22 ++++++++++++++++++++++ 3 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 tests/test_pyfunction.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 05375f97..48b4a484 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Removed - Remove `ManagedPyRef` (unused, and needs specialization) [#930](https://github.com/PyO3/pyo3/pull/930) +### Fixed +- Fix passing explicit `None` to `Option` argument `#[pyfunction]` with a default value. [#936](https://github.com/PyO3/pyo3/pull/936) + ## [0.10.1] - 2020-05-14 ### Fixed - Fix deadlock in `Python::acquire_gil()` after dropping a `PyObject` or `Py`. [#924](https://github.com/PyO3/pyo3/pull/924) diff --git a/pyo3-derive-backend/src/pymethod.rs b/pyo3-derive-backend/src/pymethod.rs index 1fabe589..58242cd7 100644 --- a/pyo3-derive-backend/src/pymethod.rs +++ b/pyo3-derive-backend/src/pymethod.rs @@ -520,9 +520,9 @@ fn impl_arg_param( }; // Get Option<&T> from Option> quote! { - let #mut_ _tmp = match #arg_value.as_ref().filter(|obj| !obj.is_none()) { + let #mut_ _tmp = match #arg_value { Some(_obj) => { - Some(_obj.extract::<<#tref as pyo3::derive_utils::ExtractExt>::Target>()?) + _obj.extract::::Target>>()? }, None => #default, }; @@ -530,15 +530,15 @@ fn impl_arg_param( } } else { quote! { - let #arg_name = match #arg_value.as_ref().filter(|obj| !obj.is_none()) { - Some(_obj) => Some(_obj.extract()?), + let #arg_name = match #arg_value { + Some(_obj) => _obj.extract()?, None => #default, }; } } } else if let Some(default) = spec.default_value(name) { quote! { - let #arg_name = match #arg_value.as_ref().filter(|obj| !obj.is_none()) { + let #arg_name = match #arg_value { Some(_obj) => _obj.extract()?, None => #default, }; diff --git a/tests/test_pyfunction.rs b/tests/test_pyfunction.rs new file mode 100644 index 00000000..c3e56547 --- /dev/null +++ b/tests/test_pyfunction.rs @@ -0,0 +1,22 @@ +use pyo3::prelude::*; +use pyo3::wrap_pyfunction; + +mod common; + +#[pyfunction(arg = "true")] +fn optional_bool(arg: Option) -> String { + format!("{:?}", arg) +} + +#[test] +fn test_optional_bool() { + // Regression test for issue #932 + let gil = Python::acquire_gil(); + let py = gil.python(); + let f = wrap_pyfunction!(optional_bool)(py); + + py_assert!(py, f, "f() == 'Some(true)'"); + py_assert!(py, f, "f(True) == 'Some(true)'"); + py_assert!(py, f, "f(False) == 'Some(false)'"); + py_assert!(py, f, "f(None) == 'None'"); +}