1
- import asyncio
2
1
import functools
3
2
import inspect
4
- from typing import Awaitable
3
+ from typing import Awaitable , Callable
5
4
6
5
from ._utils import _ThreadedLoopAdapter
7
6
8
7
9
- def ensure_future (coro : Awaitable ) -> asyncio .Future :
10
- """Wrap a coroutine in a Future assigned to the threaded event loop.
8
+ async def _run_async_gen (asyncgen ):
9
+ async for item in asyncgen :
10
+ yield item
11
+
12
+
13
+ def use_threaded_loop (callback : Awaitable ) -> Callable :
14
+ """Wrap a coroutine callback so that Playwright coroutines are executed in
15
+ the threaded event loop.
11
16
12
17
On windows, Playwright runs in an event loop of its own in a separate thread.
13
18
If Playwright coroutines are awaited directly, they are assigned to the main
@@ -17,26 +22,33 @@ def ensure_future(coro: Awaitable) -> asyncio.Future:
17
22
Usage:
18
23
```
19
24
from playwright.async_api import Page
20
- from scrapy_playwright import ensure_future
25
+ from scrapy_playwright.utils import use_threaded_loop
21
26
27
+ @use_threaded_loop
22
28
async def parse(self, response):
23
29
page: Page = response.meta["playwright_page"]
24
- await ensure_future( page.screenshot(path="example.png", full_page=True) )
30
+ await page.screenshot(path="example.png", full_page=True)
25
31
```
26
32
"""
27
- return _ThreadedLoopAdapter ._ensure_future (coro )
28
33
29
-
30
- def use_threaded_loop (callback ):
31
- if not (inspect .iscoroutinefunction (callback ) or inspect .isasyncgenfunction (callback )):
34
+ if not inspect .iscoroutinefunction (callback ) and not inspect .isasyncgenfunction (callback ):
32
35
raise RuntimeError (
33
36
f"Cannot decorate callback '{ callback .__name__ } ' with 'use_threaded_loop':"
34
37
" callback must be a coroutine function or an async generator"
35
38
)
36
39
37
40
@functools .wraps (callback )
38
- async def wrapper (* args , ** kwargs ):
39
- future : asyncio . Future = _ThreadedLoopAdapter ._ensure_future (callback (* args , ** kwargs ))
41
+ async def async_func_wrapper (* args , ** kwargs ):
42
+ future = _ThreadedLoopAdapter ._ensure_future (callback (* args , ** kwargs ))
40
43
return await future
41
44
42
- return wrapper
45
+ @functools .wraps (callback )
46
+ async def async_gen_wrapper (* args , ** kwargs ):
47
+ asyncgen = _run_async_gen (callback (* args , ** kwargs ))
48
+ future = _ThreadedLoopAdapter ._ensure_future (asyncgen )
49
+ for item in await future :
50
+ yield item
51
+
52
+ if inspect .isasyncgenfunction (callback ):
53
+ return async_gen_wrapper
54
+ return async_func_wrapper
0 commit comments