Seamless operability between C++11 and Python
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

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).