mirror of
https://github.com/pybind/pybind11.git
synced 2026-05-14 02:03:34 +00:00
Handle result from PyObject_VisitManagedDict (#6032)
* Handle result from PyObject_VisitManagedDict * add unit test * style: pre-commit fixes * use different variable name This avoids a warning on msvc about Py_Visit shadowing the vret variable. * skip test_get_referrers on unsupported runtimes The managed-dict referrer check is only known to work on CPython 3.13.13+ and 3.14.4+, while earlier releases and non-CPython interpreters can report different traversal behavior. Made-with: Cursor --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Ralf W. Grosse-Kunstleve <rgrossekunst@nvidia.com>
This commit is contained in:
@@ -578,7 +578,10 @@ inline PyObject *make_object_base_type(PyTypeObject *metaclass) {
|
|||||||
/// dynamic_attr: Allow the garbage collector to traverse the internal instance `__dict__`.
|
/// dynamic_attr: Allow the garbage collector to traverse the internal instance `__dict__`.
|
||||||
extern "C" inline int pybind11_traverse(PyObject *self, visitproc visit, void *arg) {
|
extern "C" inline int pybind11_traverse(PyObject *self, visitproc visit, void *arg) {
|
||||||
#if PY_VERSION_HEX >= 0x030D0000
|
#if PY_VERSION_HEX >= 0x030D0000
|
||||||
PyObject_VisitManagedDict(self, visit, arg);
|
int ret = PyObject_VisitManagedDict(self, visit, arg);
|
||||||
|
if (ret) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
PyObject *&dict = *_PyObject_GetDictPtr(self);
|
PyObject *&dict = *_PyObject_GetDictPtr(self);
|
||||||
Py_VISIT(dict);
|
Py_VISIT(dict);
|
||||||
|
|||||||
@@ -104,6 +104,10 @@ TEST_SUBMODULE(class_, m) {
|
|||||||
~NoConstructorNew() { print_destroyed(this); }
|
~NoConstructorNew() { print_destroyed(this); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct DynamicAttr {
|
||||||
|
DynamicAttr() = default;
|
||||||
|
};
|
||||||
|
|
||||||
py::class_<NoConstructor>(m, "NoConstructor")
|
py::class_<NoConstructor>(m, "NoConstructor")
|
||||||
.def_static("new_instance", &NoConstructor::new_instance, "Return an instance");
|
.def_static("new_instance", &NoConstructor::new_instance, "Return an instance");
|
||||||
|
|
||||||
@@ -112,6 +116,8 @@ TEST_SUBMODULE(class_, m) {
|
|||||||
.def_static("__new__",
|
.def_static("__new__",
|
||||||
[](const py::object &) { return NoConstructorNew::new_instance(); });
|
[](const py::object &) { return NoConstructorNew::new_instance(); });
|
||||||
|
|
||||||
|
py::class_<DynamicAttr>(m, "DynamicAttr", py::dynamic_attr()).def(py::init<>());
|
||||||
|
|
||||||
// test_pass_unique_ptr
|
// test_pass_unique_ptr
|
||||||
struct ToBeHeldByUniquePtr {};
|
struct ToBeHeldByUniquePtr {};
|
||||||
py::class_<ToBeHeldByUniquePtr, std::unique_ptr<ToBeHeldByUniquePtr>>(m, "ToBeHeldByUniquePtr")
|
py::class_<ToBeHeldByUniquePtr, std::unique_ptr<ToBeHeldByUniquePtr>>(m, "ToBeHeldByUniquePtr")
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import gc
|
||||||
import sys
|
import sys
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
@@ -18,6 +19,13 @@ def refcount_immortal(ob: object) -> int:
|
|||||||
return sys.getrefcount(ob)
|
return sys.getrefcount(ob)
|
||||||
|
|
||||||
|
|
||||||
|
MANAGED_DICT_GET_REFERRERS_SUPPORTED = (
|
||||||
|
env.CPYTHON
|
||||||
|
and sys.version_info >= (3, 13, 13)
|
||||||
|
and (sys.version_info < (3, 14) or sys.version_info >= (3, 14, 4))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_obj_class_name():
|
def test_obj_class_name():
|
||||||
expected_name = "UserType" if env.PYPY else "pybind11_tests.UserType"
|
expected_name = "UserType" if env.PYPY else "pybind11_tests.UserType"
|
||||||
assert m.obj_class_name(UserType(1)) == expected_name
|
assert m.obj_class_name(UserType(1)) == expected_name
|
||||||
@@ -45,6 +53,16 @@ def test_instance(msg):
|
|||||||
assert cstats.alive() == 0
|
assert cstats.alive() == 0
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(
|
||||||
|
not MANAGED_DICT_GET_REFERRERS_SUPPORTED,
|
||||||
|
reason="Requires CPython 3.13.13+ or 3.14.4+ managed dict traversal support",
|
||||||
|
)
|
||||||
|
def test_get_referrers():
|
||||||
|
instance = m.DynamicAttr()
|
||||||
|
instance.a = "test"
|
||||||
|
assert instance in gc.get_referrers(instance.__dict__)
|
||||||
|
|
||||||
|
|
||||||
def test_instance_new():
|
def test_instance_new():
|
||||||
instance = m.NoConstructorNew() # .__new__(m.NoConstructor.__class__)
|
instance = m.NoConstructorNew() # .__new__(m.NoConstructor.__class__)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user