Skip to content

Commit e0e8d81

Browse files
committed
refactor(robot): replace __del__ with weakref.finalize in imports_manager
Replace fragile `__del__` methods with `weakref.finalize` in both `_ImportEntry` and `ImportsManager` for reliable cleanup: - _ImportEntry: register finalizer in __init__ with file_watchers list and file_watcher_manager as strong refs (no self access needed); change _remove_file_watcher to use .clear() instead of = [] - ImportsManager: register finalizer lazily in executor property when ProcessPoolExecutor is created; add static _shutdown_executor callback weakref.finalize is guaranteed to run even with circular references and does not prevent garbage collection of reference cycles.
1 parent 7b3999e commit e0e8d81

File tree

1 file changed

+22
-9
lines changed

1 file changed

+22
-9
lines changed

packages/robot/src/robotcode/robot/diagnostics/imports_manager.py

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -112,18 +112,30 @@ def __init__(self, parent: "ImportsManager") -> None:
112112
self.references: weakref.WeakSet[Any] = weakref.WeakSet()
113113
self.file_watchers: List[FileWatcherEntry] = []
114114
self._lock = RLock(default_timeout=120, name="ImportEntryLock")
115+
self._release_watchers_finalizer = weakref.finalize(
116+
self,
117+
_ImportEntry._release_watchers,
118+
self.file_watchers,
119+
parent.file_watcher_manager,
120+
)
115121

116-
def __del__(self) -> None:
117-
try:
118-
self._remove_file_watcher()
119-
except RuntimeError:
120-
pass
122+
@staticmethod
123+
def _release_watchers(
124+
watchers: List[FileWatcherEntry],
125+
manager: FileWatcherManagerBase,
126+
) -> None:
127+
for watcher in watchers:
128+
try:
129+
manager.remove_file_watcher_entry(watcher)
130+
except RuntimeError:
131+
pass
132+
watchers.clear()
121133

122134
def _remove_file_watcher(self) -> None:
123135
if self.file_watchers:
124136
for watcher in self.file_watchers:
125137
self.parent.file_watcher_manager.remove_file_watcher_entry(watcher)
126-
self.file_watchers = []
138+
self.file_watchers.clear()
127139

128140
@abstractmethod
129141
def check_file_changed(self, changes: List[FileEvent]) -> Optional[FileChangeType]: ...
@@ -644,10 +656,10 @@ def __init__(
644656
context_name="imports",
645657
)
646658

647-
def __del__(self) -> None:
659+
@staticmethod
660+
def _shutdown_executor(executor: ProcessPoolExecutor) -> None:
648661
try:
649-
if self._executor is not None:
650-
self._executor.shutdown(wait=False)
662+
executor.shutdown(wait=False)
651663
except RuntimeError:
652664
pass
653665

@@ -1250,6 +1262,7 @@ def executor(self) -> ProcessPoolExecutor:
12501262
with self._executor_lock:
12511263
if self._executor is None:
12521264
self._executor = ProcessPoolExecutor(mp_context=mp.get_context("spawn"))
1265+
weakref.finalize(self, ImportsManager._shutdown_executor, self._executor)
12531266

12541267
return self._executor
12551268

0 commit comments

Comments
 (0)