File indexing completed on 2025-01-18 10:17:58
0001 import pytest
0002
0003 import env
0004
0005 m = pytest.importorskip("pybind11_tests.virtual_functions")
0006 from pybind11_tests import ConstructorStats
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
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
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
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
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
0186
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
0195 nc = m.NonCopyable(a * a, b * b)
0196 return nc
0197
0198 def get_movable(self, a, b):
0199
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
0206 self.nc = m.NonCopyable(a, b)
0207 return self.nc
0208
0209 def get_movable(self, a, b):
0210
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
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
0270
0271
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
0287
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
0297
0298
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
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