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:
Dean Moldovan 2016-09-21 01:06:32 +02:00
parent 37e22e436e
commit 865e43034b
5 changed files with 90 additions and 42 deletions

View File

@ -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

View File

@ -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];

View File

@ -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();

View File

@ -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 {

View File

@ -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();
}