From 26d52816caf39e19e345a17dfb724ac0a7f533db Mon Sep 17 00:00:00 2001 From: Maximilian Haye Date: Mon, 13 Jan 2025 17:47:49 +0100 Subject: [PATCH] fix(ThreadWorker): don't unload modules without __loader__ DuckDB (and likely other packages) manually add their submodules (e.g. duckdb.duckdb.functional) during side-effects when the "parent" (e.g. duckdb.duckdb) is loaded. Since extension modules can't be reloaded, it is impossible to re-run these side-effects to recreate these modules. This commit preserves modules without the __loader__ attribute (which is added by Python's normal import machinery), sacrificing isolation for compatibility. --- questionpy_server/worker/impl/thread.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/questionpy_server/worker/impl/thread.py b/questionpy_server/worker/impl/thread.py index cf2c086..a56ddaf 100644 --- a/questionpy_server/worker/impl/thread.py +++ b/questionpy_server/worker/impl/thread.py @@ -39,6 +39,7 @@ def run(self) -> None: # is only for testing anyway, it'll do for now. original_path = sys.path.copy() original_module_names = set(sys.modules.keys()) + original_path_importer_cache = sys.path_importer_cache.copy() connection = WorkerToServerConnection(self._pipe.right, self._pipe.right) manager = WorkerManager(connection) @@ -50,7 +51,15 @@ def run(self) -> None: self._loop.call_soon_threadsafe(self._end_event.set) sys.path = original_path + sys.path_importer_cache = original_path_importer_cache for module_name in sys.modules.keys() - original_module_names: + if getattr(sys.modules[module_name], "__loader__", None) is None: + log.warning( + "Not unloading '%s', as it doesn't have a '__loader__' attribute and was probably added " + "manually.", + sys.modules[module_name].__name__, + ) + continue # Having reset the path, this forces questionpy and any package modules to be reloaded upon next import. del sys.modules[module_name]