Imr*_*ran 22 python python-3.x async-await python-asyncio aiohttp
aiohttp的入门文档提供了以下客户端示例:
import asyncio
import aiohttp
async def fetch_page(session, url):
with aiohttp.Timeout(10):
async with session.get(url) as response:
assert response.status == 200
return await response.read()
loop = asyncio.get_event_loop()
with aiohttp.ClientSession(loop=loop) as session:
content = loop.run_until_complete(
fetch_page(session, 'http://python.org'))
print(content)
Run Code Online (Sandbox Code Playgroud)
他们为Python 3.4用户提供以下注释:
如果您使用的是Python 3.4,请使用@coroutine装饰器替换a yield和async def.
如果我遵循这些说明,我会得到:
import aiohttp
import asyncio
@asyncio.coroutine
def fetch(session, url):
with aiohttp.Timeout(10):
async with session.get(url) as response:
return (yield from response.text())
if __name__ == '__main__':
loop = asyncio.get_event_loop()
with aiohttp.ClientSession(loop=loop) as session:
html = loop.run_until_complete(
fetch(session, 'http://python.org'))
print(html)
Run Code Online (Sandbox Code Playgroud)
但是,这不会运行,因为async with
Python 3.4不支持:
$ python3 client.py
File "client.py", line 7
async with session.get(url) as response:
^
SyntaxError: invalid syntax
Run Code Online (Sandbox Code Playgroud)
如何将async with
语句翻译为与Python 3.4一起使用?
Mar*_*ers 17
只是不要将结果session.get()
用作上下文管理器; 直接用它作为协程.生成的请求上下文管理器session.get()
通常会在退出时释放请求,但使用response.text()
时也是如此,因此您可以忽略此处:
@asyncio.coroutine
def fetch(session, url):
with aiohttp.Timeout(10):
response = yield from session.get(url)
return (yield from response.text())
Run Code Online (Sandbox Code Playgroud)
这里返回的请求包装器没有必需的异步方法(__aenter__
和__aexit__
),当不使用Python 3.5时它们完全省略(参见相关的源代码).
如果你在session.get()
呼叫和访问response.text()
等待之间有更多的声明,你可能想要使用一个try:..finally:
来释放连接; 如果发生异常,Python 3.5发行版上下文管理器也会关闭响应.因为yield from response.release()
这里需要a ,所以在Python 3.4之前不能将它封装在上下文管理器中:
import sys
@asyncio.coroutine
def fetch(session, url):
with aiohttp.Timeout(10):
response = yield from session.get(url)
try:
# other statements
return (yield from response.text())
finally:
if sys.exc_info()[0] is not None:
# on exceptions, close the connection altogether
response.close()
else:
yield from response.release()
Run Code Online (Sandbox Code Playgroud)
aiohttp
使用3.4语法实现的示例.基于json客户端示例,您的函数将是:
@asyncio.coroutine
def fetch(session, url):
with aiohttp.Timeout(10):
resp = yield from session.get(url)
try:
return (yield from resp.text())
finally:
yield from resp.release()
Run Code Online (Sandbox Code Playgroud)
UPD:
请注意,Martijn的解决方案适用于简单的情况,但在特定情况下可能会导致不必要的行为:
@asyncio.coroutine
def fetch(session, url):
with aiohttp.Timeout(5):
response = yield from session.get(url)
# Any actions that may lead to error:
1/0
return (yield from response.text())
# exception + warning "Unclosed response"
Run Code Online (Sandbox Code Playgroud)
除了例外,你还会收到警告"Unclosed response".这可能会导致复杂应用程序中的连接泄漏.如果您手动调用resp.release()
/,您将避免此问题resp.close()
:
@asyncio.coroutine
def fetch(session, url):
with aiohttp.Timeout(5):
resp = yield from session.get(url)
try:
# Any actions that may lead to error:
1/0
return (yield from resp.text())
except Exception as e:
# .close() on exception.
resp.close()
raise e
finally:
# .release() otherwise to return connection into free connection pool.
# It's ok to release closed response:
# https://github.com/KeepSafe/aiohttp/blob/master/aiohttp/client_reqrep.py#L664
yield from resp.release()
# exception only
Run Code Online (Sandbox Code Playgroud)
我认为最好遵循官方示例(和__aexit__
实现)并明确调用resp.release()
/ resp.close()
.