diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 1514faba1..91b3e375d 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -218,6 +218,8 @@ public: if (!src || !typeinfo) return false; if (src.is_none()) { + // Defer accepting None to other overloads (if we aren't in convert mode): + if (!convert) return false; value = nullptr; return true; } @@ -982,6 +984,8 @@ public: if (!src || !typeinfo) return false; if (src.is_none()) { + // Defer accepting None to other overloads (if we aren't in convert mode): + if (!convert) return false; value = nullptr; return true; } diff --git a/include/pybind11/functional.h b/include/pybind11/functional.h index ab9e1c3c5..ae168c4d8 100644 --- a/include/pybind11/functional.h +++ b/include/pybind11/functional.h @@ -22,9 +22,12 @@ struct type_caster<std::function<Return(Args...)>> { using function_type = Return (*) (Args...); public: - bool load(handle src, bool) { - if (src.is_none()) + bool load(handle src, bool convert) { + if (src.is_none()) { + // Defer accepting None to other overloads (if we aren't in convert mode): + if (!convert) return false; return true; + } if (!isinstance<function>(src)) return false; diff --git a/tests/test_python_types.cpp b/tests/test_python_types.cpp index a82145be5..f2793c036 100644 --- a/tests/test_python_types.cpp +++ b/tests/test_python_types.cpp @@ -500,6 +500,18 @@ test_initializer python_types([](py::module &m) { m.def("return_none_int", []() -> int * { return nullptr; }); m.def("return_none_float", []() -> float * { return nullptr; }); + m.def("defer_none_cstring", [](char *) { return false; }); + m.def("defer_none_cstring", [](py::none) { return true; }); + m.def("defer_none_custom", [](ExamplePythonTypes *) { return false; }); + m.def("defer_none_custom", [](py::none) { return true; }); + // void and optional, however, don't defer: + m.def("nodefer_none_void", [](void *) { return true; }); + m.def("nodefer_none_void", [](py::none) { return false; }); +#ifdef PYBIND11_HAS_OPTIONAL + m.def("nodefer_none_optional", [](std::optional<int>) { return true; }); + m.def("nodefer_none_optional", [](py::none) { return false; }); +#endif + m.def("return_capsule_with_destructor", []() { py::print("creating capsule"); diff --git a/tests/test_python_types.py b/tests/test_python_types.py index 3e337d4d5..81a3edec9 100644 --- a/tests/test_python_types.py +++ b/tests/test_python_types.py @@ -550,8 +550,22 @@ def test_builtins_cast_return_none(): assert m.return_none_float() is None +def test_none_deferred(): + """None passed as various argument types should defer to other overloads""" + import pybind11_tests as m + + assert not m.defer_none_cstring("abc") + assert m.defer_none_cstring(None) + assert not m.defer_none_custom(m.ExamplePythonTypes.new_instance()) + assert m.defer_none_custom(None) + assert m.nodefer_none_void(None) + if has_optional: + assert m.nodefer_none_optional(None) + + def test_capsule_with_destructor(capture): import pybind11_tests as m + pytest.gc_collect() with capture: a = m.return_capsule_with_destructor() del a