I want to have a dict mapping from active frame (FrameType
) objects to some data. Active meaning that it is in the current execution stack trace.
However, holding a reference to the frame object is not a good idea because that would also keep inactive frames and that will blow up the memory very soon because of all the references to all the local variables.
The natural solution would be to use a weakref to the frame objects, or more specifically WeakKeyDictionary
.
However, currently it is not possible to create a weakref to a frame object:
import weakref import sys f = sys._getframe() weakref.ref(f)
yields
TypeError: cannot create weak reference to 'frame' object
I assume this is a CPython implementation detail?
So, what are the options?
I could maybe anyway create references but try to clean them up as soon as possible when they are not active anymore. How do I actually check if a frame object is active?
Advertisement
Answer
Here’s a crazy idea: If you can’t hold a reference to the frame, make the frame hold a reference to you.
class FrameDict: def __init__(self): self._data_by_frame_id = {} def __setitem__(self, frame, data): frame_id = id(frame) # Make the frame hold a reference to a KeyDeleter, so when the frame # dies, the KeyDeleter dies, and the frame is removed from the dict deleter = KeyDeleter(self._data_by_frame_id, frame_id) frame.f_locals[deleter] = deleter self._data_by_frame_id[frame_id] = data def __getitem__(self, frame): frame_id = id(frame) return self._data_by_frame_id[frame_id] class KeyDeleter: def __init__(self, mapping, key): self.mapping = mapping self.key = key def __del__(self): del self.mapping[self.key]
Demo:
import inspect def new_frame(): return inspect.currentframe() frame_dict = FrameDict() frame = new_frame() frame_dict[frame] = 'foobar' print(frame_dict[frame]) # foobar del frame print(frame_dict._data_by_frame_id) # {}