import pytest import gc def test_alias_delay_initialization(capture, msg): # A only initializes its trampoline class when we inherit from it; if we # just create and use an A instance directly, the trampoline initialization # is bypassed and we only initialize an A() instead (for performance # reasons) from pybind11_tests import A, call_f class B(A): def __init__(self): super(B, self).__init__() def f(self): print("In python f()") # C++ version with capture: a = A() call_f(a) del a gc.collect() assert capture == "A.f()" # Python version with capture: b = B() call_f(b) del b gc.collect() assert capture == """ PyA.PyA() PyA.f() In python f() PyA.~PyA() """ def test_alias_delay_initialization(capture, msg): from pybind11_tests import A2, call_f # A2, unlike the above, is configured to always initialize the alias; while # the extra initialization and extra class layer has small virtual dispatch # performance penalty, it also allows us to do more things with the # trampoline class such as defining local variables and performing # construction/destruction. class B2(A2): def __init__(self): super(B2, self).__init__() def f(self): print("In python B2.f()") # No python subclass version with capture: a2 = A2() call_f(a2) del a2 gc.collect() assert capture == """ PyA2.PyA2() PyA2.f() A2.f() PyA2.~PyA2() """ # Python subclass version with capture: b2 = B2() call_f(b2) del b2 gc.collect() assert capture == """ PyA2.PyA2() PyA2.f() In python B2.f() PyA2.~PyA2() """