File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change 88executed before running any tests.
99"""
1010
11+ import gc
1112import os
1213import os .path as osp
1314
2324import skimage
2425from guidata .config import ValidationMode , set_validation_mode
2526from guidata .utils .gitreport import format_git_info_for_pytest , get_git_info_for_modules
27+ from qtpy import QtCore as QC
2628from sigima .tests import helpers
2729
2830import datalab
@@ -143,3 +145,11 @@ def reset_cwd(request): # pylint: disable=unused-argument
143145def pytest_runtest_teardown (item , nextitem ): # pylint: disable=unused-argument
144146 """Run teardown after each test."""
145147 PluginRegistry .unregister_all_plugins ()
148+ # Destroy the widgets of the test now, rather than during a later test: closed
149+ # widgets deleted later (e.g. the internal console) are only deleted by the
150+ # next event loop, and main windows are part of reference cycles, only
151+ # destroyed when the garbage collector runs, possibly amid Qt operations
152+ if QC .QCoreApplication .instance () is not None :
153+ QC .QCoreApplication .processEvents ()
154+ QC .QCoreApplication .sendPostedEvents (None , QC .QEvent .DeferredDelete )
155+ gc .collect ()
Original file line number Diff line number Diff line change @@ -279,9 +279,21 @@ def _on_server_error(self, message: str) -> None:
279279 self ._on_server_stopped ()
280280
281281 def cleanup (self ) -> None :
282- """Clean up resources on shutdown."""
283- if self ._controller is not None and self ._controller .is_running :
284- self ._controller .stop ()
282+ """Clean up resources on shutdown.
283+
284+ The Web API controller is shared by all main windows: once this one is
285+ closed, the controller must neither keep it alive nor notify its actions.
286+ """
287+ controller = self ._controller
288+ if controller is None :
289+ return
290+ if controller .is_running :
291+ controller .stop ()
292+ controller .server_started .disconnect (self ._on_server_started )
293+ controller .server_stopped .disconnect (self ._on_server_stopped )
294+ controller .server_error .disconnect (self ._on_server_error )
295+ controller .release_main_window (self ._main_window )
296+ self ._controller = None
285297
286298 def show_connection_info (self ) -> None :
287299 """Show connection info dialog or copy to clipboard.
Original file line number Diff line number Diff line change @@ -106,6 +106,18 @@ def set_main_window(self, main_window: DLMainWindow) -> None:
106106 """
107107 self ._main_window = main_window
108108
109+ def release_main_window (self , main_window : DLMainWindow ) -> None :
110+ """Release the main window reference, if it is the given one.
111+
112+ The controller being shared by all main windows, a closed main window
113+ must not be kept alive by its reference.
114+
115+ Args:
116+ main_window: The DataLab main window being closed.
117+ """
118+ if self ._main_window is main_window :
119+ self ._main_window = None
120+
109121 @property
110122 def is_running (self ) -> bool :
111123 """Check if the server is running."""
@@ -337,6 +349,11 @@ def stop(self) -> None:
337349
338350 self ._uvicorn_server = None
339351 self ._adapter = None
352+ # pylint: disable=import-outside-toplevel
353+ from datalab .webapi .routes import set_adapter
354+
355+ # The adapter references the main window
356+ set_adapter (None )
340357 self ._url = None
341358 self ._token = None
342359
Original file line number Diff line number Diff line change 6060_LOCALHOST_NO_TOKEN : bool = False
6161
6262
63- def set_adapter (adapter : WorkspaceAdapter ) -> None :
64- """Set the workspace adapter for route handlers."""
63+ def set_adapter (adapter : WorkspaceAdapter | None ) -> None :
64+ """Set the workspace adapter for route handlers (None when stopped) ."""
6565 global _ADAPTER # noqa: PLW0603 # pylint: disable=global-statement
6666 _ADAPTER = adapter
6767
You can’t perform that action at this time.
0 commit comments