Как я могу дождаться завершения объекта __del__ до закрытия асинхронного цикла? - PullRequest
0 голосов
/ 19 февраля 2019

У меня есть класс, в котором будет объект aiohttp.ClientSession.

Обычно, когда вы используете

async with aiohttp.ClientSession() as session:  
   # some code

Сессия будет закрыта после вызова метода __aexit__ сеанса.

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

Это работает:

import asyncio
import aiohttp

class MyAPI:
    def __init__(self):
        self.session = aiohttp.ClientSession()

    def __del__(self):
        # Close connection when this object is destroyed
        print('In __del__ now')
        asyncio.shield(self.session.__aexit__(None, None, None))



async def main():
    api = MyAPI()

asyncio.run(main())

Однако, если в каком-то местеВозникает исключение, цикл обработки событий завершается до завершения метода __aexit__.Как я могу преодолеть это?

stacktrace:

Traceback (most recent call last):
  File "/home/ron/.PyCharm2018.3/config/scratches/async.py", line 19, in <module>
    asyncio.run(main())
  File "/usr/local/lib/python3.7/asyncio/runners.py", line 43, in run
    return loop.run_until_complete(main)
  File "/usr/local/lib/python3.7/asyncio/base_events.py", line 568, in run_until_complete
    return future.result()
  File "/home/ron/.PyCharm2018.3/config/scratches/async.py", line 17, in main
    raise ValueError
ValueError
In __del__ now
Exception ignored in: <function MyAPI.__del__ at 0x7f49982c0e18>
Traceback (most recent call last):
  File "/home/ron/.PyCharm2018.3/config/scratches/async.py", line 11, in __del__
  File "/usr/local/lib/python3.7/asyncio/tasks.py", line 765, in shield
  File "/usr/local/lib/python3.7/asyncio/tasks.py", line 576, in ensure_future
  File "/usr/local/lib/python3.7/asyncio/events.py", line 644, in get_event_loop
RuntimeError: There is no current event loop in thread 'MainThread'.
sys:1: RuntimeWarning: coroutine 'ClientSession.__aexit__' was never awaited
Unclosed client session
client_session: <aiohttp.client.ClientSession object at 0x7f49982c2e10>

1 Ответ

0 голосов
/ 19 февраля 2019

Не используйте ловушку __del__ для очистки асинхронных ресурсов.Вы не можете считать, что он вызывается вообще, не говоря уже о контроле, когда он будет использоваться, или если асинхронный цикл все еще доступен в это время.Вы действительно хотите обработать это явно.

Либо сделайте API асинхронным диспетчером контекста, либо иным образом явно очистите ресурсы при выходе с помощью обработчика finally, скажем;операторы with и async with в основном предназначены для инкапсуляции очистки ресурсов, традиционно выполняемой в finally блоках.

Я бы сделал экземпляр API контекстным менеджером здесь:

class MyAPI:
    def __init__(self):
        self.session = aiohttp.ClientSession()

    async def __aenter__(self):
        return self

    async def __aexit__(self, *excinfo):
        await self.session.close()

Обратите внимание, что все, что действительно делает ClientSession.__aexit__(), это ожидание на self.close(), так что вышеизложенное непосредственно касается этой сопрограммы.

Затем используйте это в своем основном цикле с:

async def main():
    async with MyAPI() as api:
        pass

Другой вариант - предоставить свой собственный объект сеанса экземпляру MyAPI и взять на себя ответственность за его закрытие, когда вы закончите:

class MyAPI:
    def __init__(self, session):
        self.session = session

async def main():
    session = aiohttp.ClientSession()
    try:
        api = MyAPI(session)
        # do things with the API
    finally:
        await session.close()
...