diff --git a/packages/toolbox-core/src/toolbox_core/client.py b/packages/toolbox-core/src/toolbox_core/client.py index c3551d8a..1fcea46c 100644 --- a/packages/toolbox-core/src/toolbox_core/client.py +++ b/packages/toolbox-core/src/toolbox_core/client.py @@ -141,22 +141,6 @@ async def __aexit__(self, exc_type, exc_val, exc_tb): """ await self.close() - def __del__(self): - # This method is a "best-effort" safety net. - # It should NOT be relied upon for guaranteed resource cleanup. - # Explicitly using "async with" or calling "await client.close()" is the correct way. - if self.__manage_session: - try: - loop = get_running_loop() - except RuntimeError: - loop = None - - if loop and loop.is_running(): - # If a loop is running, try to schedule the close operation. - # This is "fire-and-forget"; there's no guarantee it will complete - # before the loop or interpreter shuts down. - loop.create_task(self.__session.close()) - async def close(self): """ Asynchronously closes the underlying client session. Doing so will cause diff --git a/packages/toolbox-core/src/toolbox_core/sync_client.py b/packages/toolbox-core/src/toolbox_core/sync_client.py index 979bca0d..7754468f 100644 --- a/packages/toolbox-core/src/toolbox_core/sync_client.py +++ b/packages/toolbox-core/src/toolbox_core/sync_client.py @@ -68,10 +68,7 @@ def close(self): any tools created by this Client to cease to function. """ coro = self.__async_client.close() - run_coroutine_threadsafe(coro, self.__loop).result(timeout=5) - - def __del__(self): - self.close() + run_coroutine_threadsafe(coro, self.__loop).result() def load_tool( self, diff --git a/packages/toolbox-core/tests/conftest.py b/packages/toolbox-core/tests/conftest.py index 6d2686a0..e579f843 100644 --- a/packages/toolbox-core/tests/conftest.py +++ b/packages/toolbox-core/tests/conftest.py @@ -22,7 +22,6 @@ import subprocess import tempfile import time -from asyncio import run_coroutine_threadsafe from typing import Generator import google @@ -30,8 +29,6 @@ from google.auth import compute_engine from google.cloud import secretmanager, storage -from toolbox_core import ToolboxSyncClient - #### Define Utility Functions def get_env_var(key: str) -> str: @@ -95,38 +92,6 @@ def get_auth_token(client_id: str) -> str: #### Define Fixtures -@pytest_asyncio.fixture(autouse=True) -def patch_sync_client_for_deadlock(monkeypatch): - """ - Automatically replaces the blocking `ToolboxSyncClient.close()` method - with a non-blocking version for the entire test run. - - The original `ToolboxSyncClient.close()` is a blocking method because it - calls `.result()`. In the pytest environment, this blocking call creates a - deadlock during the test teardown phase when it conflicts with other fixtures - (like `sync_client_environment` or `toolbox_server`) that are also managing - background processes and threads. - - By replacing `close` with this safe, non-blocking version, we prevent the - deadlock and allow the test suite's fixtures to tear down cleanly. - This change is only active during the test run. - """ - - def non_blocking_close(self): - """A replacement for close() that doesn't block.""" - if hasattr(self.__class__, "_ToolboxSyncClient__loop") and hasattr( - self, "_ToolboxSyncClient__async_client" - ): - loop = self.__class__._ToolboxSyncClient__loop - async_client = self._ToolboxSyncClient__async_client - - if loop and loop.is_running(): - coro = async_client.close() - run_coroutine_threadsafe(coro, loop) - - monkeypatch.setattr(ToolboxSyncClient, "close", non_blocking_close) - - @pytest_asyncio.fixture(scope="session") def project_id() -> str: return get_env_var("GOOGLE_CLOUD_PROJECT")