Commit c7ac16bb by Dean Moldovan Committed by Wenzel Jakob

Add py::reinterpret_borrow<T>()/steal<T>() for low-level unchecked casts

The pytype converting constructors are convenient and safe for user
code, but for library internals the additional type checks and possible
conversions are sometimes not desired. `reinterpret_borrow<T>()` and
`reinterpret_steal<T>()` serve as the low-level unsafe counterparts
of `cast<T>()`.

This deprecates the `object(handle, bool)` constructor.

Renamed `borrowed` parameter to `is_borrowed` to avoid shadowing
warnings on MSVC.
parent e18bc02f
......@@ -182,7 +182,7 @@ struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> {
if (!src)
return false;
object obj(src, true);
auto obj = reinterpret_borrow<object>(src);
object sparse_module = module::import("scipy.sparse");
object matrix_type = sparse_module.attr(
rowMajor ? "csr_matrix" : "csc_matrix");
......
......@@ -31,7 +31,7 @@ enum eval_mode {
template <eval_mode mode = eval_expr>
object eval(str expr, object global = object(), object local = object()) {
if (!global) {
global = object(PyEval_GetGlobals(), true);
global = reinterpret_borrow<object>(PyEval_GetGlobals());
if (!global)
global = dict();
}
......@@ -50,17 +50,16 @@ object eval(str expr, object global = object(), object local = object()) {
default: pybind11_fail("invalid evaluation mode");
}
object result(PyRun_String(buffer.c_str(), start, global.ptr(), local.ptr()), false);
PyObject *result = PyRun_String(buffer.c_str(), start, global.ptr(), local.ptr());
if (!result)
throw error_already_set();
return result;
return reinterpret_steal<object>(result);
}
template <eval_mode mode = eval_statements>
object eval_file(str fname, object global = object(), object local = object()) {
if (!global) {
global = object(PyEval_GetGlobals(), true);
global = reinterpret_borrow<object>(PyEval_GetGlobals());
if (!global)
global = dict();
}
......@@ -83,9 +82,9 @@ object eval_file(str fname, object global = object(), object local = object()) {
FILE *f = _Py_fopen(fname.ptr(), "r");
#else
/* No unicode support in open() :( */
object fobj(PyFile_FromString(
auto fobj = reinterpret_steal<object>(PyFile_FromString(
const_cast<char *>(fname_str.c_str()),
const_cast<char*>("r")), false);
const_cast<char*>("r")));
FILE *f = nullptr;
if (fobj)
f = PyFile_AsFile(fobj.ptr());
......@@ -96,14 +95,11 @@ object eval_file(str fname, object global = object(), object local = object()) {
pybind11_fail("File \"" + fname_str + "\" could not be opened!");
}
object result(PyRun_FileEx(f, fname_str.c_str(), start, global.ptr(),
local.ptr(), closeFile),
false);
PyObject *result = PyRun_FileEx(f, fname_str.c_str(), start, global.ptr(),
local.ptr(), closeFile);
if (!result)
throw error_already_set();
return result;
return reinterpret_steal<object>(result);
}
NAMESPACE_END(pybind11)
......@@ -36,7 +36,7 @@ public:
captured variables), in which case the roundtrip can be avoided.
*/
if (PyCFunction_Check(src_.ptr())) {
capsule c(PyCFunction_GetSelf(src_.ptr()), true);
auto c = reinterpret_borrow<capsule>(PyCFunction_GetSelf(src_.ptr()));
auto rec = (function_record *) c;
using FunctionType = Return (*) (Args...);
......@@ -47,7 +47,7 @@ public:
}
}
object src(src_, true);
auto src = reinterpret_borrow<object>(src_);
value = [src](Args... args) -> Return {
gil_scoped_acquire acq;
object retval(src(std::move(args)...));
......
......@@ -239,7 +239,7 @@ public:
PyObject *ptr = nullptr;
if (!detail::npy_api::get().PyArray_DescrConverter_(args.release().ptr(), &ptr) || !ptr)
throw error_already_set();
return object(ptr, false);
return reinterpret_steal<dtype>(ptr);
}
/// Return dtype associated with a C++ type.
......@@ -266,7 +266,7 @@ private:
static object _dtype_from_pep3118() {
static PyObject *obj = module::import("numpy.core._internal")
.attr("_dtype_from_pep3118").cast<object>().release().ptr();
return object(obj, true);
return reinterpret_borrow<object>(obj);
}
dtype strip_padding() {
......@@ -279,7 +279,7 @@ private:
std::vector<field_descr> field_descriptors;
for (auto field : attr("fields").attr("items")()) {
auto spec = object(field, true).cast<tuple>();
auto spec = field.cast<tuple>();
auto name = spec[0].cast<pybind11::str>();
auto format = spec[1].cast<tuple>()[0].cast<dtype>();
auto offset = spec[1].cast<tuple>()[1].cast<pybind11::int_>();
......@@ -326,22 +326,22 @@ public:
if (base && ptr) {
if (isinstance<array>(base))
/* Copy flags from base (except baseship bit) */
flags = array(base, true).flags() & ~detail::npy_api::NPY_ARRAY_OWNDATA_;
flags = reinterpret_borrow<array>(base).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_;
}
object tmp(api.PyArray_NewFromDescr_(
auto tmp = reinterpret_steal<object>(api.PyArray_NewFromDescr_(
api.PyArray_Type_, descr.release().ptr(), (int) ndim, (Py_intptr_t *) shape.data(),
(Py_intptr_t *) strides.data(), const_cast<void *>(ptr), flags, nullptr), false);
(Py_intptr_t *) strides.data(), const_cast<void *>(ptr), flags, nullptr));
if (!tmp)
pybind11_fail("NumPy: unable to create array!");
if (ptr) {
if (base) {
PyArray_GET_(tmp.ptr(), base) = base.inc_ref().ptr();
} else {
tmp = object(api.PyArray_NewCopy_(tmp.ptr(), -1 /* any order */), false);
tmp = reinterpret_steal<object>(api.PyArray_NewCopy_(tmp.ptr(), -1 /* any order */));
}
}
m_ptr = tmp.release().ptr();
......@@ -374,7 +374,7 @@ public:
/// Array descriptor (dtype)
pybind11::dtype dtype() const {
return object(PyArray_GET_(m_ptr, descr), true);
return reinterpret_borrow<pybind11::dtype>(PyArray_GET_(m_ptr, descr));
}
/// Total number of elements
......@@ -399,7 +399,7 @@ public:
/// Base object
object base() const {
return object(PyArray_GET_(m_ptr, base), true);
return reinterpret_borrow<object>(PyArray_GET_(m_ptr, base));
}
/// Dimensions of the array
......@@ -474,14 +474,14 @@ public:
/// Return a new view with all of the dimensions of length 1 removed
array squeeze() {
auto& api = detail::npy_api::get();
return array(api.PyArray_Squeeze_(m_ptr), false);
return reinterpret_steal<array>(api.PyArray_Squeeze_(m_ptr));
}
/// Ensure that the argument is a NumPy array
static array ensure(object input, int ExtraFlags = 0) {
auto& api = detail::npy_api::get();
return array(api.PyArray_FromAny_(
input.release().ptr(), nullptr, 0, 0, detail::npy_api::NPY_ENSURE_ARRAY_ | ExtraFlags, nullptr), false);
return reinterpret_steal<array>(api.PyArray_FromAny_(
input.release().ptr(), nullptr, 0, 0, detail::npy_api::NPY_ENSURE_ARRAY_ | ExtraFlags, nullptr));
}
protected:
......@@ -542,7 +542,7 @@ template <typename T, int ExtraFlags = array::forcecast> class array_t : public
public:
array_t() : array() { }
array_t(handle h, bool borrowed) : array(h, borrowed) { m_ptr = ensure_(m_ptr); }
array_t(handle h, bool is_borrowed) : array(h, is_borrowed) { m_ptr = ensure_(m_ptr); }
array_t(const object &o) : array(o) { m_ptr = ensure_(m_ptr); }
......@@ -668,7 +668,7 @@ public:
enum { value = values[detail::log2(sizeof(T)) * 2 + (std::is_unsigned<T>::value ? 1 : 0)] };
static pybind11::dtype dtype() {
if (auto ptr = npy_api::get().PyArray_DescrFromType_(value))
return object(ptr, true);
return reinterpret_borrow<pybind11::dtype>(ptr);
pybind11_fail("Unsupported buffer format!");
}
template <typename T2 = T, enable_if_t<std::is_signed<T2>::value, int> = 0>
......@@ -683,7 +683,7 @@ template <typename T> constexpr const int npy_format_descriptor<
enum { value = npy_api::NumPyName }; \
static pybind11::dtype dtype() { \
if (auto ptr = npy_api::get().PyArray_DescrFromType_(value)) \
return object(ptr, true); \
return reinterpret_borrow<pybind11::dtype>(ptr); \
pybind11_fail("Unsupported buffer format!"); \
} \
static PYBIND11_DESCR name() { return _(Name); } }
......@@ -778,7 +778,7 @@ struct npy_format_descriptor<T, enable_if_t<is_pod_struct<T>::value>> {
static PYBIND11_DESCR name() { return _("struct"); }
static pybind11::dtype dtype() {
return object(dtype_ptr(), true);
return reinterpret_borrow<pybind11::dtype>(dtype_ptr());
}
static std::string format() {
......@@ -801,7 +801,7 @@ private:
auto& api = npy_api::get();
if (!PyObject_TypeCheck(obj, api.PyVoidArrType_Type_))
return false;
if (auto descr = object(api.PyArray_DescrFromScalar_(obj), false)) {
if (auto descr = reinterpret_steal<object>(api.PyArray_DescrFromScalar_(obj))) {
if (api.PyArray_EquivTypes_(dtype_ptr(), descr.ptr())) {
value = ((PyVoidScalarObject_Proxy *) obj)->obval;
return true;
......
......@@ -256,7 +256,7 @@ protected:
detail::function_record *chain = nullptr, *chain_start = rec;
if (rec->sibling) {
if (PyCFunction_Check(rec->sibling.ptr())) {
capsule rec_capsule(PyCFunction_GetSelf(rec->sibling.ptr()), true);
auto rec_capsule = reinterpret_borrow<capsule>(PyCFunction_GetSelf(rec->sibling.ptr()));
chain = (detail::function_record *) rec_capsule;
/* Never append a method to an overload chain of a parent class;
instead, hide the parent's overloads in this case */
......@@ -382,7 +382,7 @@ protected:
result = PYBIND11_TRY_NEXT_OVERLOAD;
try {
for (; it != nullptr; it = it->next) {
tuple args_(args, true);
auto args_ = reinterpret_borrow<tuple>(args);
size_t kwargs_consumed = 0;
/* For each overload:
......@@ -502,7 +502,7 @@ protected:
msg += "\n";
}
msg += "\nInvoked with: ";
tuple args_(args, true);
auto args_ = reinterpret_borrow<tuple>(args);
for (size_t ti = overloads->is_constructor ? 1 : 0; ti < args_.size(); ++ti) {
msg += static_cast<std::string>(pybind11::str(args_[ti]));
if ((ti + 1) != args_.size() )
......@@ -565,7 +565,7 @@ public:
module def_submodule(const char *name, const char *doc = nullptr) {
std::string full_name = std::string(PyModule_GetName(m_ptr))
+ std::string(".") + std::string(name);
module result(PyImport_AddModule(full_name.c_str()), true);
auto result = reinterpret_borrow<module>(PyImport_AddModule(full_name.c_str()));
if (doc && options::show_user_defined_docstrings())
result.attr("__doc__") = pybind11::str(doc);
attr(name) = result;
......@@ -576,7 +576,7 @@ public:
PyObject *obj = PyImport_ImportModule(name);
if (!obj)
throw import_error("Module \"" + std::string(name) + "\" not found!");
return module(obj, false);
return reinterpret_steal<module>(obj);
}
// Adds an object to the module using the given name. Throws if an object with the given name
......@@ -636,7 +636,7 @@ protected:
pybind11_fail("generic_type: type \"" + std::string(rec->name) +
"\" is already registered!");
object name(PYBIND11_FROM_STRING(rec->name), false);
auto name = reinterpret_steal<object>(PYBIND11_FROM_STRING(rec->name));
object scope_module;
if (rec->scope) {
if (hasattr(rec->scope, rec->name))
......@@ -657,8 +657,8 @@ protected:
scope_qualname = rec->scope.attr("__qualname__");
object ht_qualname;
if (scope_qualname) {
ht_qualname = object(PyUnicode_FromFormat(
"%U.%U", scope_qualname.ptr(), name.ptr()), false);
ht_qualname = reinterpret_steal<object>(PyUnicode_FromFormat(
"%U.%U", scope_qualname.ptr(), name.ptr()));
} else {
ht_qualname = name;
}
......@@ -684,7 +684,7 @@ protected:
garbage collector (the GC will call type_traverse(), which will in
turn find the newly constructed type in an invalid state) */
object type_holder(PyType_Type.tp_alloc(&PyType_Type, 0), false);
auto type_holder = reinterpret_steal<object>(PyType_Type.tp_alloc(&PyType_Type, 0));
auto type = (PyHeapTypeObject*) type_holder.ptr();
if (!type_holder || !name)
......@@ -768,7 +768,7 @@ protected:
/// Helper function which tags all parents of a type using mult. inheritance
void mark_parents_nonsimple(PyTypeObject *value) {
tuple t(value->tp_bases, true);
auto t = reinterpret_borrow<tuple>(value->tp_bases);
for (handle h : t) {
auto tinfo2 = get_type_info((PyTypeObject *) h.ptr());
if (tinfo2)
......@@ -785,10 +785,10 @@ protected:
if (ob_type == &PyType_Type) {
std::string name_ = std::string(ht_type.tp_name) + "__Meta";
#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3
object ht_qualname(PyUnicode_FromFormat("%U__Meta", attr("__qualname__").ptr()), false);
auto ht_qualname = reinterpret_steal<object>(PyUnicode_FromFormat("%U__Meta", attr("__qualname__").ptr()));
#endif
object name(PYBIND11_FROM_STRING(name_.c_str()), false);
object type_holder(PyType_Type.tp_alloc(&PyType_Type, 0), false);
auto name = reinterpret_steal<object>(PYBIND11_FROM_STRING(name_.c_str()));
auto type_holder = reinterpret_steal<object>(PyType_Type.tp_alloc(&PyType_Type, 0));
if (!type_holder || !name)
pybind11_fail("generic_type::metaclass(): unable to create type object!");
......@@ -936,7 +936,7 @@ public:
static_assert(detail::all_of_t<is_valid_class_option, options...>::value,
"Unknown/invalid class_ template parameters provided");
PYBIND11_OBJECT(class_, detail::generic_type, PyType_Check)
PYBIND11_OBJECT(class_, generic_type, PyType_Check)
template <typename... Extra>
class_(handle scope, const char *name, const Extra &... extra) {
......@@ -1117,9 +1117,9 @@ public:
}
}
pybind11::str doc_obj = pybind11::str((rec_fget->doc && pybind11::options::show_user_defined_docstrings()) ? rec_fget->doc : "");
object property(
const auto property = reinterpret_steal<object>(
PyObject_CallFunctionObjArgs((PyObject *) &PyProperty_Type, fget.ptr() ? fget.ptr() : Py_None,
fset.ptr() ? fset.ptr() : Py_None, Py_None, doc_obj.ptr(), nullptr), false);
fset.ptr() ? fset.ptr() : Py_None, Py_None, doc_obj.ptr(), nullptr));
if (rec_fget->class_)
attr(name) = property;
else
......@@ -1178,8 +1178,8 @@ private:
static detail::function_record *get_function_record(handle h) {
h = detail::get_function(h);
return h ? (detail::function_record *) capsule(
PyCFunction_GetSelf(h.ptr()), true) : nullptr;
return h ? (detail::function_record *) reinterpret_borrow<capsule>(PyCFunction_GetSelf(h.ptr()))
: nullptr;
}
};
......
......@@ -47,7 +47,7 @@ template <typename Type, typename Key> struct set_caster {
bool load(handle src, bool convert) {
if (!isinstance<pybind11::set>(src))
return false;
pybind11::set s(src, true);
auto s = reinterpret_borrow<pybind11::set>(src);
value.clear();
key_conv conv;
for (auto entry : s) {
......@@ -61,7 +61,7 @@ template <typename Type, typename Key> struct set_caster {
static handle cast(const type &src, return_value_policy policy, handle parent) {
pybind11::set s;
for (auto const &value: src) {
object value_ = object(key_conv::cast(value, policy, parent), false);
auto value_ = reinterpret_steal<object>(key_conv::cast(value, policy, parent));
if (!value_ || !s.add(value_))
return handle();
}
......@@ -79,7 +79,7 @@ template <typename Type, typename Key, typename Value> struct map_caster {
bool load(handle src, bool convert) {
if (!isinstance<dict>(src))
return false;
dict d(src, true);
auto d = reinterpret_borrow<dict>(src);
key_conv kconv;
value_conv vconv;
value.clear();
......@@ -95,8 +95,8 @@ template <typename Type, typename Key, typename Value> struct map_caster {
static handle cast(const type &src, return_value_policy policy, handle parent) {
dict d;
for (auto const &kv: src) {
object key = object(key_conv::cast(kv.first, policy, parent), false);
object value = object(value_conv::cast(kv.second, policy, parent), false);
auto key = reinterpret_steal<object>(key_conv::cast(kv.first, policy, parent));
auto value = reinterpret_steal<object>(value_conv::cast(kv.second, policy, parent));
if (!key || !value)
return handle();
d[key] = value;
......@@ -114,7 +114,7 @@ template <typename Type, typename Value> struct list_caster {
bool load(handle src, bool convert) {
if (!isinstance<sequence>(src))
return false;
sequence s(src, true);
auto s = reinterpret_borrow<sequence>(src);
value_conv conv;
value.clear();
reserve_maybe(s, &value);
......@@ -135,7 +135,7 @@ template <typename Type, typename Value> struct list_caster {
list l(src.size());
size_t index = 0;
for (auto const &value: src) {
object value_ = object(value_conv::cast(value, policy, parent), false);
auto value_ = reinterpret_steal<object>(value_conv::cast(value, policy, parent));
if (!value_)
return handle();
PyList_SET_ITEM(l.ptr(), index++, value_.release().ptr()); // steals a reference
......@@ -159,7 +159,7 @@ template <typename Type, size_t Size> struct type_caster<std::array<Type, Size>>
bool load(handle src, bool convert) {
if (!isinstance<list>(src))
return false;
list l(src, true);
auto l = reinterpret_borrow<list>(src);
if (l.size() != Size)
return false;
value_conv conv;
......@@ -176,7 +176,7 @@ template <typename Type, size_t Size> struct type_caster<std::array<Type, Size>>
list l(Size);
size_t index = 0;
for (auto const &value: src) {
object value_ = object(value_conv::cast(value, policy, parent), false);
auto value_ = reinterpret_steal<object>(value_conv::cast(value, policy, parent));
if (!value_)
return handle();
PyList_SET_ITEM(l.ptr(), index++, value_.release().ptr()); // steals a reference
......
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