Python: Requests でリクエスト全体のタイムアウト時間を指定する
概要
Python で Requests ライブラリを使った場合、タイムアウトとして以下の指定が可能。
- Connnect Timeout : TCP 接続完了までのタイムアウト時間
- Read Timeout : サーバーからのデータ受信待ち時間 (無通信タイマー)
サーバーに接続して応答を受信し終わるまで、サーバーからのデータは実際には少しずつ順次受信する。Read Timeout はデータを受信するごとにリセットされるため、具体的に最悪値を見積もることはできない。速度制限された回線などでは想定外に時間がかかってしまう場合がある。
実装例
stream=True
を指定すると、応答を完全に受信し切る前に順次データを処理することができる。これを使って、部分的なデータを受信するごとに自前でチェックしてタイムアウトさせる。データを全く受信しない場合はブロックしてしまうので、別途既存のタイムアウト (Connnect Timeout, Read Timeout) も指定が必要。
def requests_get_with_timeout(url: str, overall_timeout: float, **kwargs) -> bytes:
to = time() + overall_timeout
res = requests.get(url, stream=True, **kwargs)
res.raise_for_status()
result = b''
with res:
for chunk in res.iter_content(CHUNK_SIZE):
if time() > to:
raise RequestException(f"Request timed out. (timeout={overall_timeout})", response=res)
result += chunk
return result
requests_get_with_timeout("http://localhost:8080", 10.0, timeout=5.0)
補足
タイムアウト時間を指定する際はこれらの違いを意識しておきたい。全体タイムアウトを指定できるライブラリも存在するが、Requests では実装・保守コストの観点から対応しない方針となっている模様。(関連 Issue: #3099)