Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions playwright/_impl/_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,10 @@ def dispatch(self, msg: ParsedMessagePayload) -> None:
# Event handlers like route/locatorHandlerTriggered require us to perform async work.
# In order to report their potential errors to the user, we need to catch it and store it in the connection
def _done_callback(future: asyncio.Future) -> None:
# Pending handlers are cancelled when the sync context manager exits,
# and Future.exception() raises on a cancelled future.
if future.cancelled():
return
exc = future.exception()
if exc:
self._on_event_listener_error(exc)
Expand Down
39 changes: 39 additions & 0 deletions tests/sync/test_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
# limitations under the License.

import contextlib
import logging
import multiprocessing
import os
import signal
Expand All @@ -34,6 +35,7 @@
Dialog,
Error,
Page,
Route,
TimeoutError,
sync_playwright,
)
Expand Down Expand Up @@ -339,6 +341,43 @@ def test_expect_response_should_use_context_timeout(
assert "Timeout 1000ms exceeded" in exc_info.value.message


def _test_sync_playwright_should_not_log_cancelled_route_handler_on_exit(
browser_name: str, launch_arguments: Dict[str, Any], empty_page: str
) -> None:
records: List[logging.LogRecord] = []

class RecordingHandler(logging.Handler):
def emit(self, record: logging.LogRecord) -> None:
records.append(record)

logging.getLogger("asyncio").addHandler(RecordingHandler())
held: List[Route] = []
with sync_playwright() as p:
browser = p[browser_name].launch(**launch_arguments)
page = browser.new_page()
page.route("**/held", lambda route: held.append(route))
page.goto(empty_page)
page.evaluate("() => { fetch('/held').catch(() => {}); }")
while not held:
page.wait_for_timeout(50)
browser.close()
# Exiting the context manager cancels the pending route handler task.
assert [r.getMessage() for r in records] == []


def test_sync_playwright_should_not_log_cancelled_route_handler_on_exit(
server: Server, browser_name: str, launch_arguments: Dict[str, Any]
) -> None:
# https://github.com/microsoft/playwright/issues/43463
p = multiprocessing.Process(
target=_test_sync_playwright_should_not_log_cancelled_route_handler_on_exit,
args=[browser_name, launch_arguments, server.EMPTY_PAGE],
)
p.start()
p.join()
assert p.exitcode == 0


def _test_sync_playwright_stop_multiple_times() -> None:
playwright = sync_playwright().start()
playwright.stop()
Expand Down
Loading