Skip to content

Commit c7012ee

Browse files
committed
respect max_open_pages_per_browser limit on concurrent new_page calls
1 parent bfcc904 commit c7012ee

File tree

2 files changed

+43
-27
lines changed

2 files changed

+43
-27
lines changed

src/crawlee/browsers/_playwright_browser_controller.py

Lines changed: 33 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ def __init__(
7777
self._last_page_opened_at = datetime.now(timezone.utc)
7878

7979
self._total_opened_pages = 0
80+
self._opening_pages_count = 0
8081

8182
self._context_creation_lock: Lock | None = None
8283

@@ -119,7 +120,7 @@ def idle_time(self) -> timedelta:
119120
@property
120121
@override
121122
def has_free_capacity(self) -> bool:
122-
return self.pages_count < self._max_open_pages_per_browser
123+
return (self.pages_count + self._opening_pages_count) < self._max_open_pages_per_browser
123124

124125
@property
125126
@override
@@ -151,33 +152,38 @@ async def new_page(
151152
Raises:
152153
ValueError: If the browser has reached the maximum number of open pages.
153154
"""
154-
if not self.has_free_capacity:
155-
raise ValueError('Cannot open more pages in this browser.')
156-
157-
if self._use_incognito_pages:
158-
# In incognito there is exactly one context per one page. Create new context for each new page.
159-
new_context = await self._create_browser_context(
160-
browser_new_context_options=browser_new_context_options,
161-
proxy_info=proxy_info,
162-
)
163-
page = await new_context.new_page()
164-
else:
165-
async with await self._get_context_creation_lock():
166-
if not self._browser_context:
167-
self._browser_context = await self._create_browser_context(
168-
browser_new_context_options=browser_new_context_options,
169-
proxy_info=proxy_info,
170-
)
171-
page = await self._browser_context.new_page()
172-
173-
# Handle page close event
174-
page.on(event='close', f=self._on_page_close)
175-
176-
# Update internal state
177-
self._pages.append(page)
178-
self._last_page_opened_at = datetime.now(timezone.utc)
155+
self._opening_pages_count += 1
179156

180-
self._total_opened_pages += 1
157+
try:
158+
if not self.has_free_capacity:
159+
raise ValueError('Cannot open more pages in this browser.')
160+
161+
if self._use_incognito_pages:
162+
# In incognito there is exactly one context per one page. Create new context for each new page.
163+
new_context = await self._create_browser_context(
164+
browser_new_context_options=browser_new_context_options,
165+
proxy_info=proxy_info,
166+
)
167+
page = await new_context.new_page()
168+
else:
169+
async with await self._get_context_creation_lock():
170+
if not self._browser_context:
171+
self._browser_context = await self._create_browser_context(
172+
browser_new_context_options=browser_new_context_options,
173+
proxy_info=proxy_info,
174+
)
175+
page = await self._browser_context.new_page()
176+
177+
# Handle page close event
178+
page.on(event='close', f=self._on_page_close)
179+
180+
# Update internal state
181+
self._pages.append(page)
182+
self._last_page_opened_at = datetime.now(timezone.utc)
183+
184+
self._total_opened_pages += 1
185+
finally:
186+
self._opening_pages_count -= 1
181187
return page
182188

183189
@override

tests/unit/browsers/test_playwright_browser_controller.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,3 +136,13 @@ async def delayed_launch_persistent_context(*args: Any, **kwargs: Any) -> Any:
136136
await asyncio.gather(controller.new_page(), controller.new_page())
137137

138138
assert mocked_context_launcher.call_count == 1
139+
140+
141+
async def test_max_open_pages_limit_on_concurrent_creation(browser: Browser) -> None:
142+
"""Test that max open pages limit is respected during concurrent page creation."""
143+
controller = PlaywrightBrowserController(browser, max_open_pages_per_browser=1)
144+
145+
with pytest.raises(ValueError, match=r'Cannot open more pages in this browser.'):
146+
await asyncio.gather(controller.new_page(), controller.new_page())
147+
148+
await controller.close()

0 commit comments

Comments
 (0)