aiohttp:response.status 什么时候可用?

aiohttp: when is the response.status available?

aiohttp 入门文档提供了以下客户端示例:

async with aiohttp.ClientSession() as session:
    async with session.get('https://api.github.com/events') as resp:
        print(resp.status)
        print(await resp.text())

我无法理解 response.status 何时可用。我的理解是协程在 await response.read() 行释放控制。我怎么可能在等待响应卷土重来之前访问状态?

resp 对象在 async with 块内可用。因此 resp.status 也可用。您也可以在某些方法上调用 await,例如 resp.text(),但不会释放对 async with 块的控制。即使在调用 await 之后,您也可以使用 resp

您首先获得 HTTP 响应 headers,其中第一行包含状态代码。如果您这样选择,您可以阅读其余的回复 body(这里有 resp.text())。由于 headers 总是相对较小而 body 可能非常大,aiohttp 让您有机会分别阅读两者。

重要区别:await ... 可能会释放对上下文的控制,例如,如果等待的数据不够快。 async with ... 语句也是如此。因此,您的代码在 resp 可用之前不会到达行 print(resp.status)

例如代码:

import aiohttp
import asyncio
import urllib.parse
import datetime

async def get(session, url):
    print("[{:%M:%S.%f}] getting {} ...".format(datetime.datetime.now(), urllib.parse.urlsplit(url).hostname))
    async with session.get(url) as resp:
        print("[{:%M:%S.%f}] {}, status: {}".format(datetime.datetime.now(), urllib.parse.urlsplit(url).hostname, resp.status))
        doc = await resp.text()
        print("[{:%M:%S.%f}] {}, len: {}".format(datetime.datetime.now(), urllib.parse.urlsplit(url).hostname, len(doc)))

async def main():
    session = aiohttp.ClientSession()

    url = "http://demo.borland.com/Testsite/stadyn_largepagewithimages.html"
    f1 = asyncio.ensure_future(get(session, url))
    print("[{:%M:%S.%f}] added {} to event loop".format(datetime.datetime.now(), urllib.parse.urlsplit(url).hostname))

    url = ""
    f2 = asyncio.ensure_future(get(session, url))
    print("[{:%M:%S.%f}] added {} to event loop".format(datetime.datetime.now(), urllib.parse.urlsplit(url).hostname))

    url = "https://api.github.com/events"
    f3 = asyncio.ensure_future(get(session, url))
    print("[{:%M:%S.%f}] added {} to event loop".format(datetime.datetime.now(), urllib.parse.urlsplit(url).hostname))

    await f1
    await f2
    await f3

    session.close()

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

可以产生这样的结果:

[16:42.415481] added demo.borland.com to event loop
[16:42.415481] added whosebug.com to event loop
[16:42.415481] added api.github.com to event loop
[16:42.415481] getting demo.borland.com ...
[16:42.422481] getting whosebug.com ...
[16:42.682496] getting api.github.com ...
[16:43.002515] demo.borland.com, status: 200
[16:43.510544] whosebug.com, status: 200
[16:43.759558] whosebug.com, len: 110650
[16:43.883565] demo.borland.com, len: 239012
[16:44.089577] api.github.com, status: 200
[16:44.318590] api.github.com, len: 43055

澄清(thx @deceze):在这里您可以看到(查看括号之间的时间)所有协程在发送检索网站的请求后释放控制,并在等待响应文本时第二次释放控制。此外,与 Whosebug 相比,borland 有太多的文本(不包括其他网络特征)以至于它只准备在打印来自 Whosebug 的文本后显示,尽管早些时候被请求。