mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-22 13:15:12 +00:00
Nicer API to pass py::capsule destructor (#752)
* nicer py::capsule destructor mechanism * added destructor-only version of capsule & tests * added documentation for module destructors (fixes #733)
This commit is contained in:
parent
ab26259c87
commit
b16421edb1
@ -170,6 +170,20 @@ would be then able to access the data behind the same pointer.
|
|||||||
|
|
||||||
.. [#f6] https://docs.python.org/3/extending/extending.html#using-capsules
|
.. [#f6] https://docs.python.org/3/extending/extending.html#using-capsules
|
||||||
|
|
||||||
|
Module Destructors
|
||||||
|
==================
|
||||||
|
|
||||||
|
pybind11 does not provide an explicit mechanism to invoke cleanup code at
|
||||||
|
module destruction time. In rare cases where such functionality is required, it
|
||||||
|
is possible to emulate it using Python capsules with a destruction callback.
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
auto cleanup_callback = []() {
|
||||||
|
// perform cleanup here -- this function is called with the GIL held
|
||||||
|
};
|
||||||
|
|
||||||
|
m.add_object("_cleanup", py::capsule(cleanup_callback));
|
||||||
|
|
||||||
Generating documentation using Sphinx
|
Generating documentation using Sphinx
|
||||||
=====================================
|
=====================================
|
||||||
|
@ -235,7 +235,7 @@ handle eigen_ref_array(Type &src, handle parent = none()) {
|
|||||||
// not the Type of the pointer given is const.
|
// not the Type of the pointer given is const.
|
||||||
template <typename props, typename Type, typename = enable_if_t<is_eigen_dense_plain<Type>::value>>
|
template <typename props, typename Type, typename = enable_if_t<is_eigen_dense_plain<Type>::value>>
|
||||||
handle eigen_encapsulate(Type *src) {
|
handle eigen_encapsulate(Type *src) {
|
||||||
capsule base(src, [](PyObject *o) { delete static_cast<Type *>(PyCapsule_GetPointer(o, nullptr)); });
|
capsule base(src, [](void *o) { delete static_cast<Type *>(o); });
|
||||||
return eigen_ref_array<props>(*src, base);
|
return eigen_ref_array<props>(*src, base);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -294,8 +294,8 @@ protected:
|
|||||||
rec->def->ml_meth = reinterpret_cast<PyCFunction>(*dispatcher);
|
rec->def->ml_meth = reinterpret_cast<PyCFunction>(*dispatcher);
|
||||||
rec->def->ml_flags = METH_VARARGS | METH_KEYWORDS;
|
rec->def->ml_flags = METH_VARARGS | METH_KEYWORDS;
|
||||||
|
|
||||||
capsule rec_capsule(rec, [](PyObject *o) {
|
capsule rec_capsule(rec, [](void *ptr) {
|
||||||
destruct((detail::function_record *) PyCapsule_GetPointer(o, nullptr));
|
destruct((detail::function_record *) ptr);
|
||||||
});
|
});
|
||||||
|
|
||||||
object scope_module;
|
object scope_module;
|
||||||
|
@ -1004,10 +1004,44 @@ public:
|
|||||||
PYBIND11_OBJECT_DEFAULT(capsule, object, PyCapsule_CheckExact)
|
PYBIND11_OBJECT_DEFAULT(capsule, object, PyCapsule_CheckExact)
|
||||||
PYBIND11_DEPRECATED("Use reinterpret_borrow<capsule>() or reinterpret_steal<capsule>()")
|
PYBIND11_DEPRECATED("Use reinterpret_borrow<capsule>() or reinterpret_steal<capsule>()")
|
||||||
capsule(PyObject *ptr, bool is_borrowed) : object(is_borrowed ? object(ptr, borrowed) : object(ptr, stolen)) { }
|
capsule(PyObject *ptr, bool is_borrowed) : object(is_borrowed ? object(ptr, borrowed) : object(ptr, stolen)) { }
|
||||||
explicit capsule(const void *value, void (*destruct)(PyObject *) = nullptr)
|
|
||||||
: object(PyCapsule_New(const_cast<void*>(value), nullptr, destruct), stolen) {
|
explicit capsule(const void *value)
|
||||||
if (!m_ptr) pybind11_fail("Could not allocate capsule object!");
|
: object(PyCapsule_New(const_cast<void *>(value), nullptr, nullptr), stolen) {
|
||||||
|
if (!m_ptr)
|
||||||
|
pybind11_fail("Could not allocate capsule object!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PYBIND11_DEPRECATED("Please pass a destructor that takes a void pointer as input")
|
||||||
|
capsule(const void *value, void (*destruct)(PyObject *))
|
||||||
|
: object(PyCapsule_New(const_cast<void*>(value), nullptr, destruct), stolen) {
|
||||||
|
if (!m_ptr)
|
||||||
|
pybind11_fail("Could not allocate capsule object!");
|
||||||
|
}
|
||||||
|
|
||||||
|
capsule(const void *value, void (*destructor)(void *)) {
|
||||||
|
m_ptr = PyCapsule_New(const_cast<void *>(value), nullptr, [](PyObject *o) {
|
||||||
|
auto destructor = reinterpret_cast<void (*)(void *)>(PyCapsule_GetContext(o));
|
||||||
|
void *ptr = PyCapsule_GetPointer(o, nullptr);
|
||||||
|
destructor(ptr);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!m_ptr)
|
||||||
|
pybind11_fail("Could not allocate capsule object!");
|
||||||
|
|
||||||
|
if (PyCapsule_SetContext(m_ptr, (void *) destructor) != 0)
|
||||||
|
pybind11_fail("Could not set capsule context!");
|
||||||
|
}
|
||||||
|
|
||||||
|
capsule(void (*destructor)()) {
|
||||||
|
m_ptr = PyCapsule_New(reinterpret_cast<void *>(destructor), nullptr, [](PyObject *o) {
|
||||||
|
auto destructor = reinterpret_cast<void (*)()>(PyCapsule_GetPointer(o, nullptr));
|
||||||
|
destructor();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!m_ptr)
|
||||||
|
pybind11_fail("Could not allocate capsule object!");
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T> operator T *() const {
|
template <typename T> operator T *() const {
|
||||||
T * result = static_cast<T *>(PyCapsule_GetPointer(m_ptr, nullptr));
|
T * result = static_cast<T *>(PyCapsule_GetPointer(m_ptr, nullptr));
|
||||||
if (!result) pybind11_fail("Unable to extract capsule contents!");
|
if (!result) pybind11_fail("Unable to extract capsule contents!");
|
||||||
|
@ -470,6 +470,24 @@ test_initializer python_types([](py::module &m) {
|
|||||||
m.def("return_none_bool", []() -> bool * { return nullptr; });
|
m.def("return_none_bool", []() -> bool * { return nullptr; });
|
||||||
m.def("return_none_int", []() -> int * { return nullptr; });
|
m.def("return_none_int", []() -> int * { return nullptr; });
|
||||||
m.def("return_none_float", []() -> float * { return nullptr; });
|
m.def("return_none_float", []() -> float * { return nullptr; });
|
||||||
|
|
||||||
|
m.def("return_capsule_with_destructor",
|
||||||
|
[]() {
|
||||||
|
py::print("creating capsule");
|
||||||
|
return py::capsule([]() {
|
||||||
|
py::print("destructing capsule");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
m.def("return_capsule_with_destructor_2",
|
||||||
|
[]() {
|
||||||
|
py::print("creating capsule");
|
||||||
|
return py::capsule((void *) 1234, [](void *ptr) {
|
||||||
|
py::print("destructing capsule: {}"_s.format((size_t) ptr));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
#if defined(_MSC_VER)
|
#if defined(_MSC_VER)
|
||||||
|
@ -512,3 +512,24 @@ def test_builtins_cast_return_none():
|
|||||||
assert m.return_none_bool() is None
|
assert m.return_none_bool() is None
|
||||||
assert m.return_none_int() is None
|
assert m.return_none_int() is None
|
||||||
assert m.return_none_float() is None
|
assert m.return_none_float() is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_capsule_with_destructor(capture):
|
||||||
|
import pybind11_tests as m
|
||||||
|
with capture:
|
||||||
|
a = m.return_capsule_with_destructor()
|
||||||
|
del a
|
||||||
|
pytest.gc_collect()
|
||||||
|
assert capture.unordered == """
|
||||||
|
creating capsule
|
||||||
|
destructing capsule
|
||||||
|
"""
|
||||||
|
|
||||||
|
with capture:
|
||||||
|
a = m.return_capsule_with_destructor_2()
|
||||||
|
del a
|
||||||
|
pytest.gc_collect()
|
||||||
|
assert capture.unordered == """
|
||||||
|
creating capsule
|
||||||
|
destructing capsule: 1234
|
||||||
|
"""
|
||||||
|
Loading…
Reference in New Issue
Block a user