Skip to content

Commit 5062fd9

Browse files
committed
rev2
1 parent 0f8b745 commit 5062fd9

File tree

2 files changed

+29
-36
lines changed

2 files changed

+29
-36
lines changed

playwright/_impl/_connection.py

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -124,22 +124,7 @@ class ProtocolCallback:
124124
def __init__(self, loop: asyncio.AbstractEventLoop) -> None:
125125
self.stack_trace: traceback.StackSummary = traceback.StackSummary()
126126
self.future = loop.create_future()
127-
# The outer task can get cancelled by the user, this forwards the cancellation to the inner task.
128-
current_task = asyncio.current_task()
129-
130-
def cb(task: asyncio.Task) -> None:
131-
if current_task:
132-
current_task.remove_done_callback(cb)
133-
if task.cancelled():
134-
self.future.cancel()
135-
136-
if current_task:
137-
current_task.add_done_callback(cb)
138-
self.future.add_done_callback(
139-
lambda _: current_task.remove_done_callback(cb)
140-
if current_task
141-
else None
142-
)
127+
self.current_task = asyncio.current_task()
143128

144129

145130
class RootChannelOwner(ChannelOwner):
@@ -251,7 +236,9 @@ def dispatch(self, msg: ParsedMessagePayload) -> None:
251236
id = msg.get("id")
252237
if id:
253238
callback = self._callbacks.pop(id)
254-
if callback.future.cancelled():
239+
if (
240+
callback.current_task and callback.current_task.cancelled()
241+
) or callback.future.cancelled():
255242
return
256243
error = msg.get("error")
257244
if error:

tests/async/test_asyncio.py

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,33 +12,39 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414
import asyncio
15-
import contextlib
15+
import gc
16+
from typing import Dict
17+
18+
import pytest
1619

1720
from playwright.async_api import async_playwright
1821

1922

20-
def test_should_cancel_underlying_calls(browser_name: str):
23+
async def test_should_cancel_underlying_protocol_calls(
24+
browser_name: str, launch_arguments: Dict
25+
):
2126
handler_exception = None
2227

23-
async def main():
24-
loop = asyncio.get_running_loop()
28+
def exception_handlerdler(loop, context) -> None:
29+
nonlocal handler_exception
30+
handler_exception = context["exception"]
2531

26-
def handler(loop, context):
27-
nonlocal handler_exception
28-
handler_exception = context["exception"]
32+
asyncio.get_running_loop().set_exception_handler(exception_handlerdler)
2933

30-
async with async_playwright() as p:
31-
loop.set_exception_handler(handler)
32-
browser = await p[browser_name].launch()
33-
page = await browser.new_page()
34-
task = asyncio.create_task(page.wait_for_selector("will-never-find"))
35-
# make sure that the wait_for_selector message was sent to the server (driver)
36-
await asyncio.sleep(1)
37-
task.cancel()
38-
with contextlib.suppress(asyncio.CancelledError):
39-
await task
40-
await browser.close()
34+
async with async_playwright() as p:
35+
browser = await p[browser_name].launch(**launch_arguments)
36+
page = await browser.new_page()
37+
task = asyncio.create_task(page.wait_for_selector("will-never-find"))
38+
# make sure that the wait_for_selector message was sent to the server (driver)
39+
await asyncio.sleep(0.1)
40+
task.cancel()
41+
with pytest.raises(asyncio.CancelledError):
42+
await task
43+
await browser.close()
4144

42-
asyncio.run(main())
45+
# The actual 'Future exception was never retrieved' is logged inside the Future destructor (__del__).
46+
gc.collect()
4347

4448
assert handler_exception is None
49+
50+
asyncio.get_running_loop().set_exception_handler(None)

0 commit comments

Comments
 (0)