【问题标题】:python asycio RuntimeWarning: coroutine was never awaitedpython asyncio RuntimeWarning:从未等待协程
【发布时间】:2020-10-10 01:45:39
【问题描述】:

我正在尝试同时向一个 url (~50) 发送许多请求。

from asyncio import Queue

import yaml
import asyncio

from aiohttp import ClientSession, TCPConnector


async def http_get(url, cookie):
    cookie = cookie.split('; ')
    cookie1 = cookie[0].split('=')
    cookie2 = cookie[1].split('=')
    cookies = {
        cookie1[0]: cookie1[1],
        cookie2[0]: cookie2[1]
    }

    async with ClientSession(cookies=cookies) as session:
        async with session.get(url, ssl=False) as response:
            return await response.json()


class FetchUtil:
    def __init__(self):
        self.config = yaml.safe_load(open('../config.yaml'))

    def fetch(self):
        asyncio.run(self.extract_objects())

    async def http_get_objects(self, object_type, limit, offset):
        path = '/path' + \
               '?query=&filter=%s&limit=%s&offset=%s' % (
                   object_type,
                   limit,
                   offset)
        return await self.http_get_domain(path)

    async def http_get_objects_limit(self, object_type, offset):
        result = await self.http_get_objects(
            object_type,
            self.config['object_limit'],
            offset
        )
        return result['result']

    async def http_get_domain(self, path):
        return await http_get(
            f'https://{self.config["domain"]}{path}',
            self.config['cookie']
        )

    async def call_objects(self, object_type, offset):
        result = await self.http_get_objects_limit(
            object_type,
            offset
        )
        return result

    async def extract_objects(self):
        calls = []
        object_count = (await self.http_get_objects(
                'PV', '1', '0'))['result']['count']
        for i in range(0, object_count, self.config['object_limit']):
            calls.append(self.call_objects('PV', str(i)))

        queue = Queue()
        for i in range(0, len(calls), self.config['call_limit']):
            results = await asyncio.gather(*calls[i:self.config['call_limit']])
            await queue.put(results)

使用 fetch 作为入口点运行此代码后,我收到以下错误消息:

/usr/local/Cellar/python/3.7.4_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/events.py:88: RuntimeWarning: coroutine 'FetchUtil.call_objects' was never awaited
      self._context.run(self._callback, *self._args)
    RuntimeWarning: Enable tracemalloc to get the object allocation traceback

asyncio.gather第一次返回后停止执行的程序。我很难理解这条消息,因为我认为我努力确保所有功能都是异步任务。我没有await 的唯一功能是call_objects,因为我希望它同时运行。

https://xinhuang.github.io/posts/2017-07-31-common-mistakes-using-python3-asyncio.html#org630d301

在这篇文章中给出如下解释:

此运行时警告可能在许多情况下发生,但原因是 相同:通过调用异步创建协程对象 函数,但从未插入到 EventLoop 中。

当我使用 asyncio.gather 调用异步任务时,我相信这就是我正在做的事情。

我应该注意,当我在 http_get 中放置 print('url') 时,它会输出我想要的前 50 个 url,当 asyncio.gather 第一次返回时似乎会出现问题。

【问题讨论】:

  • 也许[i:self.config['call_limit']] 应该是[i:i + self.config['call_limit']]?前者可能会产生一堆空切片,这导致一些调用永远不会传递给gather(因此从未等待)。
  • 您发现了一个我修复的逻辑错误,然后我的程序开始按预期工作,所以谢谢,但我实际上不明白为什么它不只是多次执行相同的请求而不是停止出现错误。
  • 我现在已经发布了解释作为答案。

标签: python-3.x python-asyncio aiohttp


【解决方案1】:

贴出的代码有逻辑错误:[i:self.config['call_limit']]应该是[i:i + self.config['call_limit']]

它会导致错误,因为表达式在第一个之后的迭代中计算为条目切片,导致某些调用协程永远不会传递给gather,因此永远不会等待)

我真的不明白为什么它不只是多次执行相同的请求而不是因为错误而停止。

因为您的i 会递增,使其在除第一次之外的每个循环迭代中都大于call_limit。例如,假设 call_limit 为 10,i 在第一次迭代中将为 0,您将等待 calls[0:10],到目前为止一切顺利。但在下一次迭代中,i 将是 10,而您将等待 calls[10:10],一个空切片。在之后的迭代中,您将等待calls[20:10](也是一个空切片,尽管从逻辑上讲它应该是一个错误),然后是calls[30:10],再次为空,等等。只有第一次迭代会选择列表的实际成员。

【讨论】:

    猜你喜欢
    • 2022-11-19
    • 2018-10-26
    • 2020-07-03
    • 2021-10-22
    • 2019-12-15
    • 2021-10-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多