1# -*- coding: utf-8 -*- 2import pytest 3 4import env # noqa: F401 5 6from pybind11_tests import methods_and_attributes as m 7from pybind11_tests import ConstructorStats 8 9 10def test_methods_and_attributes(): 11 instance1 = m.ExampleMandA() 12 instance2 = m.ExampleMandA(32) 13 14 instance1.add1(instance2) 15 instance1.add2(instance2) 16 instance1.add3(instance2) 17 instance1.add4(instance2) 18 instance1.add5(instance2) 19 instance1.add6(32) 20 instance1.add7(32) 21 instance1.add8(32) 22 instance1.add9(32) 23 instance1.add10(32) 24 25 assert str(instance1) == "ExampleMandA[value=320]" 26 assert str(instance2) == "ExampleMandA[value=32]" 27 assert str(instance1.self1()) == "ExampleMandA[value=320]" 28 assert str(instance1.self2()) == "ExampleMandA[value=320]" 29 assert str(instance1.self3()) == "ExampleMandA[value=320]" 30 assert str(instance1.self4()) == "ExampleMandA[value=320]" 31 assert str(instance1.self5()) == "ExampleMandA[value=320]" 32 33 assert instance1.internal1() == 320 34 assert instance1.internal2() == 320 35 assert instance1.internal3() == 320 36 assert instance1.internal4() == 320 37 assert instance1.internal5() == 320 38 39 assert instance1.overloaded() == "()" 40 assert instance1.overloaded(0) == "(int)" 41 assert instance1.overloaded(1, 1.0) == "(int, float)" 42 assert instance1.overloaded(2.0, 2) == "(float, int)" 43 assert instance1.overloaded(3, 3) == "(int, int)" 44 assert instance1.overloaded(4.0, 4.0) == "(float, float)" 45 assert instance1.overloaded_const(-3) == "(int) const" 46 assert instance1.overloaded_const(5, 5.0) == "(int, float) const" 47 assert instance1.overloaded_const(6.0, 6) == "(float, int) const" 48 assert instance1.overloaded_const(7, 7) == "(int, int) const" 49 assert instance1.overloaded_const(8.0, 8.0) == "(float, float) const" 50 assert instance1.overloaded_float(1, 1) == "(float, float)" 51 assert instance1.overloaded_float(1, 1.0) == "(float, float)" 52 assert instance1.overloaded_float(1.0, 1) == "(float, float)" 53 assert instance1.overloaded_float(1.0, 1.0) == "(float, float)" 54 55 assert instance1.value == 320 56 instance1.value = 100 57 assert str(instance1) == "ExampleMandA[value=100]" 58 59 cstats = ConstructorStats.get(m.ExampleMandA) 60 assert cstats.alive() == 2 61 del instance1, instance2 62 assert cstats.alive() == 0 63 assert cstats.values() == ["32"] 64 assert cstats.default_constructions == 1 65 assert cstats.copy_constructions == 2 66 assert cstats.move_constructions >= 2 67 assert cstats.copy_assignments == 0 68 assert cstats.move_assignments == 0 69 70 71def test_copy_method(): 72 """Issue #443: calling copied methods fails in Python 3""" 73 74 m.ExampleMandA.add2c = m.ExampleMandA.add2 75 m.ExampleMandA.add2d = m.ExampleMandA.add2b 76 a = m.ExampleMandA(123) 77 assert a.value == 123 78 a.add2(m.ExampleMandA(-100)) 79 assert a.value == 23 80 a.add2b(m.ExampleMandA(20)) 81 assert a.value == 43 82 a.add2c(m.ExampleMandA(6)) 83 assert a.value == 49 84 a.add2d(m.ExampleMandA(-7)) 85 assert a.value == 42 86 87 88def test_properties(): 89 instance = m.TestProperties() 90 91 assert instance.def_readonly == 1 92 with pytest.raises(AttributeError): 93 instance.def_readonly = 2 94 95 instance.def_readwrite = 2 96 assert instance.def_readwrite == 2 97 98 assert instance.def_property_readonly == 2 99 with pytest.raises(AttributeError): 100 instance.def_property_readonly = 3 101 102 instance.def_property = 3 103 assert instance.def_property == 3 104 105 with pytest.raises(AttributeError) as excinfo: 106 dummy = instance.def_property_writeonly # noqa: F841 unused var 107 assert "unreadable attribute" in str(excinfo.value) 108 109 instance.def_property_writeonly = 4 110 assert instance.def_property_readonly == 4 111 112 with pytest.raises(AttributeError) as excinfo: 113 dummy = instance.def_property_impossible # noqa: F841 unused var 114 assert "unreadable attribute" in str(excinfo.value) 115 116 with pytest.raises(AttributeError) as excinfo: 117 instance.def_property_impossible = 5 118 assert "can't set attribute" in str(excinfo.value) 119 120 121def test_static_properties(): 122 assert m.TestProperties.def_readonly_static == 1 123 with pytest.raises(AttributeError) as excinfo: 124 m.TestProperties.def_readonly_static = 2 125 assert "can't set attribute" in str(excinfo.value) 126 127 m.TestProperties.def_readwrite_static = 2 128 assert m.TestProperties.def_readwrite_static == 2 129 130 with pytest.raises(AttributeError) as excinfo: 131 dummy = m.TestProperties.def_writeonly_static # noqa: F841 unused var 132 assert "unreadable attribute" in str(excinfo.value) 133 134 m.TestProperties.def_writeonly_static = 3 135 assert m.TestProperties.def_readonly_static == 3 136 137 assert m.TestProperties.def_property_readonly_static == 3 138 with pytest.raises(AttributeError) as excinfo: 139 m.TestProperties.def_property_readonly_static = 99 140 assert "can't set attribute" in str(excinfo.value) 141 142 m.TestProperties.def_property_static = 4 143 assert m.TestProperties.def_property_static == 4 144 145 with pytest.raises(AttributeError) as excinfo: 146 dummy = m.TestProperties.def_property_writeonly_static 147 assert "unreadable attribute" in str(excinfo.value) 148 149 m.TestProperties.def_property_writeonly_static = 5 150 assert m.TestProperties.def_property_static == 5 151 152 # Static property read and write via instance 153 instance = m.TestProperties() 154 155 m.TestProperties.def_readwrite_static = 0 156 assert m.TestProperties.def_readwrite_static == 0 157 assert instance.def_readwrite_static == 0 158 159 instance.def_readwrite_static = 2 160 assert m.TestProperties.def_readwrite_static == 2 161 assert instance.def_readwrite_static == 2 162 163 with pytest.raises(AttributeError) as excinfo: 164 dummy = instance.def_property_writeonly_static # noqa: F841 unused var 165 assert "unreadable attribute" in str(excinfo.value) 166 167 instance.def_property_writeonly_static = 4 168 assert instance.def_property_static == 4 169 170 # It should be possible to override properties in derived classes 171 assert m.TestPropertiesOverride().def_readonly == 99 172 assert m.TestPropertiesOverride.def_readonly_static == 99 173 174 # Only static attributes can be deleted 175 del m.TestPropertiesOverride.def_readonly_static 176 assert ( 177 hasattr(m.TestPropertiesOverride, "def_readonly_static") 178 and m.TestPropertiesOverride.def_readonly_static 179 is m.TestProperties.def_readonly_static 180 ) 181 assert "def_readonly_static" not in m.TestPropertiesOverride.__dict__ 182 properties_override = m.TestPropertiesOverride() 183 with pytest.raises(AttributeError) as excinfo: 184 del properties_override.def_readonly 185 assert "can't delete attribute" in str(excinfo.value) 186 187 188def test_static_cls(): 189 """Static property getter and setters expect the type object as the their only argument""" 190 191 instance = m.TestProperties() 192 assert m.TestProperties.static_cls is m.TestProperties 193 assert instance.static_cls is m.TestProperties 194 195 def check_self(self): 196 assert self is m.TestProperties 197 198 m.TestProperties.static_cls = check_self 199 instance.static_cls = check_self 200 201 202def test_metaclass_override(): 203 """Overriding pybind11's default metaclass changes the behavior of `static_property`""" 204 205 assert type(m.ExampleMandA).__name__ == "pybind11_type" 206 assert type(m.MetaclassOverride).__name__ == "type" 207 208 assert m.MetaclassOverride.readonly == 1 209 assert ( 210 type(m.MetaclassOverride.__dict__["readonly"]).__name__ 211 == "pybind11_static_property" 212 ) 213 214 # Regular `type` replaces the property instead of calling `__set__()` 215 m.MetaclassOverride.readonly = 2 216 assert m.MetaclassOverride.readonly == 2 217 assert isinstance(m.MetaclassOverride.__dict__["readonly"], int) 218 219 220def test_no_mixed_overloads(): 221 from pybind11_tests import debug_enabled 222 223 with pytest.raises(RuntimeError) as excinfo: 224 m.ExampleMandA.add_mixed_overloads1() 225 assert str( 226 excinfo.value 227 ) == "overloading a method with both static and instance methods is not supported; " + ( 228 "compile in debug mode for more details" 229 if not debug_enabled 230 else "error while attempting to bind static method ExampleMandA.overload_mixed1" 231 "(arg0: float) -> str" 232 ) 233 234 with pytest.raises(RuntimeError) as excinfo: 235 m.ExampleMandA.add_mixed_overloads2() 236 assert str( 237 excinfo.value 238 ) == "overloading a method with both static and instance methods is not supported; " + ( 239 "compile in debug mode for more details" 240 if not debug_enabled 241 else "error while attempting to bind instance method ExampleMandA.overload_mixed2" 242 "(self: pybind11_tests.methods_and_attributes.ExampleMandA, arg0: int, arg1: int)" 243 " -> str" 244 ) 245 246 247@pytest.mark.parametrize("access", ["ro", "rw", "static_ro", "static_rw"]) 248def test_property_return_value_policies(access): 249 if not access.startswith("static"): 250 obj = m.TestPropRVP() 251 else: 252 obj = m.TestPropRVP 253 254 ref = getattr(obj, access + "_ref") 255 assert ref.value == 1 256 ref.value = 2 257 assert getattr(obj, access + "_ref").value == 2 258 ref.value = 1 # restore original value for static properties 259 260 copy = getattr(obj, access + "_copy") 261 assert copy.value == 1 262 copy.value = 2 263 assert getattr(obj, access + "_copy").value == 1 264 265 copy = getattr(obj, access + "_func") 266 assert copy.value == 1 267 copy.value = 2 268 assert getattr(obj, access + "_func").value == 1 269 270 271def test_property_rvalue_policy(): 272 """When returning an rvalue, the return value policy is automatically changed from 273 `reference(_internal)` to `move`. The following would not work otherwise.""" 274 275 instance = m.TestPropRVP() 276 o = instance.rvalue 277 assert o.value == 1 278 279 os = m.TestPropRVP.static_rvalue 280 assert os.value == 1 281 282 283# https://foss.heptapod.net/pypy/pypy/-/issues/2447 284@pytest.mark.xfail("env.PYPY") 285def test_dynamic_attributes(): 286 instance = m.DynamicClass() 287 assert not hasattr(instance, "foo") 288 assert "foo" not in dir(instance) 289 290 # Dynamically add attribute 291 instance.foo = 42 292 assert hasattr(instance, "foo") 293 assert instance.foo == 42 294 assert "foo" in dir(instance) 295 296 # __dict__ should be accessible and replaceable 297 assert "foo" in instance.__dict__ 298 instance.__dict__ = {"bar": True} 299 assert not hasattr(instance, "foo") 300 assert hasattr(instance, "bar") 301 302 with pytest.raises(TypeError) as excinfo: 303 instance.__dict__ = [] 304 assert str(excinfo.value) == "__dict__ must be set to a dictionary, not a 'list'" 305 306 cstats = ConstructorStats.get(m.DynamicClass) 307 assert cstats.alive() == 1 308 del instance 309 assert cstats.alive() == 0 310 311 # Derived classes should work as well 312 class PythonDerivedDynamicClass(m.DynamicClass): 313 pass 314 315 for cls in m.CppDerivedDynamicClass, PythonDerivedDynamicClass: 316 derived = cls() 317 derived.foobar = 100 318 assert derived.foobar == 100 319 320 assert cstats.alive() == 1 321 del derived 322 assert cstats.alive() == 0 323 324 325# https://foss.heptapod.net/pypy/pypy/-/issues/2447 326@pytest.mark.xfail("env.PYPY") 327def test_cyclic_gc(): 328 # One object references itself 329 instance = m.DynamicClass() 330 instance.circular_reference = instance 331 332 cstats = ConstructorStats.get(m.DynamicClass) 333 assert cstats.alive() == 1 334 del instance 335 assert cstats.alive() == 0 336 337 # Two object reference each other 338 i1 = m.DynamicClass() 339 i2 = m.DynamicClass() 340 i1.cycle = i2 341 i2.cycle = i1 342 343 assert cstats.alive() == 2 344 del i1, i2 345 assert cstats.alive() == 0 346 347 348def test_bad_arg_default(msg): 349 from pybind11_tests import debug_enabled 350 351 with pytest.raises(RuntimeError) as excinfo: 352 m.bad_arg_def_named() 353 assert msg(excinfo.value) == ( 354 "arg(): could not convert default argument 'a: UnregisteredType' in function " 355 "'should_fail' into a Python object (type not registered yet?)" 356 if debug_enabled 357 else "arg(): could not convert default argument into a Python object (type not registered " 358 "yet?). Compile in debug mode for more information." 359 ) 360 361 with pytest.raises(RuntimeError) as excinfo: 362 m.bad_arg_def_unnamed() 363 assert msg(excinfo.value) == ( 364 "arg(): could not convert default argument 'UnregisteredType' in function " 365 "'should_fail' into a Python object (type not registered yet?)" 366 if debug_enabled 367 else "arg(): could not convert default argument into a Python object (type not registered " 368 "yet?). Compile in debug mode for more information." 369 ) 370 371 372def test_accepts_none(msg): 373 a = m.NoneTester() 374 assert m.no_none1(a) == 42 375 assert m.no_none2(a) == 42 376 assert m.no_none3(a) == 42 377 assert m.no_none4(a) == 42 378 assert m.no_none5(a) == 42 379 assert m.ok_none1(a) == 42 380 assert m.ok_none2(a) == 42 381 assert m.ok_none3(a) == 42 382 assert m.ok_none4(a) == 42 383 assert m.ok_none5(a) == 42 384 385 with pytest.raises(TypeError) as excinfo: 386 m.no_none1(None) 387 assert "incompatible function arguments" in str(excinfo.value) 388 with pytest.raises(TypeError) as excinfo: 389 m.no_none2(None) 390 assert "incompatible function arguments" in str(excinfo.value) 391 with pytest.raises(TypeError) as excinfo: 392 m.no_none3(None) 393 assert "incompatible function arguments" in str(excinfo.value) 394 with pytest.raises(TypeError) as excinfo: 395 m.no_none4(None) 396 assert "incompatible function arguments" in str(excinfo.value) 397 with pytest.raises(TypeError) as excinfo: 398 m.no_none5(None) 399 assert "incompatible function arguments" in str(excinfo.value) 400 401 # The first one still raises because you can't pass None as a lvalue reference arg: 402 with pytest.raises(TypeError) as excinfo: 403 assert m.ok_none1(None) == -1 404 assert ( 405 msg(excinfo.value) 406 == """ 407 ok_none1(): incompatible function arguments. The following argument types are supported: 408 1. (arg0: m.methods_and_attributes.NoneTester) -> int 409 410 Invoked with: None 411 """ 412 ) 413 414 # The rest take the argument as pointer or holder, and accept None: 415 assert m.ok_none2(None) == -1 416 assert m.ok_none3(None) == -1 417 assert m.ok_none4(None) == -1 418 assert m.ok_none5(None) == -1 419 420 with pytest.raises(TypeError) as excinfo: 421 m.no_none_kwarg(None) 422 assert "incompatible function arguments" in str(excinfo.value) 423 with pytest.raises(TypeError) as excinfo: 424 m.no_none_kwarg(a=None) 425 assert "incompatible function arguments" in str(excinfo.value) 426 with pytest.raises(TypeError) as excinfo: 427 m.no_none_kwarg_kw_only(None) 428 assert "incompatible function arguments" in str(excinfo.value) 429 with pytest.raises(TypeError) as excinfo: 430 m.no_none_kwarg_kw_only(a=None) 431 assert "incompatible function arguments" in str(excinfo.value) 432 433 434def test_str_issue(msg): 435 """#283: __str__ called on uninitialized instance when constructor arguments invalid""" 436 437 assert str(m.StrIssue(3)) == "StrIssue[3]" 438 439 with pytest.raises(TypeError) as excinfo: 440 str(m.StrIssue("no", "such", "constructor")) 441 assert ( 442 msg(excinfo.value) 443 == """ 444 __init__(): incompatible constructor arguments. The following argument types are supported: 445 1. m.methods_and_attributes.StrIssue(arg0: int) 446 2. m.methods_and_attributes.StrIssue() 447 448 Invoked with: 'no', 'such', 'constructor' 449 """ 450 ) 451 452 453def test_unregistered_base_implementations(): 454 a = m.RegisteredDerived() 455 a.do_nothing() 456 assert a.rw_value == 42 457 assert a.ro_value == 1.25 458 a.rw_value += 5 459 assert a.sum() == 48.25 460 a.increase_value() 461 assert a.rw_value == 48 462 assert a.ro_value == 1.5 463 assert a.sum() == 49.5 464 assert a.rw_value_prop == 48 465 a.rw_value_prop += 1 466 assert a.rw_value_prop == 49 467 a.increase_value() 468 assert a.ro_value_prop == 1.75 469 470 471def test_ref_qualified(): 472 """Tests that explicit lvalue ref-qualified methods can be called just like their 473 non ref-qualified counterparts.""" 474 475 r = m.RefQualified() 476 assert r.value == 0 477 r.refQualified(17) 478 assert r.value == 17 479 assert r.constRefQualified(23) == 40 480 481 482def test_overload_ordering(): 483 "Check to see if the normal overload order (first defined) and prepend overload order works" 484 assert m.overload_order("string") == 1 485 assert m.overload_order(0) == 4 486 487 # Different for Python 2 vs. 3 488 uni_name = type(u"").__name__ 489 490 assert "1. overload_order(arg0: int) -> int" in m.overload_order.__doc__ 491 assert ( 492 "2. overload_order(arg0: {}) -> int".format(uni_name) 493 in m.overload_order.__doc__ 494 ) 495 assert ( 496 "3. overload_order(arg0: {}) -> int".format(uni_name) 497 in m.overload_order.__doc__ 498 ) 499 assert "4. overload_order(arg0: int) -> int" in m.overload_order.__doc__ 500 501 with pytest.raises(TypeError) as err: 502 m.overload_order(1.1) 503 504 assert "1. (arg0: int) -> int" in str(err.value) 505 assert "2. (arg0: {}) -> int".format(uni_name) in str(err.value) 506 assert "3. (arg0: {}) -> int".format(uni_name) in str(err.value) 507 assert "4. (arg0: int) -> int" in str(err.value) 508