2016-05-26 5 views
17

「と非同期」aiohttpのStartedドキュメントが次のクライアント例与える行き方:は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では、awaitをfromとyieldを、そして、 async defを@coroutineデコレータで置き換えてください。

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()の結果をコンテキストマネージャとして使用しないでください。それを直接コルーチンとして使用してください。

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

要求ラッパーがここで返さ必要な非同期メソッド(__aenter____aexit__)を持っていない彼らは、:あなたがここにいることを無視することができるようにsession.get()が、終了時になり、通常release the requestを生成するが、so does using response.text()ことを要求コンテキストマネージャ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:

@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" 

:マルタインのソリューションは、単純な場合のために働くだろうが、具体的な例では、不要な行動につながる可能性があること

注意例外の他に、「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__'ロジックにマッチするようにコードを修正しました。 –