Skip to content

Commit 06ec9a2

Browse files
committed
Wrapper to assign futures to the correct event loop
1 parent 84ba393 commit 06ec9a2

File tree

2 files changed

+32
-2
lines changed

2 files changed

+32
-2
lines changed

scrapy_playwright/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,6 @@
11
__version__ = "0.0.40"
2+
3+
4+
from ._utils import ensure_future # noqa: F401
5+
from .handler import ScrapyPlaywrightDownloadHandler # noqa: F401
6+
from .page import PageMethod # noqa: F401

scrapy_playwright/_utils.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,10 +129,14 @@ async def _process_queue(cls) -> None:
129129
cls._coro_queue.task_done()
130130

131131
@classmethod
132-
def _deferred_from_coro(cls, coro) -> Deferred:
132+
def _ensure_future(cls, coro: Awaitable) -> asyncio.Future:
133133
future: asyncio.Future = asyncio.Future()
134134
asyncio.run_coroutine_threadsafe(cls._coro_queue.put((coro, future)), cls._loop)
135-
return scrapy.utils.defer.deferred_from_coro(future)
135+
return future
136+
137+
@classmethod
138+
def _deferred_from_coro(cls, coro: Awaitable) -> Deferred:
139+
return scrapy.utils.defer.deferred_from_coro(cls._ensure_future(coro))
136140

137141
@classmethod
138142
def start(cls, caller_id: int) -> None:
@@ -158,3 +162,24 @@ def stop(cls, caller_id: int) -> None:
158162
asyncio.run_coroutine_threadsafe(cls._coro_queue.join(), cls._loop)
159163
cls._loop.call_soon_threadsafe(cls._loop.stop)
160164
cls._thread.join()
165+
166+
167+
def ensure_future(coro: Awaitable) -> asyncio.Future:
168+
"""Wrap a coroutine in a Future assigned to the threaded event loop.
169+
170+
On windows, Playwright runs in an event loop of its own in a separate thread.
171+
If Playwright coroutines are awaited directly, they are assigned to the main
172+
thread's event loop, resulting in: "ValueError: The future belongs to a
173+
different loop than the one specified as the loop argument"
174+
175+
Usage:
176+
```
177+
from playwright.async_api import Page
178+
from scrapy_playwright import ensure_future
179+
180+
async def parse(self, response):
181+
page: Page = response.meta["playwright_page"]
182+
await ensure_future(page.screenshot(path="example.png", full_page=True))
183+
```
184+
"""
185+
return _ThreadedLoopAdapter._ensure_future(coro)

0 commit comments

Comments
 (0)