web-dev-qa-db-ja.com

単一のaiohttp.ClientSessionを管理する方法は?

学習演習として、aiohttpのクイックスタートの例を変更して、単一のClientSessionで複数のURLをフェッチしようとしています(ドキュメントでは、通常、アプリケーションごとに1つのClientSessionを作成する必要があるとされています)。

import aiohttp
import asyncio

async def fetch(session, url):
  async with session.get(url) as response:
    return await response.text()

async def main(url, session):
  print(f"Starting '{url}'")
  html = await fetch(session, url)
  print(f"'{url}' done")

urls = (
  "https://python.org",
  "https://Twitter.com",
  "https://tumblr.com",
  "https://example.com",
  "https://github.com",
)

loop = asyncio.get_event_loop()
session = aiohttp.ClientSession()
loop.run_until_complete(asyncio.gather(
  *(loop.create_task(main(url, session)) for url in urls)
))
# session.close()   <- this doesn't make a difference

ただし、コルーチンの外部でClientSessionを作成することは、明らかにその方法ではありません。

 
➜python 1_async.py 
 1_async.py:30:UserWarning:コルーチンの外部でクライアントセッションを作成することは非常に危険なアイデアです
 session = aiohttp.ClientSession()
コルーチン外でのクライアントセッションの作成
 client_session:
開始 'https://python.org' 
開始 'https://Twitter.com' 
開始 'https://tumblr.com' 
開始 'https://example.com' 
開始 'https:/ /github.com'
'https://Twitter.com'done
'https://example.com'done
'https://github.com'done 
 'https://python.org' done 
 'https://tumblr.com' done 
 1_async.py:34:RuntimeWarning:coroutine'ClientSession.close 'was待たない
 session.close()
閉じられていないクライアントセッション
 client_session:
閉じられていないコネクタ
接続:['[(、15024.110107067)]' 、 '[(、15024.147785039)]'、 '[(、15024.252375415)]'、 '[(、15024.292646968)]'、 '[(、15024.342368087)]'、 '[(、15024.466971983)]'、 '[(、 150 24.602057745)] '、' [(、15024.837045568)] '] 
コネクタ:

FWIW、上記の変更を試みる前はmainでした。

async def main(url):
  async with aiohttp.ClientSession() as session:
    print(f"Starting '{url}'")
    html = await fetch(session, url)
    print(f"'{url}' done")

これを行う正しい方法は何でしょうか? URLのリストをmainに渡すことを考えましたが、非順次的に機能させることができませんでした。

8
Tamás Szelei

_Creating a client session outside of coroutine is a very dangerous idea_は、作成時に現在のループにバインドされるためです。後で実行ループを変更すると、ハングします。しかし、十分注意深く使用すれば、無視することができます。 関連ドキュメント

私に関しては、私はこの警告を無視するだけです。しかし、それを克服することも簡単です。

_async def create_session():
    return aiohttp.ClientSession()

session = asyncio.get_event_loop().run_until_complete(create_session())
_

さらに、Taskオブジェクトを明示的に作成する必要はありませんが、次のコルーチン関数を実行するだけです。

_loop.run_until_complete(asyncio.gather(
  *(main(url, session) for url in urls)
))
_

最後に、closeがコルーチンであることを忘れないでください。 loop.run_until_complete(session.close())を使用してsessionを閉じる必要があります。

ところで、非同期のようなループを作成したい場合は、 私の別の答え を参照できます。

6
Sraw