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