diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 1d9740d38..7fa0348eb 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -39,7 +39,10 @@ PYBIND11_NOINLINE inline internals &get_internals() { return *internals_ptr; handle builtins(PyEval_GetBuiltins()); const char *id = PYBIND11_INTERNALS_ID; - capsule caps(builtins[id]); + capsule caps; + if (builtins.contains(id)) { + caps = builtins[id]; + } if (caps.check()) { internals_ptr = caps; } else { @@ -1221,7 +1224,7 @@ private: } void process(list &/*args_list*/, arg_v a) { - if (m_kwargs[a.name]) { + if (m_kwargs.contains(a.name)) { #if defined(NDEBUG) multiple_values_error(); #else @@ -1240,7 +1243,7 @@ private: void process(list &/*args_list*/, detail::kwargs_proxy kp) { for (const auto &k : dict(kp, true)) { - if (m_kwargs[k.first]) { + if (m_kwargs.contains(k.first)) { #if defined(NDEBUG) multiple_values_error(); #else diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index a3e66728a..fb8d3c6d0 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -125,11 +125,11 @@ private: static npy_api lookup() { module m = module::import("numpy.core.multiarray"); - object c = (object) m.attr("_ARRAY_API"); + auto c = m.attr("_ARRAY_API").cast(); #if PY_MAJOR_VERSION >= 3 - void **api_ptr = (void **) (c ? PyCapsule_GetPointer(c.ptr(), NULL) : nullptr); + void **api_ptr = (void **) PyCapsule_GetPointer(c.ptr(), NULL); #else - void **api_ptr = (void **) (c ? PyCObject_AsVoidPtr(c.ptr()) : nullptr); + void **api_ptr = (void **) PyCObject_AsVoidPtr(c.ptr()); #endif npy_api api; #define DECL_NPY_API(Func) api.Func##_ = (decltype(api.Func##_)) api_ptr[API_##Func]; diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 4a6fa8dcf..a0e2e725c 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -278,9 +278,11 @@ protected: object scope_module; if (rec->scope) { - scope_module = (object) rec->scope.attr("__module__"); - if (!scope_module) - scope_module = (object) rec->scope.attr("__name__"); + if (hasattr(rec->scope, "__module__")) { + scope_module = rec->scope.attr("__module__"); + } else if (hasattr(rec->scope, "__name__")) { + scope_module = rec->scope.attr("__name__"); + } } m_ptr = PyCFunction_NewEx(rec->def, rec_capsule.ptr(), scope_module.ptr()); @@ -544,8 +546,8 @@ public: template module &def(const char *name_, Func &&f, const Extra& ... extra) { - cpp_function func(std::forward(f), name(name_), - sibling((handle) attr(name_)), scope(*this), extra...); + cpp_function func(std::forward(f), name(name_), scope(*this), + sibling(getattr(*this, name_, none())), extra...); /* PyModule_AddObject steals a reference to 'func' */ PyModule_AddObject(ptr(), name_, func.inc_ref().ptr()); return *this; @@ -588,16 +590,18 @@ protected: object name(PYBIND11_FROM_STRING(rec->name), false); object scope_module; if (rec->scope) { - scope_module = (object) rec->scope.attr("__module__"); - if (!scope_module) - scope_module = (object) rec->scope.attr("__name__"); + if (hasattr(rec->scope, "__module__")) { + scope_module = rec->scope.attr("__module__"); + } else if (hasattr(rec->scope, "__name__")) { + scope_module = rec->scope.attr("__name__"); + } } #if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 /* Qualified names for Python >= 3.3 */ object scope_qualname; - if (rec->scope) - scope_qualname = (object) rec->scope.attr("__qualname__"); + if (rec->scope && hasattr(rec->scope, "__qualname__")) + scope_qualname = rec->scope.attr("__qualname__"); object ht_qualname; if (scope_qualname) { ht_qualname = object(PyUnicode_FromFormat( @@ -894,17 +898,16 @@ public: template class_ &def(const char *name_, Func&& f, const Extra&... extra) { - cpp_function cf(std::forward(f), name(name_), - sibling(attr(name_)), is_method(*this), - extra...); + cpp_function cf(std::forward(f), name(name_), is_method(*this), + sibling(getattr(*this, name_, none())), extra...); attr(cf.name()) = cf; return *this; } template class_ & def_static(const char *name_, Func f, const Extra&... extra) { - cpp_function cf(std::forward(f), name(name_), - sibling(attr(name_)), scope(*this), extra...); + cpp_function cf(std::forward(f), name(name_), scope(*this), + sibling(getattr(*this, name_, none())), extra...); attr(cf.name()) = cf; return *this; } @@ -1338,16 +1341,16 @@ PYBIND11_NOINLINE inline void print(tuple args, dict kwargs) { for (size_t i = 0; i < args.size(); ++i) { strings[i] = args[i].cast().str(); } - auto sep = kwargs["sep"] ? kwargs["sep"] : cast(" "); + auto sep = kwargs.contains("sep") ? kwargs["sep"] : cast(" "); auto line = sep.attr("join").cast()(strings); - auto file = kwargs["file"] ? kwargs["file"].cast() - : module::import("sys").attr("stdout"); + auto file = kwargs.contains("file") ? kwargs["file"].cast() + : module::import("sys").attr("stdout"); auto write = file.attr("write").cast(); write(line); - write(kwargs["end"] ? kwargs["end"] : cast("\n")); + write(kwargs.contains("end") ? kwargs["end"] : cast("\n")); - if (kwargs["flush"] && kwargs["flush"].cast()) { + if (kwargs.contains("flush") && kwargs["flush"].cast()) { file.attr("flush").cast()(); } } @@ -1500,7 +1503,7 @@ inline function get_type_overload(const void *this_ptr, const detail::type_info if (cache.find(key) != cache.end()) return function(); - function overload = (function) py_object.attr(name); + function overload = getattr(py_object, name, function()); if (overload.is_cpp_function()) { cache.insert(key); return function(); diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index 872c90cc9..180bcdd7c 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -41,6 +41,7 @@ public: accessor attr(handle key) const; accessor attr(const char *key) const; args_proxy operator*() const; + template bool contains(T &&key) const; template object operator()(Args &&...args) const; @@ -117,6 +118,52 @@ public: template T cast() &&; }; +inline bool hasattr(handle obj, handle name) { + return PyObject_HasAttr(obj.ptr(), name.ptr()) == 1; +} + +inline bool hasattr(handle obj, const char *name) { + return PyObject_HasAttrString(obj.ptr(), name) == 1; +} + +inline object getattr(handle obj, handle name) { + PyObject *result = PyObject_GetAttr(obj.ptr(), name.ptr()); + if (!result) { throw error_already_set(); } + return {result, false}; +} + +inline object getattr(handle obj, const char *name) { + PyObject *result = PyObject_GetAttrString(obj.ptr(), name); + if (!result) { throw error_already_set(); } + return {result, false}; +} + +inline object getattr(handle obj, handle name, handle default_) { + if (PyObject *result = PyObject_GetAttr(obj.ptr(), name.ptr())) { + return {result, false}; + } else { + PyErr_Clear(); + return {default_, true}; + } +} + +inline object getattr(handle obj, const char *name, handle default_) { + if (PyObject *result = PyObject_GetAttrString(obj.ptr(), name)) { + return {result, false}; + } else { + PyErr_Clear(); + return {default_, true}; + } +} + +inline void setattr(handle obj, handle name, handle value) { + if (PyObject_SetAttr(obj.ptr(), name.ptr(), value.ptr()) != 0) { throw error_already_set(); } +} + +inline void setattr(handle obj, const char *name, handle value) { + if (PyObject_SetAttrString(obj.ptr(), name, value.ptr()) != 0) { throw error_already_set(); } +} + NAMESPACE_BEGIN(detail) inline handle get_function(handle value) { if (value) { @@ -151,24 +198,14 @@ public: } operator object() const { - object result(attr ? PyObject_GetAttr(obj.ptr(), key.ptr()) - : PyObject_GetItem(obj.ptr(), key.ptr()), false); - if (!result) {PyErr_Clear(); } - return result; + PyObject *result = attr ? PyObject_GetAttr(obj.ptr(), key.ptr()) + : PyObject_GetItem(obj.ptr(), key.ptr()); + if (!result) { throw error_already_set(); } + return {result, false}; } template T cast() const { return operator object().cast(); } - operator bool() const { - if (attr) { - return (bool) PyObject_HasAttr(obj.ptr(), key.ptr()); - } else { - object result(PyObject_GetItem(obj.ptr(), key.ptr()), false); - if (!result) PyErr_Clear(); - return (bool) result; - } - }; - private: handle obj; object key; @@ -598,6 +635,8 @@ public: detail::dict_iterator begin() const { return (++detail::dict_iterator(*this, 0)); } detail::dict_iterator end() const { return detail::dict_iterator(); } void clear() const { PyDict_Clear(ptr()); } + bool contains(handle key) const { return PyDict_Contains(ptr(), key.ptr()) == 1; } + bool contains(const char *key) const { return PyDict_Contains(ptr(), pybind11::str(key).ptr()) == 1; } }; class list : public object { @@ -695,6 +734,9 @@ template accessor object_api::operator[](const char *key) const template accessor object_api::attr(handle key) const { return {derived(), key, true}; } template accessor object_api::attr(const char *key) const { return {derived(), key, true}; } template args_proxy object_api::operator*() const { return {derived().ptr()}; } +template template bool object_api::contains(T &&key) const { + return attr("__contains__").template cast()(std::forward(key)).template cast(); +} template pybind11::str object_api::str() const { diff --git a/tests/pybind11_tests.cpp b/tests/pybind11_tests.cpp index f3f557a23..35981a0a6 100644 --- a/tests/pybind11_tests.cpp +++ b/tests/pybind11_tests.cpp @@ -39,7 +39,7 @@ PYBIND11_PLUGIN(pybind11_tests) { for (const auto &initializer : initializers()) initializer(m); - if (!m.attr("have_eigen")) m.attr("have_eigen") = py::cast(false); + if (!py::hasattr(m, "have_eigen")) m.attr("have_eigen") = py::cast(false); return m.ptr(); }