()
| 310 | reason="3.14.0b1/2 managed dict bug: https://github.com/python/cpython/issues/133912", |
| 311 | ) |
| 312 | def test_dynamic_attributes(): |
| 313 | instance = m.DynamicClass() |
| 314 | assert not hasattr(instance, "foo") |
| 315 | assert "foo" not in dir(instance) |
| 316 | |
| 317 | # Dynamically add attribute |
| 318 | instance.foo = 42 |
| 319 | assert hasattr(instance, "foo") |
| 320 | assert instance.foo == 42 |
| 321 | assert "foo" in dir(instance) |
| 322 | |
| 323 | # __dict__ should be accessible and replaceable |
| 324 | assert "foo" in instance.__dict__ |
| 325 | instance.__dict__ = {"bar": True} |
| 326 | assert not hasattr(instance, "foo") |
| 327 | assert hasattr(instance, "bar") |
| 328 | |
| 329 | with pytest.raises(TypeError) as excinfo: |
| 330 | instance.__dict__ = [] |
| 331 | assert str(excinfo.value) == "__dict__ must be set to a dictionary, not a 'list'" |
| 332 | |
| 333 | if env.GRAALPY: |
| 334 | pytest.skip("ConstructorStats is incompatible with GraalPy.") |
| 335 | cstats = ConstructorStats.get(m.DynamicClass) |
| 336 | assert cstats.alive() == 1 |
| 337 | del instance |
| 338 | pytest.gc_collect() |
| 339 | assert cstats.alive() == 0 |
| 340 | |
| 341 | # Derived classes should work as well |
| 342 | class PythonDerivedDynamicClass(m.DynamicClass): |
| 343 | pass |
| 344 | |
| 345 | for cls in (m.CppDerivedDynamicClass, PythonDerivedDynamicClass): |
| 346 | derived = cls() |
| 347 | derived.foobar = 100 |
| 348 | assert derived.foobar == 100 |
| 349 | |
| 350 | assert cstats.alive() == 1 |
| 351 | del derived |
| 352 | pytest.gc_collect() |
| 353 | assert cstats.alive() == 0 |
| 354 | |
| 355 | |
| 356 | # https://foss.heptapod.net/pypy/pypy/-/issues/2447 |
nothing calls this directly
no test coverage detected