我在Ubuntu 16.04上使用aiohttp V2.0.7与Python 3.5.2在这样的代码中遇到嵌套块的麻烦:
while True:
try:
async with aiohttp.ClientSession() as session:
with contextlib.closing(MyServerSession(loop, session)) as ss:
# this was simplified for testing!
done, pending = await asyncio.wait(job1(ss), job2(ss))
except aiohttp.ClientError as ce:
log.error("HTTP session lost, retrying", client_error=ce)
time.sleep(10)
MyServerSession()实现close()以在服务器上注销,但点击^ C产生:
future: <Task finished coro=<MyServerSession.logout() done, defined at my_server.py:134> exception=RuntimeError('Session is closed',)>
Traceback (most recent call last):
File "/usr/lib/python3.5/asyncio/tasks.py", line 239, in _step
result = coro.send(None)
File "my_server.py", line 75, in _do_REST
async with self.session.request(verb, self.url + '/' + resource, timeout=timeout, **kwargs) as resp:
File "/usr/local/lib/python3.5/dist-packages/aiohttp/client.py", line 626, in __aenter__
self._resp = yield from self._coro
File "/usr/local/lib/python3.5/dist-packages/aiohttp/client.py", line 164, in _request
raise RuntimeError('Session is closed')
RuntimeError: Session is closed
看来aiohttp会在注销完成之前关闭会话吗?!
我不确定如何进一步调试? 我错过了什么?
答案 0 :(得分:0)
现在回答我自己的问题。
在下面的测试中,显示了asnyc上下文管理器,前两个嵌套async with blocks,然后一个使用来自contextlib的closing()。
class asy_context1:
def __aenter__(self):
print("c1: enter")
return asyncio.sleep(0.3)
def __aexit__(self, exc_type, exc, tb):
print("c1: exit")
return asyncio.sleep(0.3)
class asy_context2:
def __aenter__(self):
print("c2: enter")
return asyncio.sleep(0.1)
def __aexit__(self, exc_type, exc, tb):
print("c2: exit")
return asyncio.sleep(0.1)
async def test2async_withs():
async with asy_context1() as a1:
async with asy_context2() as a2:
pass
# raise RuntimeError("unexpected") # also works
from contextlib import closing
class asy_context3:
def __init__(self, loop):
self.loop = loop
async def logout(self):
print("logging out")
def close(self):
print("c3:close")
self.loop.run_until_complete(self.logout())
async def test_mixed_withs(loop):
async with asy_context1() as a1:
with closing(asy_context3(loop)) as a3:
pass
# raise RuntimeError("unexpected")
loop = asyncio.get_event_loop()
loop.run_until_complete(test2async_withs())
loop.run_until_complete(test_mixed_withs(loop))
loop.close()
这个输出是:
c1: enter
c2: enter
c2: exit
c1: exit
c1: enter
c3:close
c1: exit
logging out
Traceback (most recent call last):
File "test_exceptions.py", line 143, in <module>
test_case2()
File "test_exceptions.py", line 140, in test_case2
loop.run_until_complete(test_mixed_withs(loop))
File "/usr/lib/python3.5/asyncio/base_events.py", line 385, in run_until_complete
raise RuntimeError('Event loop stopped before Future completed.')
RuntimeError: Event loop stopped before Future completed.
虽然嵌套的异步上下文管理器按预期工作,但closing()的使用不会。
关键是,asy_context3.close()中新循环开始的时间刚刚没有定义。 close()按顺序调用,但close()中的loop.run_until_complete()似乎是在asy_context1 .__ aexit __()之后调度的。
contextlib.closing()显然不适合在asyncio上下文中使用。