diff --git a/playwright/async_api/_context_manager.py b/playwright/async_api/_context_manager.py index b5bdbbbb3..2876d85e5 100644 --- a/playwright/async_api/_context_manager.py +++ b/playwright/async_api/_context_manager.py @@ -25,6 +25,7 @@ class PlaywrightContextManager: def __init__(self) -> None: self._connection: Connection + self._exit_was_called = False async def __aenter__(self) -> AsyncPlaywright: loop = asyncio.get_running_loop() @@ -51,4 +52,7 @@ async def start(self) -> AsyncPlaywright: return await self.__aenter__() async def __aexit__(self, *args: Any) -> None: + if self._exit_was_called: + return + self._exit_was_called = True await self._connection.stop_async() diff --git a/playwright/sync_api/_context_manager.py b/playwright/sync_api/_context_manager.py index 3b5c6d8b4..4249a1fa1 100644 --- a/playwright/sync_api/_context_manager.py +++ b/playwright/sync_api/_context_manager.py @@ -36,6 +36,7 @@ def __init__(self) -> None: self._loop: asyncio.AbstractEventLoop self._own_loop = False self._watcher: Optional[AbstractChildWatcher] = None + self._exit_was_called = False def __enter__(self) -> SyncPlaywright: try: @@ -98,6 +99,9 @@ def start(self) -> SyncPlaywright: return self.__enter__() def __exit__(self, *args: Any) -> None: + if self._exit_was_called: + return + self._exit_was_called = True self._connection.stop_sync() if self._watcher: self._watcher.close() diff --git a/tests/async/test_asyncio.py b/tests/async/test_asyncio.py index 26d376c8c..7808aae48 100644 --- a/tests/async/test_asyncio.py +++ b/tests/async/test_asyncio.py @@ -48,3 +48,9 @@ def exception_handlerdler(loop, context) -> None: assert handler_exception is None asyncio.get_running_loop().set_exception_handler(None) + + +async def test_async_playwright_stop_multiple_times() -> None: + playwright = await async_playwright().start() + await playwright.stop() + await playwright.stop() diff --git a/tests/sync/test_sync.py b/tests/sync/test_sync.py index 375e4ff2a..e1555d611 100644 --- a/tests/sync/test_sync.py +++ b/tests/sync/test_sync.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import multiprocessing import os import pytest @@ -290,3 +291,16 @@ def test_expect_response_should_use_context_timeout( pass assert exc_info.type is TimeoutError assert "Timeout 1000ms exceeded" in exc_info.value.message + + +def _test_sync_playwright_stop_multiple_times() -> None: + playwright = sync_playwright().start() + playwright.stop() + playwright.stop() + + +def test_sync_playwright_stop_multiple_times() -> None: + p = multiprocessing.Process(target=_test_sync_playwright_stop_multiple_times) + p.start() + p.join() + assert p.exitcode == 0