Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 10:17:58

0001 import pytest
0002 
0003 import env  # noqa: F401
0004 
0005 m = pytest.importorskip("pybind11_tests.virtual_functions")
0006 from pybind11_tests import ConstructorStats  # noqa: E402
0007 
0008 
0009 def test_override(capture, msg):
0010     class ExtendedExampleVirt(m.ExampleVirt):
0011         def __init__(self, state):
0012             super().__init__(state + 1)
0013             self.data = "Hello world"
0014 
0015         def run(self, value):
0016             print(f"ExtendedExampleVirt::run({value}), calling parent..")
0017             return super().run(value + 1)
0018 
0019         def run_bool(self):
0020             print("ExtendedExampleVirt::run_bool()")
0021             return False
0022 
0023         def get_string1(self):
0024             return "override1"
0025 
0026         def pure_virtual(self):
0027             print(f"ExtendedExampleVirt::pure_virtual(): {self.data}")
0028 
0029     class ExtendedExampleVirt2(ExtendedExampleVirt):
0030         def __init__(self, state):
0031             super().__init__(state + 1)
0032 
0033         def get_string2(self):
0034             return "override2"
0035 
0036     ex12 = m.ExampleVirt(10)
0037     with capture:
0038         assert m.runExampleVirt(ex12, 20) == 30
0039     assert (
0040         capture
0041         == """
0042         Original implementation of ExampleVirt::run(state=10, value=20, str1=default1, str2=default2)
0043     """
0044     )
0045 
0046     with pytest.raises(RuntimeError) as excinfo:
0047         m.runExampleVirtVirtual(ex12)
0048     assert (
0049         msg(excinfo.value)
0050         == 'Tried to call pure virtual function "ExampleVirt::pure_virtual"'
0051     )
0052 
0053     ex12p = ExtendedExampleVirt(10)
0054     with capture:
0055         assert m.runExampleVirt(ex12p, 20) == 32
0056     assert (
0057         capture
0058         == """
0059         ExtendedExampleVirt::run(20), calling parent..
0060         Original implementation of ExampleVirt::run(state=11, value=21, str1=override1, str2=default2)
0061     """
0062     )
0063     with capture:
0064         assert m.runExampleVirtBool(ex12p) is False
0065     assert capture == "ExtendedExampleVirt::run_bool()"
0066     with capture:
0067         m.runExampleVirtVirtual(ex12p)
0068     assert capture == "ExtendedExampleVirt::pure_virtual(): Hello world"
0069 
0070     ex12p2 = ExtendedExampleVirt2(15)
0071     with capture:
0072         assert m.runExampleVirt(ex12p2, 50) == 68
0073     assert (
0074         capture
0075         == """
0076         ExtendedExampleVirt::run(50), calling parent..
0077         Original implementation of ExampleVirt::run(state=17, value=51, str1=override1, str2=override2)
0078     """
0079     )
0080 
0081     cstats = ConstructorStats.get(m.ExampleVirt)
0082     assert cstats.alive() == 3
0083     del ex12, ex12p, ex12p2
0084     assert cstats.alive() == 0
0085     assert cstats.values() == ["10", "11", "17"]
0086     assert cstats.copy_constructions == 0
0087     assert cstats.move_constructions >= 0
0088 
0089 
0090 def test_alias_delay_initialization1(capture):
0091     """`A` only initializes its trampoline class when we inherit from it
0092 
0093     If we just create and use an A instance directly, the trampoline initialization is
0094     bypassed and we only initialize an A() instead (for performance reasons).
0095     """
0096 
0097     class B(m.A):
0098         def __init__(self):
0099             super().__init__()
0100 
0101         def f(self):
0102             print("In python f()")
0103 
0104     # C++ version
0105     with capture:
0106         a = m.A()
0107         m.call_f(a)
0108         del a
0109         pytest.gc_collect()
0110     assert capture == "A.f()"
0111 
0112     # Python version
0113     with capture:
0114         b = B()
0115         m.call_f(b)
0116         del b
0117         pytest.gc_collect()
0118     assert (
0119         capture
0120         == """
0121         PyA.PyA()
0122         PyA.f()
0123         In python f()
0124         PyA.~PyA()
0125     """
0126     )
0127 
0128 
0129 def test_alias_delay_initialization2(capture):
0130     """`A2`, unlike the above, is configured to always initialize the alias
0131 
0132     While the extra initialization and extra class layer has small virtual dispatch
0133     performance penalty, it also allows us to do more things with the trampoline
0134     class such as defining local variables and performing construction/destruction.
0135     """
0136 
0137     class B2(m.A2):
0138         def __init__(self):
0139             super().__init__()
0140 
0141         def f(self):
0142             print("In python B2.f()")
0143 
0144     # No python subclass version
0145     with capture:
0146         a2 = m.A2()
0147         m.call_f(a2)
0148         del a2
0149         pytest.gc_collect()
0150         a3 = m.A2(1)
0151         m.call_f(a3)
0152         del a3
0153         pytest.gc_collect()
0154     assert (
0155         capture
0156         == """
0157         PyA2.PyA2()
0158         PyA2.f()
0159         A2.f()
0160         PyA2.~PyA2()
0161         PyA2.PyA2()
0162         PyA2.f()
0163         A2.f()
0164         PyA2.~PyA2()
0165     """
0166     )
0167 
0168     # Python subclass version
0169     with capture:
0170         b2 = B2()
0171         m.call_f(b2)
0172         del b2
0173         pytest.gc_collect()
0174     assert (
0175         capture
0176         == """
0177         PyA2.PyA2()
0178         PyA2.f()
0179         In python B2.f()
0180         PyA2.~PyA2()
0181     """
0182     )
0183 
0184 
0185 # PyPy: Reference count > 1 causes call with noncopyable instance
0186 # to fail in ncv1.print_nc()
0187 @pytest.mark.xfail("env.PYPY")
0188 @pytest.mark.skipif(
0189     not hasattr(m, "NCVirt"), reason="NCVirt does not work on Intel/PGI/NVCC compilers"
0190 )
0191 def test_move_support():
0192     class NCVirtExt(m.NCVirt):
0193         def get_noncopyable(self, a, b):
0194             # Constructs and returns a new instance:
0195             nc = m.NonCopyable(a * a, b * b)
0196             return nc
0197 
0198         def get_movable(self, a, b):
0199             # Return a referenced copy
0200             self.movable = m.Movable(a, b)
0201             return self.movable
0202 
0203     class NCVirtExt2(m.NCVirt):
0204         def get_noncopyable(self, a, b):
0205             # Keep a reference: this is going to throw an exception
0206             self.nc = m.NonCopyable(a, b)
0207             return self.nc
0208 
0209         def get_movable(self, a, b):
0210             # Return a new instance without storing it
0211             return m.Movable(a, b)
0212 
0213     ncv1 = NCVirtExt()
0214     assert ncv1.print_nc(2, 3) == "36"
0215     assert ncv1.print_movable(4, 5) == "9"
0216     ncv2 = NCVirtExt2()
0217     assert ncv2.print_movable(7, 7) == "14"
0218     # Don't check the exception message here because it differs under debug/non-debug mode
0219     with pytest.raises(RuntimeError):
0220         ncv2.print_nc(9, 9)
0221 
0222     nc_stats = ConstructorStats.get(m.NonCopyable)
0223     mv_stats = ConstructorStats.get(m.Movable)
0224     assert nc_stats.alive() == 1
0225     assert mv_stats.alive() == 1
0226     del ncv1, ncv2
0227     assert nc_stats.alive() == 0
0228     assert mv_stats.alive() == 0
0229     assert nc_stats.values() == ["4", "9", "9", "9"]
0230     assert mv_stats.values() == ["4", "5", "7", "7"]
0231     assert nc_stats.copy_constructions == 0
0232     assert mv_stats.copy_constructions == 1
0233     assert nc_stats.move_constructions >= 0
0234     assert mv_stats.move_constructions >= 0
0235 
0236 
0237 def test_dispatch_issue(msg):
0238     """#159: virtual function dispatch has problems with similar-named functions"""
0239 
0240     class PyClass1(m.DispatchIssue):
0241         def dispatch(self):
0242             return "Yay.."
0243 
0244     class PyClass2(m.DispatchIssue):
0245         def dispatch(self):
0246             with pytest.raises(RuntimeError) as excinfo:
0247                 super().dispatch()
0248             assert (
0249                 msg(excinfo.value)
0250                 == 'Tried to call pure virtual function "Base::dispatch"'
0251             )
0252 
0253             return m.dispatch_issue_go(PyClass1())
0254 
0255     b = PyClass2()
0256     assert m.dispatch_issue_go(b) == "Yay.."
0257 
0258 
0259 def test_recursive_dispatch_issue(msg):
0260     """#3357: Recursive dispatch fails to find python function override"""
0261 
0262     class Data(m.Data):
0263         def __init__(self, value):
0264             super().__init__()
0265             self.value = value
0266 
0267     class Adder(m.Adder):
0268         def __call__(self, first, second, visitor):
0269             # lambda is a workaround, which adds extra frame to the
0270             # current CPython thread. Removing lambda reveals the bug
0271             # [https://github.com/pybind/pybind11/issues/3357]
0272             (lambda: visitor(Data(first.value + second.value)))()
0273 
0274     class StoreResultVisitor:
0275         def __init__(self):
0276             self.result = None
0277 
0278         def __call__(self, data):
0279             self.result = data.value
0280 
0281     store = StoreResultVisitor()
0282 
0283     m.add2(Data(1), Data(2), Adder(), store)
0284     assert store.result == 3
0285 
0286     # without lambda in Adder class, this function fails with
0287     # RuntimeError: Tried to call pure virtual function "AdderBase::__call__"
0288     m.add3(Data(1), Data(2), Data(3), Adder(), store)
0289     assert store.result == 6
0290 
0291 
0292 def test_override_ref():
0293     """#392/397: overriding reference-returning functions"""
0294     o = m.OverrideTest("asdf")
0295 
0296     # Not allowed (see associated .cpp comment)
0297     # i = o.str_ref()
0298     # assert o.str_ref() == "asdf"
0299     assert o.str_value() == "asdf"
0300 
0301     assert o.A_value().value == "hi"
0302     a = o.A_ref()
0303     assert a.value == "hi"
0304     a.value = "bye"
0305     assert a.value == "bye"
0306 
0307 
0308 def test_inherited_virtuals():
0309     class AR(m.A_Repeat):
0310         def unlucky_number(self):
0311             return 99
0312 
0313     class AT(m.A_Tpl):
0314         def unlucky_number(self):
0315             return 999
0316 
0317     obj = AR()
0318     assert obj.say_something(3) == "hihihi"
0319     assert obj.unlucky_number() == 99
0320     assert obj.say_everything() == "hi 99"
0321 
0322     obj = AT()
0323     assert obj.say_something(3) == "hihihi"
0324     assert obj.unlucky_number() == 999
0325     assert obj.say_everything() == "hi 999"
0326 
0327     for obj in [m.B_Repeat(), m.B_Tpl()]:
0328         assert obj.say_something(3) == "B says hi 3 times"
0329         assert obj.unlucky_number() == 13
0330         assert obj.lucky_number() == 7.0
0331         assert obj.say_everything() == "B says hi 1 times 13"
0332 
0333     for obj in [m.C_Repeat(), m.C_Tpl()]:
0334         assert obj.say_something(3) == "B says hi 3 times"
0335         assert obj.unlucky_number() == 4444
0336         assert obj.lucky_number() == 888.0
0337         assert obj.say_everything() == "B says hi 1 times 4444"
0338 
0339     class CR(m.C_Repeat):
0340         def lucky_number(self):
0341             return m.C_Repeat.lucky_number(self) + 1.25
0342 
0343     obj = CR()
0344     assert obj.say_something(3) == "B says hi 3 times"
0345     assert obj.unlucky_number() == 4444
0346     assert obj.lucky_number() == 889.25
0347     assert obj.say_everything() == "B says hi 1 times 4444"
0348 
0349     class CT(m.C_Tpl):
0350         pass
0351 
0352     obj = CT()
0353     assert obj.say_something(3) == "B says hi 3 times"
0354     assert obj.unlucky_number() == 4444
0355     assert obj.lucky_number() == 888.0
0356     assert obj.say_everything() == "B says hi 1 times 4444"
0357 
0358     class CCR(CR):
0359         def lucky_number(self):
0360             return CR.lucky_number(self) * 10
0361 
0362     obj = CCR()
0363     assert obj.say_something(3) == "B says hi 3 times"
0364     assert obj.unlucky_number() == 4444
0365     assert obj.lucky_number() == 8892.5
0366     assert obj.say_everything() == "B says hi 1 times 4444"
0367 
0368     class CCT(CT):
0369         def lucky_number(self):
0370             return CT.lucky_number(self) * 1000
0371 
0372     obj = CCT()
0373     assert obj.say_something(3) == "B says hi 3 times"
0374     assert obj.unlucky_number() == 4444
0375     assert obj.lucky_number() == 888000.0
0376     assert obj.say_everything() == "B says hi 1 times 4444"
0377 
0378     class DR(m.D_Repeat):
0379         def unlucky_number(self):
0380             return 123
0381 
0382         def lucky_number(self):
0383             return 42.0
0384 
0385     for obj in [m.D_Repeat(), m.D_Tpl()]:
0386         assert obj.say_something(3) == "B says hi 3 times"
0387         assert obj.unlucky_number() == 4444
0388         assert obj.lucky_number() == 888.0
0389         assert obj.say_everything() == "B says hi 1 times 4444"
0390 
0391     obj = DR()
0392     assert obj.say_something(3) == "B says hi 3 times"
0393     assert obj.unlucky_number() == 123
0394     assert obj.lucky_number() == 42.0
0395     assert obj.say_everything() == "B says hi 1 times 123"
0396 
0397     class DT(m.D_Tpl):
0398         def say_something(self, times):
0399             return "DT says:" + (" quack" * times)
0400 
0401         def unlucky_number(self):
0402             return 1234
0403 
0404         def lucky_number(self):
0405             return -4.25
0406 
0407     obj = DT()
0408     assert obj.say_something(3) == "DT says: quack quack quack"
0409     assert obj.unlucky_number() == 1234
0410     assert obj.lucky_number() == -4.25
0411     assert obj.say_everything() == "DT says: quack 1234"
0412 
0413     class DT2(DT):
0414         def say_something(self, times):
0415             return "DT2: " + ("QUACK" * times)
0416 
0417         def unlucky_number(self):
0418             return -3
0419 
0420     class BT(m.B_Tpl):
0421         def say_something(self, times):
0422             return "BT" * times
0423 
0424         def unlucky_number(self):
0425             return -7
0426 
0427         def lucky_number(self):
0428             return -1.375
0429 
0430     obj = BT()
0431     assert obj.say_something(3) == "BTBTBT"
0432     assert obj.unlucky_number() == -7
0433     assert obj.lucky_number() == -1.375
0434     assert obj.say_everything() == "BT -7"
0435 
0436 
0437 def test_issue_1454():
0438     # Fix issue #1454 (crash when acquiring/releasing GIL on another thread in Python 2.7)
0439     m.test_gil()
0440     m.test_gil_from_thread()
0441 
0442 
0443 def test_python_override():
0444     def func():
0445         class Test(m.test_override_cache_helper):
0446             def func(self):
0447                 return 42
0448 
0449         return Test()
0450 
0451     def func2():
0452         class Test(m.test_override_cache_helper):
0453             pass
0454 
0455         return Test()
0456 
0457     for _ in range(1500):
0458         assert m.test_override_cache(func()) == 42
0459         assert m.test_override_cache(func2()) == 0