"vscode:/vscode.git/clone" did not exist on "b3f0b4de663e31e2eeb5fbe76cd31d07cc2dd803"
Commit 865e4303 authored by Dean Moldovan's avatar Dean Moldovan
Browse files

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.
parent 37e22e43
......@@ -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();
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment