mirror of https://github.com/pybind/pybind11
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
154 lines
4.1 KiB
154 lines
4.1 KiB
from __future__ import annotations |
|
|
|
import pytest |
|
|
|
import env # noqa: F401 |
|
import pybind11_tests.class_sh_trampoline_shared_ptr_cpp_arg as m |
|
|
|
|
|
@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") |
|
def test_shared_ptr_cpp_arg(): |
|
import weakref |
|
|
|
class PyChild(m.SpBase): |
|
def is_base_used(self): |
|
return False |
|
|
|
tester = m.SpBaseTester() |
|
|
|
obj = PyChild() |
|
objref = weakref.ref(obj) |
|
|
|
# Pass the last python reference to the C++ function |
|
tester.set_object(obj) |
|
del obj |
|
pytest.gc_collect() |
|
|
|
# python reference is still around since C++ has it now |
|
assert objref() is not None |
|
assert tester.is_base_used() is False |
|
assert tester.obj.is_base_used() is False |
|
assert tester.get_object() is objref() |
|
|
|
|
|
@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") |
|
def test_shared_ptr_cpp_prop(): |
|
class PyChild(m.SpBase): |
|
def is_base_used(self): |
|
return False |
|
|
|
tester = m.SpBaseTester() |
|
|
|
# Set the last python reference as a property of the C++ object |
|
tester.obj = PyChild() |
|
pytest.gc_collect() |
|
|
|
# python reference is still around since C++ has it now |
|
assert tester.is_base_used() is False |
|
assert tester.has_python_instance() is True |
|
assert tester.obj.is_base_used() is False |
|
assert tester.obj.has_python_instance() is True |
|
|
|
|
|
@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") |
|
def test_shared_ptr_arg_identity(): |
|
import weakref |
|
|
|
tester = m.SpBaseTester() |
|
|
|
obj = m.SpBase() |
|
objref = weakref.ref(obj) |
|
|
|
tester.set_object(obj) |
|
del obj |
|
pytest.gc_collect() |
|
|
|
# NOTE: the behavior below is DIFFERENT from PR #2839 |
|
# python reference is gone because it is not an Alias instance |
|
assert objref() is None |
|
assert tester.has_python_instance() is False |
|
|
|
|
|
@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") |
|
def test_shared_ptr_alias_nonpython(): |
|
tester = m.SpBaseTester() |
|
|
|
# C++ creates the object, a python instance shouldn't exist |
|
tester.set_nonpython_instance() |
|
assert tester.is_base_used() is True |
|
assert tester.has_instance() is True |
|
assert tester.has_python_instance() is False |
|
|
|
# Now a python instance exists |
|
cobj = tester.get_object() |
|
assert cobj.has_python_instance() |
|
assert tester.has_instance() is True |
|
assert tester.has_python_instance() is True |
|
|
|
# Now it's gone |
|
del cobj |
|
pytest.gc_collect() |
|
assert tester.has_instance() is True |
|
assert tester.has_python_instance() is False |
|
|
|
# When we pass it as an arg to a new tester the python instance should |
|
# disappear because it wasn't created with an alias |
|
new_tester = m.SpBaseTester() |
|
|
|
cobj = tester.get_object() |
|
assert cobj.has_python_instance() |
|
|
|
new_tester.set_object(cobj) |
|
assert tester.has_python_instance() is True |
|
assert new_tester.has_python_instance() is True |
|
|
|
del cobj |
|
pytest.gc_collect() |
|
|
|
# Gone! |
|
assert tester.has_instance() is True |
|
assert tester.has_python_instance() is False |
|
assert new_tester.has_instance() is True |
|
assert new_tester.has_python_instance() is False |
|
|
|
|
|
@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") |
|
def test_shared_ptr_goaway(): |
|
import weakref |
|
|
|
tester = m.SpGoAwayTester() |
|
|
|
obj = m.SpGoAway() |
|
objref = weakref.ref(obj) |
|
|
|
assert tester.obj is None |
|
|
|
tester.obj = obj |
|
del obj |
|
pytest.gc_collect() |
|
|
|
# python reference is no longer around |
|
assert objref() is None |
|
# C++ reference is still around |
|
assert tester.obj is not None |
|
|
|
|
|
def test_infinite(): |
|
tester = m.SpBaseTester() |
|
while True: |
|
tester.set_object(m.SpBase()) |
|
break # Comment out for manual leak checking (use `top` command). |
|
|
|
|
|
@pytest.mark.parametrize( |
|
"pass_through_func", [m.pass_through_shd_ptr, m.pass_through_shd_ptr_release_gil] |
|
) |
|
def test_std_make_shared_factory(pass_through_func): |
|
class PyChild(m.SpBase): |
|
def __init__(self): |
|
super().__init__(0) |
|
|
|
obj = PyChild() |
|
while True: |
|
assert pass_through_func(obj) is obj |
|
break # Comment out for manual leak checking (use `top` command).
|
|
|