python asycio RuntimeWarning: сопрограммы никогда не ждали - PullRequest
0 голосов
/ 19 июня 2020

Я пытаюсь отправить много запросов по 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)

После запуска этого кода с использованием выборки в качестве точки входа я получаю следующее сообщение об ошибке:

/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 возвращается в первый раз. У меня возникли проблемы с пониманием этого сообщения, так как я подумал, что старательно убедился, что все функции являются асинхронными c задачами. Единственная функция, которую я не использовал await, была call_objects, так как я хотел, чтобы она выполнялась одновременно.

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

в этой статье дает следующее объяснение :

Это предупреждение времени выполнения может возникать во многих сценариях ios, но причина одна и та же: объект сопрограммы создается путем вызова асинхронной функции, но никогда не вставляется в событие L oop.

Я полагал, что именно это я делал, когда вызывал задачи asyn c с помощью asyncio.gather.

Я должен отметить, что когда я помещаю print('url') внутри http_get, он выводит первые 50 URL-адресов, как я хочу, проблема возникает, когда asyncio.gather возвращается в первый раз.

1 Ответ

0 голосов
/ 20 июня 2020

В опубликованном коде есть logi c ошибка: [i:self.config['call_limit']] должно быть [i:i + self.config['call_limit']].

Это вызывает ошибку, потому что выражение оценивает входной срез в итерациях после первого, вызывая некоторые сопрограмм вызовов, которые никогда не передаются на gather и, следовательно, никогда не ожидаются)

я действительно не понимаю, почему он просто не выполнял одни и те же запросы много раз вместо того, чтобы останавливаться с ошибка.

Потому что ваш i будет увеличиваться, делая его больше call_limit на каждой l oop итерации, кроме первой. Например, предполагая, что call_limit равно 10, i будет 0 в первой итерации, и вы будете ждать calls[0:10], пока все хорошо. Но на следующей итерации i будет 10, и вы будете ждать calls[10:10], пустого среза. На следующей итерации вы будете ждать calls[20:10] (также пустой срез, хотя логически это должна быть ошибка), затем calls[30:10], снова пустой и так далее. Только первая итерация выбирает фактические члены списка.

...