Bug report
Bug description:
After reusing one object's __dict__ for another object, and adding a reference to the original object in a dictionary on the target object, the original object might get collected too early (and cause a whole other dictionary to get cleared). I hope the code below describes it better than my words.
I've bisected this problem to c32dc47 .
This is the smallest reproduction I've found so far:
import gc import sys print(sys.version_info) class A: def __del__(self): print("del", hex(id(self))) a = A() a.attr_a = "test value" b = A() b.__dict__ = a.__dict__ b.other_attr = {"self reference": a, "other data": 42} del a print("before gc:", b.__dict__) gc.collect() print("after gc:", b.__dict__)
Results on 3.13:
sys.version_info(major=3, minor=13, micro=0, releaselevel='alpha', serial=5)
before gc: {'attr_a': 'test value', 'other_attr': {'self reference': <__main__.A object at 0x76cc6317a900>, 'other data': 42}}
del 0x76cc6317a900
after gc: {'attr_a': 'test value', 'other_attr': {}}
del 0x76cc6256ccd0
Results on 3.12, and what I expected to see:
sys.version_info(major=3, minor=12, micro=7, releaselevel='final', serial=0)
before gc: {'attr_a': 'test value', 'other_attr': {'self reference': <__main__.A object at 0x77496b531130>, 'other data': 42}}
after gc: {'attr_a': 'test value', 'other_attr': {'self reference': <__main__.A object at 0x77496b531130>, 'other data': 42}}
del 0x77496b531160
del 0x77496b531130
I've noticed this change in behaviour while working with Locust.io, the original issue for reference: locustio/locust#3050
CPython versions tested on:
3.13, CPython main branch
Operating systems tested on:
Linux
Linked PRs
- gh-130327: Always traverse managed dictionaries, even when inline values are available #130469
- [3.14] gh-130327: Always traverse managed dictionaries, even when inline values are available (GH-130469) #145438
- [3.13] gh-130327: Always traverse managed dictionaries, even when inline values are available (GH-130469) #145440