mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-21 20:55:11 +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
|
||||
|
||||
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
|
||||
=====================================
|
||||
|
@ -235,7 +235,7 @@ handle eigen_ref_array(Type &src, handle parent = none()) {
|
||||
// not the Type of the pointer given is const.
|
||||
template <typename props, typename Type, typename = enable_if_t<is_eigen_dense_plain<Type>::value>>
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -294,8 +294,8 @@ protected:
|
||||
rec->def->ml_meth = reinterpret_cast<PyCFunction>(*dispatcher);
|
||||
rec->def->ml_flags = METH_VARARGS | METH_KEYWORDS;
|
||||
|
||||
capsule rec_capsule(rec, [](PyObject *o) {
|
||||
destruct((detail::function_record *) PyCapsule_GetPointer(o, nullptr));
|
||||
capsule rec_capsule(rec, [](void *ptr) {
|
||||
destruct((detail::function_record *) ptr);
|
||||
});
|
||||
|
||||
object scope_module;
|
||||
|
@ -1004,10 +1004,44 @@ public:
|
||||
PYBIND11_OBJECT_DEFAULT(capsule, object, PyCapsule_CheckExact)
|
||||
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)) { }
|
||||
explicit capsule(const void *value, void (*destruct)(PyObject *) = nullptr)
|
||||
: object(PyCapsule_New(const_cast<void*>(value), nullptr, destruct), stolen) {
|
||||
if (!m_ptr) pybind11_fail("Could not allocate capsule object!");
|
||||
|
||||
explicit capsule(const void *value)
|
||||
: 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 {
|
||||
T * result = static_cast<T *>(PyCapsule_GetPointer(m_ptr, nullptr));
|
||||
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_int", []() -> int * { 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)
|
||||
|
@ -512,3 +512,24 @@ def test_builtins_cast_return_none():
|
||||
assert m.return_none_bool() is None
|
||||
assert m.return_none_int() 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