From 5fe463f3afecb01667d495c7f431651d4288d0a9 Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Sat, 6 Dec 2025 11:25:54 +0100 Subject: [PATCH] Allow agent to be explicitly shut down with controllable timeouts --- src/serena/agent.py | 8 ++++++-- src/serena/ls_manager.py | 9 +++++---- src/serena/project.py | 4 ++-- test/serena/test_serena_agent.py | 2 +- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/serena/agent.py b/src/serena/agent.py index dcbe2dbf..c02e1051 100644 --- a/src/serena/agent.py +++ b/src/serena/agent.py @@ -675,18 +675,22 @@ class SerenaAgent: ToolRegistry().print_tool_overview(self._active_tools.values()) def __del__(self) -> None: + self.shutdown() + + def shutdown(self, timeout: float = 2.0) -> None: """ - Destructor to clean up the language server instance and GUI logger + Shuts down the agent, freeing resources and stopping background tasks. """ if not hasattr(self, "_is_initialized"): return log.info("SerenaAgent is shutting down ...") if self._active_project is not None: - self._active_project.shutdown() + self._active_project.shutdown(timeout=timeout) self._active_project = None if self._gui_log_viewer: log.info("Stopping the GUI log window ...") self._gui_log_viewer.stop() + self._gui_log_viewer = None def get_tool_by_name(self, tool_name: str) -> Tool: tool_class = ToolRegistry().get_tool_class_by_name(tool_name) diff --git a/src/serena/ls_manager.py b/src/serena/ls_manager.py index 106cb3ed..6ec224b8 100644 --- a/src/serena/ls_manager.py +++ b/src/serena/ls_manager.py @@ -182,25 +182,26 @@ class LanguageServerManager: self._stop_language_server(ls, save_cache=save_cache) @staticmethod - def _stop_language_server(ls: SolidLanguageServer, save_cache: bool = False) -> None: + def _stop_language_server(ls: SolidLanguageServer, save_cache: bool = False, timeout: float = 2.0) -> None: if ls.is_running(): if save_cache: ls.save_cache() log.info(f"Stopping language server for language {ls.language} ...") - ls.stop() + ls.stop(shutdown_timeout=timeout) def iter_language_servers(self) -> Iterator[SolidLanguageServer]: for ls in self._language_servers.values(): yield self._ensure_functional_ls(ls) - def stop_all(self, save_cache: bool = False) -> None: + def stop_all(self, save_cache: bool = False, timeout: float = 2.0) -> None: """ Stops all managed language servers. :param save_cache: whether to save the cache before stopping + :param timeout: timeout for shutdown of each language server """ for ls in self.iter_language_servers(): - self._stop_language_server(ls, save_cache=save_cache) + self._stop_language_server(ls, save_cache=save_cache, timeout=timeout) def save_all_caches(self) -> None: """ diff --git a/src/serena/project.py b/src/serena/project.py index 0a23a4a6..968ad545 100644 --- a/src/serena/project.py +++ b/src/serena/project.py @@ -438,7 +438,7 @@ class Project(ToStringMixin): log.info("Removing and stopping the language server for language %s ...", language.value) self.language_server_manager.remove_language_server(language) - def shutdown(self) -> None: + def shutdown(self, timeout: float = 2.0) -> None: if self.language_server_manager is not None: - self.language_server_manager.stop_all(save_cache=True) + self.language_server_manager.stop_all(save_cache=True, timeout=timeout) self.language_server_manager = None diff --git a/test/serena/test_serena_agent.py b/test/serena/test_serena_agent.py index bebab035..cb2c905e 100644 --- a/test/serena/test_serena_agent.py +++ b/test/serena/test_serena_agent.py @@ -65,7 +65,7 @@ def serena_agent(request: pytest.FixtureRequest, serena_config) -> Iterator[Sere agent = SerenaAgent(project=project_name, serena_config=serena_config) yield agent # explicitly delete to free resources - del agent + agent.shutdown(timeout=5) class TestSerenaAgent: