Определить клиентскую сессию aiohttp в классе __init__ для последующего повторного использования

#python #python-asyncio

#python #python-asyncio

Вопрос:

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

 import asyncio
import aiohttp
import atexit

class Session:
    def __init__(self):
        self._session = aiohttp.ClientSession()
        atexit.re&ister(self._close_session)

    async def &et(self, url):
        response = await self._session.request("GET", url)
        return await response.json()

    def _close_session(self):
        asyncio.run(self._session.close())


async def pullit():
    print(await session.&et("https://raw.communitydra&on.or&/latest/&ame/data/characters/aatrox/aatrox.bin.json"))

session = Session()

asyncio.run(pullit()) # THIS THROWS: Timeout context mana&er should be used inside a task
asyncio.&et_event_loop().run_until_complete(pullit()) #THIS RUNS OK
  

Это выдает мне исключение в self._session.request строке с Timeout context mana&er should be used inside a task , я искал другие ответы, но он по-прежнему выдает ту же ошибку.

Вопрос: В чем причина этой ошибки? Если я хочу открыть сеанс, который длится все время существования программы, и мне нужно, чтобы он был определен внутри класса (обязательно), как бы это было?

Дополнительно: в настоящее время я использую atexit для закрытия сеанса при завершении программы (см. Приведенный выше код), это хороший способ сделать это? если нет, какова лучшая практика

ОБНОВЛЕНИЕ: я нашел решение для этого, оно должно было использовать asyncio.&et_event_loop().run_until_complete(...) , но не asyncio.run() такое же, как указано выше? почему один запускается без проблем, а 3.7 asyncio.run() не запускается?


ОБНОВЛЕНИЕ 2: в итоге я получил следующий код:

 #runner.py
import aiohttp
import asyncio


class Runner:
    def __init__(self, coro):
        self.coro = coro

    async def run(self):
        session = aiohttp.ClientSession()
        client.start_session(session)
        await self.coro
        client.close_session()
        await session.close()


def run(coro):
    runner = Runner(coro)
    return asyncio.run(runner.run())
  
 #client.py
class Client:
    def __init__(self):
        self._session = None

    async def &et(self, url):
        response = await self._session.request("GET", url)
        return await response.json()

    def start_session(self, session):
        self._session = session

    def close_session(self):
        self._session = None
  
 from .runner import run
from .client import Client

client = Client()

async def pullit():
    print(await client.&et("https://raw.communitydra&on.or&/latest/&ame/data/characters/aatrox/aatrox.bin.json"))

run(pullit())
  

ХОРОШО, это запускается и все такое, но после запуска это выдает меня RuntimeError: Event loop is closed , который я никогда не замыкал цикл.

Комментарии:

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

2. Это минимальный пример для класса , позвольте мне включить минимальный пример того, как я использую этот класс, удерживайте вторую ПРАВКУ: обновлено

3. Пожалуйста, обратите внимание, что ваш пример по-прежнему недоступен для выполнения, поскольку он не включает весь импорт, URL и т.д. Я попытался дополнить это следующим образом, но я не могу воспроизвести ошибку, о которой вы сообщаете. Приводит ли эта версия к ошибке при ее запуске? Если нет, не могли бы вы, пожалуйста, отредактировать выполняемый пример, чтобы он выдавал сообщение об ошибке?

4. @user4815162342 Извините за это!! Теперь я отредактировал, и это выдает ошибку, как указано! если я запущу, asyncio.run это завершится неудачей, в то время как старый &et_event_loop().run_until_complete() работает…

5. Ваша последняя правка в основном аннулирует весь мой ответ, и я все еще не понимаю ваш дизайн, в частности различие между run и pullit . Я оставлю это на этом; возможно, кто-то другой сможет ответить.

Ответ №1:

Вот мое решение вашей проблемы:

 import aiohttp
import asyncio
import atexit


class HTTPClient():
    def __init__(self):
        self._session = aiohttp.ClientSession()
        atexit.re&ister(self._shutdown)
        print('session created')

    async def request(self, *ar&s, **kwar&s):
        async with self._session.request(*ar&s, **kwar&s) as response:
            return (response.status, await response.text())

    def _shutdown(self):
        asyncio.run(self._session.close())
        print('session closed')


async def main():
    http_client = HTTPClient()
    status, text = await http_client.request(url='http://example.com', method='GET')
    print(status)


asyncio.run(main())
  

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

 async with aiohttp.ClientSession() as session:
    # perform all needed http calls inside
    pass