From 777a25031de0351b7dd8e9406d7a5ca1b49b9a54 Mon Sep 17 00:00:00 2001 From: nightcityblade Date: Fri, 5 Jun 2026 11:10:44 +0800 Subject: [PATCH] fix: close browser contexts from snapshot --- crawl4ai/browser_manager.py | 8 ++--- tests/browser/test_browser_manager_close.py | 35 +++++++++++++++++++++ 2 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 tests/browser/test_browser_manager_close.py diff --git a/crawl4ai/browser_manager.py b/crawl4ai/browser_manager.py index 0b429c34d..946e606b2 100644 --- a/crawl4ai/browser_manager.py +++ b/crawl4ai/browser_manager.py @@ -1969,7 +1969,7 @@ async def close(self): session_ids = list(self.sessions.keys()) for session_id in session_ids: await self.kill_session(session_id) - for ctx in self.contexts_by_config.values(): + for ctx in list(self.contexts_by_config.values()): try: await ctx.close() except Exception: @@ -1995,7 +1995,7 @@ async def close(self): await self.kill_session(session_id) # Close all contexts we created - for ctx in self.contexts_by_config.values(): + for ctx in list(self.contexts_by_config.values()): try: await ctx.close() except Exception: @@ -2032,7 +2032,7 @@ async def close(self): session_ids = list(self.sessions.keys()) for session_id in session_ids: await self.kill_session(session_id) - for ctx in self.contexts_by_config.values(): + for ctx in list(self.contexts_by_config.values()): try: await ctx.close() except Exception: @@ -2064,7 +2064,7 @@ async def close(self): await self.kill_session(session_id) # Now close all contexts we created. This reclaims memory from ephemeral contexts. - for ctx in self.contexts_by_config.values(): + for ctx in list(self.contexts_by_config.values()): try: await ctx.close() except Exception as e: diff --git a/tests/browser/test_browser_manager_close.py b/tests/browser/test_browser_manager_close.py new file mode 100644 index 000000000..c45199508 --- /dev/null +++ b/tests/browser/test_browser_manager_close.py @@ -0,0 +1,35 @@ +from types import SimpleNamespace + +import pytest + +from crawl4ai.browser_manager import BrowserManager + + +class _Context: + def __init__(self, manager=None): + self.manager = manager + self.closed = False + + async def close(self): + self.closed = True + if self.manager: + self.manager.contexts_by_config.pop("second", None) + + +@pytest.mark.asyncio +async def test_close_iterates_over_context_snapshot(): + manager = BrowserManager.__new__(BrowserManager) + manager.config = SimpleNamespace(cdp_url=None, sleep_on_close=False) + manager.sessions = {} + manager.browser = manager.managed_browser = manager.playwright = None + manager.logger = SimpleNamespace(error=lambda **_: None) + manager._using_cached_cdp = manager._launched_persistent = False + manager._context_refcounts = manager._context_last_used = manager._page_to_sig = {} + first, second = _Context(manager), _Context() + manager.contexts_by_config = {"first": first, "second": second} + + await manager.close() + + assert first.closed + assert second.closed + assert manager.contexts_by_config == {}