aiohttp客户端在^ C上过早关闭会话

时间:2017-05-15 09:15:21

标签: python aiohttp

我在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会在注销完成之前关闭会话吗?!

我不确定如何进一步调试? 我错过了什么?

1 个答案:

答案 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上下文中使用。