1# -*- coding: utf-8 -*- 2import pytest 3 4import env # noqa: F401 5from pybind11_tests import ConstructorStats 6from pybind11_tests import multiple_inheritance as m 7 8 9def test_multiple_inheritance_cpp(): 10 mt = m.MIType(3, 4) 11 12 assert mt.foo() == 3 13 assert mt.bar() == 4 14 15 16@pytest.mark.skipif("env.PYPY and env.PY2") 17@pytest.mark.xfail("env.PYPY and not env.PY2") 18def test_multiple_inheritance_mix1(): 19 class Base1: 20 def __init__(self, i): 21 self.i = i 22 23 def foo(self): 24 return self.i 25 26 class MITypePy(Base1, m.Base2): 27 def __init__(self, i, j): 28 Base1.__init__(self, i) 29 m.Base2.__init__(self, j) 30 31 mt = MITypePy(3, 4) 32 33 assert mt.foo() == 3 34 assert mt.bar() == 4 35 36 37def test_multiple_inheritance_mix2(): 38 class Base2: 39 def __init__(self, i): 40 self.i = i 41 42 def bar(self): 43 return self.i 44 45 class MITypePy(m.Base1, Base2): 46 def __init__(self, i, j): 47 m.Base1.__init__(self, i) 48 Base2.__init__(self, j) 49 50 mt = MITypePy(3, 4) 51 52 assert mt.foo() == 3 53 assert mt.bar() == 4 54 55 56@pytest.mark.skipif("env.PYPY and env.PY2") 57@pytest.mark.xfail("env.PYPY and not env.PY2") 58def test_multiple_inheritance_python(): 59 class MI1(m.Base1, m.Base2): 60 def __init__(self, i, j): 61 m.Base1.__init__(self, i) 62 m.Base2.__init__(self, j) 63 64 class B1(object): 65 def v(self): 66 return 1 67 68 class MI2(B1, m.Base1, m.Base2): 69 def __init__(self, i, j): 70 B1.__init__(self) 71 m.Base1.__init__(self, i) 72 m.Base2.__init__(self, j) 73 74 class MI3(MI2): 75 def __init__(self, i, j): 76 MI2.__init__(self, i, j) 77 78 class MI4(MI3, m.Base2): 79 def __init__(self, i, j): 80 MI3.__init__(self, i, j) 81 # This should be ignored (Base2 is already initialized via MI2): 82 m.Base2.__init__(self, i + 100) 83 84 class MI5(m.Base2, B1, m.Base1): 85 def __init__(self, i, j): 86 B1.__init__(self) 87 m.Base1.__init__(self, i) 88 m.Base2.__init__(self, j) 89 90 class MI6(m.Base2, B1): 91 def __init__(self, i): 92 m.Base2.__init__(self, i) 93 B1.__init__(self) 94 95 class B2(B1): 96 def v(self): 97 return 2 98 99 class B3(object): 100 def v(self): 101 return 3 102 103 class B4(B3, B2): 104 def v(self): 105 return 4 106 107 class MI7(B4, MI6): 108 def __init__(self, i): 109 B4.__init__(self) 110 MI6.__init__(self, i) 111 112 class MI8(MI6, B3): 113 def __init__(self, i): 114 MI6.__init__(self, i) 115 B3.__init__(self) 116 117 class MI8b(B3, MI6): 118 def __init__(self, i): 119 B3.__init__(self) 120 MI6.__init__(self, i) 121 122 mi1 = MI1(1, 2) 123 assert mi1.foo() == 1 124 assert mi1.bar() == 2 125 126 mi2 = MI2(3, 4) 127 assert mi2.v() == 1 128 assert mi2.foo() == 3 129 assert mi2.bar() == 4 130 131 mi3 = MI3(5, 6) 132 assert mi3.v() == 1 133 assert mi3.foo() == 5 134 assert mi3.bar() == 6 135 136 mi4 = MI4(7, 8) 137 assert mi4.v() == 1 138 assert mi4.foo() == 7 139 assert mi4.bar() == 8 140 141 mi5 = MI5(10, 11) 142 assert mi5.v() == 1 143 assert mi5.foo() == 10 144 assert mi5.bar() == 11 145 146 mi6 = MI6(12) 147 assert mi6.v() == 1 148 assert mi6.bar() == 12 149 150 mi7 = MI7(13) 151 assert mi7.v() == 4 152 assert mi7.bar() == 13 153 154 mi8 = MI8(14) 155 assert mi8.v() == 1 156 assert mi8.bar() == 14 157 158 mi8b = MI8b(15) 159 assert mi8b.v() == 3 160 assert mi8b.bar() == 15 161 162 163def test_multiple_inheritance_python_many_bases(): 164 class MIMany14(m.BaseN1, m.BaseN2, m.BaseN3, m.BaseN4): 165 def __init__(self): 166 m.BaseN1.__init__(self, 1) 167 m.BaseN2.__init__(self, 2) 168 m.BaseN3.__init__(self, 3) 169 m.BaseN4.__init__(self, 4) 170 171 class MIMany58(m.BaseN5, m.BaseN6, m.BaseN7, m.BaseN8): 172 def __init__(self): 173 m.BaseN5.__init__(self, 5) 174 m.BaseN6.__init__(self, 6) 175 m.BaseN7.__init__(self, 7) 176 m.BaseN8.__init__(self, 8) 177 178 class MIMany916( 179 m.BaseN9, 180 m.BaseN10, 181 m.BaseN11, 182 m.BaseN12, 183 m.BaseN13, 184 m.BaseN14, 185 m.BaseN15, 186 m.BaseN16, 187 ): 188 def __init__(self): 189 m.BaseN9.__init__(self, 9) 190 m.BaseN10.__init__(self, 10) 191 m.BaseN11.__init__(self, 11) 192 m.BaseN12.__init__(self, 12) 193 m.BaseN13.__init__(self, 13) 194 m.BaseN14.__init__(self, 14) 195 m.BaseN15.__init__(self, 15) 196 m.BaseN16.__init__(self, 16) 197 198 class MIMany19(MIMany14, MIMany58, m.BaseN9): 199 def __init__(self): 200 MIMany14.__init__(self) 201 MIMany58.__init__(self) 202 m.BaseN9.__init__(self, 9) 203 204 class MIMany117(MIMany14, MIMany58, MIMany916, m.BaseN17): 205 def __init__(self): 206 MIMany14.__init__(self) 207 MIMany58.__init__(self) 208 MIMany916.__init__(self) 209 m.BaseN17.__init__(self, 17) 210 211 # Inherits from 4 registered C++ classes: can fit in one pointer on any modern arch: 212 a = MIMany14() 213 for i in range(1, 4): 214 assert getattr(a, "f" + str(i))() == 2 * i 215 216 # Inherits from 8: requires 1/2 pointers worth of holder flags on 32/64-bit arch: 217 b = MIMany916() 218 for i in range(9, 16): 219 assert getattr(b, "f" + str(i))() == 2 * i 220 221 # Inherits from 9: requires >= 2 pointers worth of holder flags 222 c = MIMany19() 223 for i in range(1, 9): 224 assert getattr(c, "f" + str(i))() == 2 * i 225 226 # Inherits from 17: requires >= 3 pointers worth of holder flags 227 d = MIMany117() 228 for i in range(1, 17): 229 assert getattr(d, "f" + str(i))() == 2 * i 230 231 232def test_multiple_inheritance_virtbase(): 233 class MITypePy(m.Base12a): 234 def __init__(self, i, j): 235 m.Base12a.__init__(self, i, j) 236 237 mt = MITypePy(3, 4) 238 assert mt.bar() == 4 239 assert m.bar_base2a(mt) == 4 240 assert m.bar_base2a_sharedptr(mt) == 4 241 242 243def test_mi_static_properties(): 244 """Mixing bases with and without static properties should be possible 245 and the result should be independent of base definition order""" 246 247 for d in (m.VanillaStaticMix1(), m.VanillaStaticMix2()): 248 assert d.vanilla() == "Vanilla" 249 assert d.static_func1() == "WithStatic1" 250 assert d.static_func2() == "WithStatic2" 251 assert d.static_func() == d.__class__.__name__ 252 253 m.WithStatic1.static_value1 = 1 254 m.WithStatic2.static_value2 = 2 255 assert d.static_value1 == 1 256 assert d.static_value2 == 2 257 assert d.static_value == 12 258 259 d.static_value1 = 0 260 assert d.static_value1 == 0 261 d.static_value2 = 0 262 assert d.static_value2 == 0 263 d.static_value = 0 264 assert d.static_value == 0 265 266 267# Requires PyPy 6+ 268def test_mi_dynamic_attributes(): 269 """Mixing bases with and without dynamic attribute support""" 270 271 for d in (m.VanillaDictMix1(), m.VanillaDictMix2()): 272 d.dynamic = 1 273 assert d.dynamic == 1 274 275 276def test_mi_unaligned_base(): 277 """Returning an offset (non-first MI) base class pointer should recognize the instance""" 278 279 n_inst = ConstructorStats.detail_reg_inst() 280 281 c = m.I801C() 282 d = m.I801D() 283 # + 4 below because we have the two instances, and each instance has offset base I801B2 284 assert ConstructorStats.detail_reg_inst() == n_inst + 4 285 b1c = m.i801b1_c(c) 286 assert b1c is c 287 b2c = m.i801b2_c(c) 288 assert b2c is c 289 b1d = m.i801b1_d(d) 290 assert b1d is d 291 b2d = m.i801b2_d(d) 292 assert b2d is d 293 294 assert ConstructorStats.detail_reg_inst() == n_inst + 4 # no extra instances 295 del c, b1c, b2c 296 assert ConstructorStats.detail_reg_inst() == n_inst + 2 297 del d, b1d, b2d 298 assert ConstructorStats.detail_reg_inst() == n_inst 299 300 301def test_mi_base_return(): 302 """Tests returning an offset (non-first MI) base class pointer to a derived instance""" 303 304 n_inst = ConstructorStats.detail_reg_inst() 305 306 c1 = m.i801c_b1() 307 assert type(c1) is m.I801C 308 assert c1.a == 1 309 assert c1.b == 2 310 311 d1 = m.i801d_b1() 312 assert type(d1) is m.I801D 313 assert d1.a == 1 314 assert d1.b == 2 315 316 assert ConstructorStats.detail_reg_inst() == n_inst + 4 317 318 c2 = m.i801c_b2() 319 assert type(c2) is m.I801C 320 assert c2.a == 1 321 assert c2.b == 2 322 323 d2 = m.i801d_b2() 324 assert type(d2) is m.I801D 325 assert d2.a == 1 326 assert d2.b == 2 327 328 assert ConstructorStats.detail_reg_inst() == n_inst + 8 329 330 del c2 331 assert ConstructorStats.detail_reg_inst() == n_inst + 6 332 del c1, d1, d2 333 assert ConstructorStats.detail_reg_inst() == n_inst 334 335 # Returning an unregistered derived type with a registered base; we won't 336 # pick up the derived type, obviously, but should still work (as an object 337 # of whatever type was returned). 338 e1 = m.i801e_c() 339 assert type(e1) is m.I801C 340 assert e1.a == 1 341 assert e1.b == 2 342 343 e2 = m.i801e_b2() 344 assert type(e2) is m.I801B2 345 assert e2.b == 2 346 347 348def test_diamond_inheritance(): 349 """Tests that diamond inheritance works as expected (issue #959)""" 350 351 # Issue #959: this shouldn't segfault: 352 d = m.D() 353 354 # Make sure all the various distinct pointers are all recognized as registered instances: 355 assert d is d.c0() 356 assert d is d.c1() 357 assert d is d.b() 358 assert d is d.c0().b() 359 assert d is d.c1().b() 360 assert d is d.c0().c1().b().c0().b() 361