250 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			Python
		
	
	
		
		
			
		
	
	
			250 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			Python
		
	
	
|  | from __future__ import annotations | ||
|  | 
 | ||
|  | import pytest | ||
|  | 
 | ||
|  | import env  # noqa: F401 | ||
|  | from pybind11_tests import ConstructorStats | ||
|  | from pybind11_tests import call_policies as m | ||
|  | 
 | ||
|  | 
 | ||
|  | @pytest.mark.xfail("env.PYPY", reason="sometimes comes out 1 off on PyPy", strict=False) | ||
|  | def test_keep_alive_argument(capture): | ||
|  |     n_inst = ConstructorStats.detail_reg_inst() | ||
|  |     with capture: | ||
|  |         p = m.Parent() | ||
|  |     assert capture == "Allocating parent." | ||
|  |     with capture: | ||
|  |         p.addChild(m.Child()) | ||
|  |         assert ConstructorStats.detail_reg_inst() == n_inst + 1 | ||
|  |     assert ( | ||
|  |         capture | ||
|  |         == """
 | ||
|  |         Allocating child. | ||
|  |         Releasing child. | ||
|  |     """
 | ||
|  |     ) | ||
|  |     with capture: | ||
|  |         del p | ||
|  |         assert ConstructorStats.detail_reg_inst() == n_inst | ||
|  |     assert capture == "Releasing parent." | ||
|  | 
 | ||
|  |     with capture: | ||
|  |         p = m.Parent() | ||
|  |     assert capture == "Allocating parent." | ||
|  |     with capture: | ||
|  |         p.addChildKeepAlive(m.Child()) | ||
|  |         assert ConstructorStats.detail_reg_inst() == n_inst + 2 | ||
|  |     assert capture == "Allocating child." | ||
|  |     with capture: | ||
|  |         del p | ||
|  |         assert ConstructorStats.detail_reg_inst() == n_inst | ||
|  |     assert ( | ||
|  |         capture | ||
|  |         == """
 | ||
|  |         Releasing parent. | ||
|  |         Releasing child. | ||
|  |     """
 | ||
|  |     ) | ||
|  | 
 | ||
|  |     p = m.Parent() | ||
|  |     c = m.Child() | ||
|  |     assert ConstructorStats.detail_reg_inst() == n_inst + 2 | ||
|  |     m.free_function(p, c) | ||
|  |     del c | ||
|  |     assert ConstructorStats.detail_reg_inst() == n_inst + 2 | ||
|  |     del p | ||
|  |     assert ConstructorStats.detail_reg_inst() == n_inst | ||
|  | 
 | ||
|  |     with pytest.raises(RuntimeError) as excinfo: | ||
|  |         m.invalid_arg_index() | ||
|  |     assert str(excinfo.value) == "Could not activate keep_alive!" | ||
|  | 
 | ||
|  | 
 | ||
|  | def test_keep_alive_return_value(capture): | ||
|  |     n_inst = ConstructorStats.detail_reg_inst() | ||
|  |     with capture: | ||
|  |         p = m.Parent() | ||
|  |     assert capture == "Allocating parent." | ||
|  |     with capture: | ||
|  |         p.returnChild() | ||
|  |         assert ConstructorStats.detail_reg_inst() == n_inst + 1 | ||
|  |     assert ( | ||
|  |         capture | ||
|  |         == """
 | ||
|  |         Allocating child. | ||
|  |         Releasing child. | ||
|  |     """
 | ||
|  |     ) | ||
|  |     with capture: | ||
|  |         del p | ||
|  |         assert ConstructorStats.detail_reg_inst() == n_inst | ||
|  |     assert capture == "Releasing parent." | ||
|  | 
 | ||
|  |     with capture: | ||
|  |         p = m.Parent() | ||
|  |     assert capture == "Allocating parent." | ||
|  |     with capture: | ||
|  |         p.returnChildKeepAlive() | ||
|  |         assert ConstructorStats.detail_reg_inst() == n_inst + 2 | ||
|  |     assert capture == "Allocating child." | ||
|  |     with capture: | ||
|  |         del p | ||
|  |         assert ConstructorStats.detail_reg_inst() == n_inst | ||
|  |     assert ( | ||
|  |         capture | ||
|  |         == """
 | ||
|  |         Releasing parent. | ||
|  |         Releasing child. | ||
|  |     """
 | ||
|  |     ) | ||
|  | 
 | ||
|  |     p = m.Parent() | ||
|  |     assert ConstructorStats.detail_reg_inst() == n_inst + 1 | ||
|  |     with capture: | ||
|  |         m.Parent.staticFunction(p) | ||
|  |         assert ConstructorStats.detail_reg_inst() == n_inst + 2 | ||
|  |     assert capture == "Allocating child." | ||
|  |     with capture: | ||
|  |         del p | ||
|  |         assert ConstructorStats.detail_reg_inst() == n_inst | ||
|  |     assert ( | ||
|  |         capture | ||
|  |         == """
 | ||
|  |         Releasing parent. | ||
|  |         Releasing child. | ||
|  |     """
 | ||
|  |     ) | ||
|  | 
 | ||
|  | 
 | ||
|  | # https://foss.heptapod.net/pypy/pypy/-/issues/2447 | ||
|  | @pytest.mark.xfail("env.PYPY", reason="_PyObject_GetDictPtr is unimplemented") | ||
|  | def test_alive_gc(capture): | ||
|  |     n_inst = ConstructorStats.detail_reg_inst() | ||
|  |     p = m.ParentGC() | ||
|  |     p.addChildKeepAlive(m.Child()) | ||
|  |     assert ConstructorStats.detail_reg_inst() == n_inst + 2 | ||
|  |     lst = [p] | ||
|  |     lst.append(lst)  # creates a circular reference | ||
|  |     with capture: | ||
|  |         del p, lst | ||
|  |         assert ConstructorStats.detail_reg_inst() == n_inst | ||
|  |     assert ( | ||
|  |         capture | ||
|  |         == """
 | ||
|  |         Releasing parent. | ||
|  |         Releasing child. | ||
|  |     """
 | ||
|  |     ) | ||
|  | 
 | ||
|  | 
 | ||
|  | def test_alive_gc_derived(capture): | ||
|  |     class Derived(m.Parent): | ||
|  |         pass | ||
|  | 
 | ||
|  |     n_inst = ConstructorStats.detail_reg_inst() | ||
|  |     p = Derived() | ||
|  |     p.addChildKeepAlive(m.Child()) | ||
|  |     assert ConstructorStats.detail_reg_inst() == n_inst + 2 | ||
|  |     lst = [p] | ||
|  |     lst.append(lst)  # creates a circular reference | ||
|  |     with capture: | ||
|  |         del p, lst | ||
|  |         assert ConstructorStats.detail_reg_inst() == n_inst | ||
|  |     assert ( | ||
|  |         capture | ||
|  |         == """
 | ||
|  |         Releasing parent. | ||
|  |         Releasing child. | ||
|  |     """
 | ||
|  |     ) | ||
|  | 
 | ||
|  | 
 | ||
|  | def test_alive_gc_multi_derived(capture): | ||
|  |     class Derived(m.Parent, m.Child): | ||
|  |         def __init__(self): | ||
|  |             m.Parent.__init__(self) | ||
|  |             m.Child.__init__(self) | ||
|  | 
 | ||
|  |     n_inst = ConstructorStats.detail_reg_inst() | ||
|  |     p = Derived() | ||
|  |     p.addChildKeepAlive(m.Child()) | ||
|  |     # +3 rather than +2 because Derived corresponds to two registered instances | ||
|  |     assert ConstructorStats.detail_reg_inst() == n_inst + 3 | ||
|  |     lst = [p] | ||
|  |     lst.append(lst)  # creates a circular reference | ||
|  |     with capture: | ||
|  |         del p, lst | ||
|  |         assert ConstructorStats.detail_reg_inst() == n_inst | ||
|  |     assert ( | ||
|  |         capture | ||
|  |         == """
 | ||
|  |         Releasing parent. | ||
|  |         Releasing child. | ||
|  |         Releasing child. | ||
|  |     """
 | ||
|  |     ) | ||
|  | 
 | ||
|  | 
 | ||
|  | def test_return_none(capture): | ||
|  |     n_inst = ConstructorStats.detail_reg_inst() | ||
|  |     with capture: | ||
|  |         p = m.Parent() | ||
|  |     assert capture == "Allocating parent." | ||
|  |     with capture: | ||
|  |         p.returnNullChildKeepAliveChild() | ||
|  |         assert ConstructorStats.detail_reg_inst() == n_inst + 1 | ||
|  |     assert capture == "" | ||
|  |     with capture: | ||
|  |         del p | ||
|  |         assert ConstructorStats.detail_reg_inst() == n_inst | ||
|  |     assert capture == "Releasing parent." | ||
|  | 
 | ||
|  |     with capture: | ||
|  |         p = m.Parent() | ||
|  |     assert capture == "Allocating parent." | ||
|  |     with capture: | ||
|  |         p.returnNullChildKeepAliveParent() | ||
|  |         assert ConstructorStats.detail_reg_inst() == n_inst + 1 | ||
|  |     assert capture == "" | ||
|  |     with capture: | ||
|  |         del p | ||
|  |         assert ConstructorStats.detail_reg_inst() == n_inst | ||
|  |     assert capture == "Releasing parent." | ||
|  | 
 | ||
|  | 
 | ||
|  | def test_keep_alive_constructor(capture): | ||
|  |     n_inst = ConstructorStats.detail_reg_inst() | ||
|  | 
 | ||
|  |     with capture: | ||
|  |         p = m.Parent(m.Child()) | ||
|  |         assert ConstructorStats.detail_reg_inst() == n_inst + 2 | ||
|  |     assert ( | ||
|  |         capture | ||
|  |         == """
 | ||
|  |         Allocating child. | ||
|  |         Allocating parent. | ||
|  |     """
 | ||
|  |     ) | ||
|  |     with capture: | ||
|  |         del p | ||
|  |         assert ConstructorStats.detail_reg_inst() == n_inst | ||
|  |     assert ( | ||
|  |         capture | ||
|  |         == """
 | ||
|  |         Releasing parent. | ||
|  |         Releasing child. | ||
|  |     """
 | ||
|  |     ) | ||
|  | 
 | ||
|  | 
 | ||
|  | def test_call_guard(): | ||
|  |     assert m.unguarded_call() == "unguarded" | ||
|  |     assert m.guarded_call() == "guarded" | ||
|  | 
 | ||
|  |     assert m.multiple_guards_correct_order() == "guarded & guarded" | ||
|  |     assert m.multiple_guards_wrong_order() == "unguarded & guarded" | ||
|  | 
 | ||
|  |     if hasattr(m, "with_gil"): | ||
|  |         assert m.with_gil() == "GIL held" | ||
|  |         assert m.without_gil() == "GIL released" |