diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 0b6ff0859..90d9d4b33 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -40,12 +40,8 @@ PYBIND11_NOINLINE inline internals &get_internals() { return *internals_ptr; handle builtins(PyEval_GetBuiltins()); const char *id = PYBIND11_INTERNALS_ID; - capsule caps; - if (builtins.contains(id)) { - caps = builtins[id]; - } - if (caps.check()) { - internals_ptr = caps; + if (builtins.contains(id) && isinstance(builtins[id])) { + internals_ptr = capsule(builtins[id]); } else { internals_ptr = new internals(); #if defined(WITH_THREAD) @@ -111,6 +107,17 @@ PYBIND11_NOINLINE inline handle get_type_handle(const std::type_info &tp, bool t return handle(type_info ? ((PyObject *) type_info->type) : nullptr); } +PYBIND11_NOINLINE inline bool isinstance_generic(handle obj, const std::type_info &tp) { + const auto type = detail::get_type_handle(tp, false); + if (!type) + return false; + + const auto result = PyObject_IsInstance(obj.ptr(), type.ptr()); + if (result == -1) + throw error_already_set(); + return result != 0; +} + PYBIND11_NOINLINE inline std::string error_string() { if (!PyErr_Occurred()) { PyErr_SetString(PyExc_RuntimeError, "Unknown internal error occurred"); @@ -536,9 +543,8 @@ public: } /* Check if this is a capsule */ - capsule c(h, true); - if (c.check()) { - value = (void *) c; + if (isinstance(h)) { + value = capsule(h, true); return true; } @@ -986,13 +992,17 @@ template <> struct handle_type_name { static PYBIND11_DESCR name() { retur template <> struct handle_type_name { static PYBIND11_DESCR name() { return _("**kwargs"); } }; template -struct type_caster::value>> { -public: - template ::value, int> = 0> - bool load(handle src, bool /* convert */) { value = type(src); return value.check(); } +struct pyobject_caster { + template ::value, int> = 0> + bool load(handle src, bool /* convert */) { value = src; return static_cast(value); } template ::value, int> = 0> - bool load(handle src, bool /* convert */) { value = type(src, true); return value.check(); } + bool load(handle src, bool /* convert */) { + if (!isinstance(src)) + return false; + value = type(src, true); + return true; + } static handle cast(const handle &src, return_value_policy /* policy */, handle /* parent */) { return src.inc_ref(); @@ -1000,6 +1010,9 @@ public: PYBIND11_TYPE_CASTER(type, handle_type_name::name()); }; +template +class type_caster::value>> : public pyobject_caster { }; + // Our conditions for enabling moving are quite restrictive: // At compile time: // - T needs to be a non-const, non-pointer, non-reference type diff --git a/include/pybind11/eigen.h b/include/pybind11/eigen.h index d8bf41cd0..3da857b71 100644 --- a/include/pybind11/eigen.h +++ b/include/pybind11/eigen.h @@ -55,7 +55,7 @@ struct type_caster::value && !is_eigen_re bool load(handle src, bool) { array_t buf(src, true); - if (!buf.check()) + if (!buf) return false; if (buf.ndim() == 1) { @@ -201,7 +201,7 @@ struct type_caster::value>> { auto shape = pybind11::tuple((pybind11::object) obj.attr("shape")); auto nnz = obj.attr("nnz").cast(); - if (!values.check() || !innerIndices.check() || !outerIndices.check()) + if (!values || !innerIndices || !outerIndices) return false; value = Eigen::MappedSparseMatrix( diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index 5309997c4..2ffbc5f5c 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -324,10 +324,9 @@ public: int flags = 0; if (base && ptr) { - array base_array(base, true); - if (base_array.check()) + if (isinstance(base)) /* Copy flags from base (except baseship bit) */ - flags = base_array.flags() & ~detail::npy_api::NPY_ARRAY_OWNDATA_; + flags = array(base, true).flags() & ~detail::npy_api::NPY_ARRAY_OWNDATA_; else /* Writable by default, easy to downgrade later on if needed */ flags = detail::npy_api::NPY_ARRAY_WRITEABLE_; @@ -627,6 +626,21 @@ struct format_descriptor::value>> { }; NAMESPACE_BEGIN(detail) +template +struct pyobject_caster> { + using type = array_t; + + bool load(handle src, bool /* convert */) { + value = type(src, true); + return static_cast(value); + } + + static handle cast(const handle &src, return_value_policy /* policy */, handle /* parent */) { + return src.inc_ref(); + } + PYBIND11_TYPE_CASTER(type, handle_type_name::name()); +}; + template struct is_std_array : std::false_type { }; template struct is_std_array> : std::true_type { }; diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index bfe169a72..ced8eb076 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -22,6 +22,7 @@ struct arg; struct arg_v; NAMESPACE_BEGIN(detail) class args_proxy; +inline bool isinstance_generic(handle obj, const std::type_info &tp); // Accessor forward declarations template class accessor; @@ -91,6 +92,7 @@ public: explicit operator bool() const { return m_ptr != nullptr; } bool operator==(const handle &h) const { return m_ptr == h.m_ptr; } bool operator!=(const handle &h) const { return m_ptr != h.m_ptr; } + PYBIND11_DEPRECATED("Use handle::operator bool() instead") bool check() const { return m_ptr != nullptr; } protected: PyObject *m_ptr = nullptr; @@ -135,6 +137,16 @@ public: template T cast() &&; }; +/// Check if `obj` is an instance of type `T` +template ::value, int> = 0> +bool isinstance(handle obj) { return T::_check(obj); } + +template ::value, int> = 0> +bool isinstance(handle obj) { return detail::isinstance_generic(obj, typeid(T)); } + +template <> inline bool isinstance(handle obj) = delete; +template <> inline bool isinstance(handle obj) { return obj.ptr() != nullptr; } + inline bool hasattr(handle obj, handle name) { return PyObject_HasAttr(obj.ptr(), name.ptr()) == 1; } @@ -386,7 +398,9 @@ NAMESPACE_END(detail) Name(object&& o) noexcept : Parent(std::move(o)) { CvtStmt; } \ Name& operator=(object&& o) noexcept { (void) object::operator=(std::move(o)); CvtStmt; return *this; } \ Name& operator=(const object& o) { return static_cast(object::operator=(o)); CvtStmt; } \ - bool check() const { return m_ptr != nullptr && (bool) CheckFun(m_ptr); } + PYBIND11_DEPRECATED("Use py::isinstance(obj) instead") \ + bool check() const { return m_ptr != nullptr && (bool) CheckFun(m_ptr); } \ + static bool _check(handle h) { return h.ptr() != nullptr && CheckFun(h.ptr()); } #define PYBIND11_OBJECT(Name, Parent, CheckFun) \ PYBIND11_OBJECT_CVT(Name, Parent, CheckFun, ) diff --git a/include/pybind11/stl.h b/include/pybind11/stl.h index 04d2daad1..ed8ab5cbb 100644 --- a/include/pybind11/stl.h +++ b/include/pybind11/stl.h @@ -45,9 +45,9 @@ template struct set_caster { using key_conv = make_caster; bool load(handle src, bool convert) { - pybind11::set s(src, true); - if (!s.check()) + if (!isinstance(src)) return false; + pybind11::set s(src, true); value.clear(); key_conv conv; for (auto entry : s) { @@ -77,9 +77,9 @@ template struct map_caster { using value_conv = make_caster; bool load(handle src, bool convert) { - dict d(src, true); - if (!d.check()) + if (!isinstance(src)) return false; + dict d(src, true); key_conv kconv; value_conv vconv; value.clear(); @@ -112,9 +112,9 @@ template struct list_caster { using value_conv = make_caster; bool load(handle src, bool convert) { - sequence s(src, true); - if (!s.check()) + if (!isinstance(src)) return false; + sequence s(src, true); value_conv conv; value.clear(); reserve_maybe(s, &value); @@ -157,9 +157,9 @@ template struct type_caster> using value_conv = make_caster; bool load(handle src, bool convert) { - list l(src, true); - if (!l.check()) + if (!isinstance(src)) return false; + list l(src, true); if (l.size() != Size) return false; value_conv conv; diff --git a/tests/test_inheritance.cpp b/tests/test_inheritance.cpp index f43edc261..2ec0b4a7a 100644 --- a/tests/test_inheritance.cpp +++ b/tests/test_inheritance.cpp @@ -83,4 +83,18 @@ test_initializer inheritance([](py::module &m) { return new BaseClass(); }); m.def("return_none", []() -> BaseClass* { return nullptr; }); + + m.def("test_isinstance", [](py::list l) { + struct Unregistered { }; // checks missing type_info code path + + return py::make_tuple( + py::isinstance(l[0]), + py::isinstance(l[1]), + py::isinstance(l[2]), + py::isinstance(l[3]), + py::isinstance(l[4]), + py::isinstance(l[5]), + py::isinstance(l[6]) + ); + }); }); diff --git a/tests/test_inheritance.py b/tests/test_inheritance.py index 351fe6b2c..7bb52be02 100644 --- a/tests/test_inheritance.py +++ b/tests/test_inheritance.py @@ -45,3 +45,11 @@ def test_automatic_upcasting(): assert type(return_class_n(2)).__name__ == "DerivedClass2" assert type(return_class_n(0)).__name__ == "BaseClass" assert type(return_class_n(1)).__name__ == "DerivedClass1" + + +def test_isinstance(): + from pybind11_tests import test_isinstance, Pet, Dog + + objects = [tuple(), dict(), Pet("Polly", "parrot")] + [Dog("Molly")] * 4 + expected = (True, True, True, True, True, False, False) + assert test_isinstance(objects) == expected