2016-05-26 31 views
17

的獲取對於aiohttp入門文檔給下面的客戶端的例子:「異步與」在Python 3.4

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) 

,他們給的Python 3.4用戶提供了以下注意事項:

如果您正在使用Python 3.4,請使用@coroutine裝飾器替換await和 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) 

然而,這將無法運行,因爲async with沒有在Python 3.4的支持:

$ python3 client.py 
    File "client.py", line 7 
    async with session.get(url) as response: 
      ^
SyntaxError: invalid syntax 

如何翻譯的async with語句與Python 3.4一起工作?

回答

12

只要不使用session.get()的結果作爲上下文管理器;直接使用它作爲協程。請求上下文管理器session.get()產生通常會release the request退出,但so does using response.text(),所以你可以忽略這裏:

@asyncio.coroutine 
def fetch(session, url): 
    with aiohttp.Timeout(10): 
     response = yield from session.get(url) 
     return (yield from response.text()) 

包裝這裏返回的請求不具有所需的異步方法(__aenter____aexit__),他們在不使用Python 3.5時完全省略(請參閱relevant source code)。

如果您在session.get()調用和訪問response.text()之間有更多的語句可用,您可能想要使用try:..finally:來釋放連接;如果發生異常,Python 3.5版本上下文管理器還會關閉響應。由於yield from response.release()這裏需要,這不能在上下文管理器封裝的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() 
4

aiohttpexamples使用3.4語法來實現。基於json client example你的函數是:

@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() 

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" 

除了例外,您還會收到警告「未公開回復」。這可能會導致複雜應用程序中的連接泄漏。你會避免這個問題,如果你手動調用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 

我覺得這是更好地遵循正式的例子(和__aexit__implementation)和呼叫resp.release()/resp.close()明確。

+0

謝謝你指點我那些例子。我沒有找到那些。 – Imran

+2

請注意,如果發生異常,您通常要*關閉*響應。 –

+1

@MartijnPieters謝謝,你是對的。我修復了代碼以匹配'_RequestContextManager .__ aexit__'邏輯。 –