внешний асинхронный менеджер контекста завершен до внутреннего асинхронного генератора
Учитывая следующий минимальный пример:
@asynccontextmanager
async def async_context():
try:
yield
finally:
await asyncio.sleep(1)
print('finalize context')
async def async_gen():
try:
yield
finally:
await asyncio.sleep(2)
# will never be called if timeout is larger than in async_context
print('finalize gen')
async def main():
async with async_context():
async for _ in async_gen():
break
if __name__ == "__main__":
asyncio.run(main())
я
break
ing во время итерации по генератору async, и я хочу, чтобы блок finally завершился до того, как будет запущен блок менеджера контекста async. В этом примере
"finalize gen"
никогда не будет напечатан, потому что программа завершится раньше, чем это произойдет.
Обратите внимание, что я намеренно выбрал тайм-аут
2
в блоке генераторов, чтобы у менеджеров контекста была возможность запускаться раньше. Если бы я выбрал
1
для обоих таймаутов будут напечатаны оба сообщения.
Это своего рода состояние гонки? Я ожидал, что все блоки будут завершены до завершения программы.
Как я могу предотвратить запуск блока контекстных менеджеров до завершения работы блока генераторов?
Для контекста:
Я использую драматурга для управления браузером хрома. Внешний диспетчер контекста предоставляет страницу, которую он закрывает в
finally
блокировать.
Я использую питон
3.9.0
.
Попробуйте этот пример: https://repl.it/@trixn86/AsyncGeneratorRaceCondition
1 ответ
Диспетчер асинхронного контекста ничего не знает об асинхронном генераторе. Ничего в
main
знает об асинхронном генераторе после того, как вы
break
, по факту. Вы не дали себе возможности дождаться завершения работы генератора.
Если вы хотите дождаться закрытия генератора, вам нужно явно обработать закрытие:
async def main():
async with async_context():
gen = async_gen()
try:
async for _ in gen:
break
finally:
await gen.aclose()
В Python 3.10 вы сможете использовать
contextlib.aclosing
вместо попытки / наконец:
async def main():
async with async_context():
gen = async_gen()
async with contextlib.aclosing(gen):
async for _ in gen:
break