Python: Requests でリクエスト全体のタイムアウト時間を指定する

概要

Python で Requests ライブラリを使った場合、タイムアウトとして以下の指定が可能。

参考) Advanced Usage - Timeouts

  • 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)