mirror of
https://github.com/pybind/pybind11.git
synced 2025-01-19 01:15:52 +00:00
Make attr and item accessors throw on error instead of returning nullptr
This also adds the `hasattr` and `getattr` functions which are needed with the new attribute behavior. The new functions behave exactly like their Python counterparts. Similarly `object` gets a `contains` method which calls `__contains__`, i.e. it's the same as the `in` keyword in Python.
This commit is contained in:
parent
37e22e436e
commit
865e43034b
@ -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
|
||||
|
@ -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<object>();
|
||||
#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];
|
||||
|
@ -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 <typename Func, typename... Extra>
|
||||
module &def(const char *name_, Func &&f, const Extra& ... extra) {
|
||||
cpp_function func(std::forward<Func>(f), name(name_),
|
||||
sibling((handle) attr(name_)), scope(*this), extra...);
|
||||
cpp_function func(std::forward<Func>(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 <typename Func, typename... Extra>
|
||||
class_ &def(const char *name_, Func&& f, const Extra&... extra) {
|
||||
cpp_function cf(std::forward<Func>(f), name(name_),
|
||||
sibling(attr(name_)), is_method(*this),
|
||||
extra...);
|
||||
cpp_function cf(std::forward<Func>(f), name(name_), is_method(*this),
|
||||
sibling(getattr(*this, name_, none())), extra...);
|
||||
attr(cf.name()) = cf;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Func, typename... Extra> class_ &
|
||||
def_static(const char *name_, Func f, const Extra&... extra) {
|
||||
cpp_function cf(std::forward<Func>(f), name(name_),
|
||||
sibling(attr(name_)), scope(*this), extra...);
|
||||
cpp_function cf(std::forward<Func>(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<object>().str();
|
||||
}
|
||||
auto sep = kwargs["sep"] ? kwargs["sep"] : cast(" ");
|
||||
auto sep = kwargs.contains("sep") ? kwargs["sep"] : cast(" ");
|
||||
auto line = sep.attr("join").cast<object>()(strings);
|
||||
|
||||
auto file = kwargs["file"] ? kwargs["file"].cast<object>()
|
||||
: module::import("sys").attr("stdout");
|
||||
auto file = kwargs.contains("file") ? kwargs["file"].cast<object>()
|
||||
: module::import("sys").attr("stdout");
|
||||
auto write = file.attr("write").cast<object>();
|
||||
write(line);
|
||||
write(kwargs["end"] ? kwargs["end"] : cast("\n"));
|
||||
write(kwargs.contains("end") ? kwargs["end"] : cast("\n"));
|
||||
|
||||
if (kwargs["flush"] && kwargs["flush"].cast<bool>()) {
|
||||
if (kwargs.contains("flush") && kwargs["flush"].cast<bool>()) {
|
||||
file.attr("flush").cast<object>()();
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
|
@ -41,6 +41,7 @@ public:
|
||||
accessor attr(handle key) const;
|
||||
accessor attr(const char *key) const;
|
||||
args_proxy operator*() const;
|
||||
template <typename T> bool contains(T &&key) const;
|
||||
|
||||
template <return_value_policy policy = return_value_policy::automatic_reference, typename... Args>
|
||||
object operator()(Args &&...args) const;
|
||||
@ -117,6 +118,52 @@ public:
|
||||
template <typename T> 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 <typename T> T cast() const { return operator object().cast<T>(); }
|
||||
|
||||
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 <typename D> accessor object_api<D>::operator[](const char *key) const
|
||||
template <typename D> accessor object_api<D>::attr(handle key) const { return {derived(), key, true}; }
|
||||
template <typename D> accessor object_api<D>::attr(const char *key) const { return {derived(), key, true}; }
|
||||
template <typename D> args_proxy object_api<D>::operator*() const { return {derived().ptr()}; }
|
||||
template <typename D> template <typename T> bool object_api<D>::contains(T &&key) const {
|
||||
return attr("__contains__").template cast<object>()(std::forward<T>(key)).template cast<bool>();
|
||||
}
|
||||
|
||||
template <typename D>
|
||||
pybind11::str object_api<D>::str() const {
|
||||
|
@ -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();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user