简体   繁体   English

RuntimeWarning:协程从未在单独的线程中等待 asyncio.run

[英]RuntimeWarning: coroutine was never awaited asyncio.run in separate thread

I've built a little aiohttp based spider.我已经构建了一个基于 aiohttp 的小蜘蛛。 Here's some abbreviated code:这是一些缩写代码:

import asyncio
from aiohttp import ClientSession
from threading import Thread

class Spider:
    def __init__(self, urls):
        self.urls = urls
        self.start()

    async def fetch(self, session, url):
        async with session.get(url) as response:
            await self.handle_status(response) # undefined here for brevity
            return await self.render_body(response) # undefined here for brevity

    async def process_urls(self):
        async with ClientSession() as session:
            tasks = {self.fetch(session, url) for url in self.urls}
            for task in asyncio.as_completed(tasks):
                raw_data = await task
                data = self.extract_data(*raw_data) # sync method undefined here for brevity
                await self.store_data(data) # undefined here for brevity

    def start(self) -> None:
        try:
            asyncio.run(self.process_urls())
        except RuntimeError:  # loop already running
            x = Thread(target=asyncio.run, args=(self.process_urls(),))
            x.start()
            x.join()

The start method is intended to start an asyncio loop, but if one is already running, then it starts a new loop in a new Thread. start 方法旨在启动一个 asyncio 循环,但如果一个已经在运行,那么它会在一个新线程中启动一个新循环。

The code works whether run from an existing loop or not.无论是否从现有循环运行,该代码都有效。 But if run from an existing loop (eg with pytest.mark.asyncio()), I get this warning:但如果从现有循环运行(例如,使用 pytest.mark.asyncio()),我会收到以下警告:

RuntimeWarning: coroutine 'Spider.process_urls' was never awaited x.join() RuntimeWarning:协程“Spider.process_urls”从未等待 x.join()

Is this warning something I should be concerned about?这个警告是我应该关注的吗?

Is there a better way to handle this that would not cause this warning?有没有更好的方法来处理这个不会导致这个警告?

I've tried loop = asyncio.get_running_loop() with loop.create_task(self.process_urls()) instead of creating a new thread, but this fails with:我试过 loop = asyncio.get_running_loop() 和 loop.create_task(self.process_urls()) 而不是创建一个新线程,但这失败了:

RuntimeError: coroutine ignored GeneratorExit RuntimeError:协程忽略了 GeneratorExit

I've tried nest-asyncio, but my primary use case involves another library that uses uvloop, so nest-asyncio is not compatible.我试过nest-asyncio,但我的主要用例涉及另一个使用uvloop的库,所以nest-asyncio不兼容。

I admit, I can't offer you full explanation on the reasons why that warning is there but I can offer you code that is warning free:我承认,我无法为您提供有关该警告存在的原因的完整解释,但我可以为您提供无警告的代码:

def start(self) -> None:
    try:
        asyncio.get_running_loop()
    except RuntimeError:
        asyncio.run(self.process_urls())
    else:
        # Since the loop is already instantiated we can use it with our own
        # tasks without creating another thread.
        # asyncio.create_task(self.process_urls())
        x = Thread(target=asyncio.run, args=(self.process_urls(),))
        x.start()
        x.join()

Possibly, you get RuntimeWarning because your code creates new Thread (and BTW, that warning gets produced even when you switch to asyncio.create_task() ) during handling of the exception where "normal laws of physics" don't apply.您可能会收到RuntimeWarning ,因为您的代码在处理“正常物理定律”不适用的异常时创建了新线程(顺便说一句,即使您切换到asyncio.create_task()也会产生该警告)。 But again, that's only my guess so take it with a grain of salt.但同样,这只是我的猜测,所以请谨慎对待。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM