はじめに
最近の学びをアウトプット!
httpx(心の中ではふててぺっくす読み😉)についてです。
目次
HTTPクライアントライブラリ httpx
PythonのHTTPクライアントライブラリには、標準ライブラリのurllib
や、デファクトスタンダード感のあるrequests
がありますが、私が注目しているのがhttpx
!
「Features」のところにあるのですが、
HTTPX builds on the well-established usability of requests
意訳:HTTPXはrequests
のよく確立された使いやすさを活かし
つまりrequests
と同様の使い方ができ、その上requests
にはない非同期リクエストまでサポートしています!2
- A broadly requests-compatible API.
- 広く
requests
互換なAPI
- 広く
- Standard synchronous interface, but with async support if you need it.
- 標準的な同期インターフェース、しかし必要であれば非同期サポート
httpx利用シーンで遭遇したエラー
requests
にはSession
がありますが、これと同じものがhttpx
ではClient
3。
これを使い、素振りを兼ねて(また将来非同期で書き換えられたらいいなという想いも心に秘めて)、Web APIにリクエストを送るコードを書いていました。
このWeb APIからはページネーションを指定してデータを取得していたのですが、容量が大きいページがたまにあり、そこで処理が落ちるようでした。
Tracebackで目にしていたのはReadTimeout
対処:鍵はClientのtimeout
httpx
を使わずにcurl
コマンドを使うと時間がかかるものの取得できることを確認。
時間がかかるという点からドキュメントのtimeoutの項目を参照し、設定したことで解消しました(安定してデータを取り切れた!🙌)
httpxのドキュメントより timeout
Quickstartの「Timeouts」には以下のようにあります。
https://www.python-httpx.org/quickstart/#timeouts
The default timeout for network inactivity is five seconds.
ネットワークの不活性によるデフォルトのタイムアウトは5秒です
httpx
ではtimeoutをきめ細かく設定できるようで、Advancedのドキュメントに案内されます。
https://www.python-httpx.org/advanced/#fine-tuning-the-configuration
There are four different types of timeouts that may occur. These are connect, read, write, and pool timeouts.
4つの異なる書類のタイムアウトが発生するかもしれない。
connect, read, write, poolのタイムアウトだ
目にしていたReadTimeout
は、4種類のうちのreadのtimeoutということですね。
The read timeout specifies the maximum duration to wait for a chunk of data to be received (for example, a chunk of the response body).
If HTTPX is unable to receive data within this time frame, a ReadTimeout exception is raised.
read timeoutは、データの塊を受け取るまでの最大の待ち時間を指定する(例:レスポンスボディの塊)
この時間枠内にHTTPXがデータを受け取れなかった場合、ReadTimeout
例外が送出される
httpxのソースコード中のコメントより
read timeoutの設定にはソースコード中のコメントが特に助けになりました。
例が豊富で曖昧さがなかったのです。
https://github.com/encode/httpx/blob/0.24.0/httpx/_config.py#L195-L207
class Timeout: """ Timeout configuration. **Usage**: Timeout(None) # No timeouts. Timeout(5.0) # 5s timeout on all operations. Timeout(None, connect=5.0) # 5s timeout on connect, no other timeouts. Timeout(5.0, connect=10.0) # 10s timeout on connect. 5s timeout elsewhere. Timeout(5.0, pool=None) # No timeout on acquiring connection from pool. # 5s timeout elsewhere. """
この中で注目したのがTimeout(5.0, connect=10.0)
という例。
10s timeout on connect. 5s timeout elsewhere.
4つのうちのconnectのタイムアウトは10秒。残り3つ(read, write, pool)のタイムアウトは通して5秒という理解です。
curl
で取得したときにかかった時間を元に、Timeout(5.0, read=10.0)
のようにread timeoutだけ個別に設定することで、エラーは再現しなくなりました!
Timeout(5.0)
という例にありますが、これは4つ(connect, read, write, pool)全部通して5秒のタイムアウトで、Quickstartのドキュメントで言われていたやつですね4
実際httpx.Client
のtimeout
引数のデフォルト値を確認すると、
DEFAULT_TIMEOUT_CONFIG
がデフォルト値です- この定数が指すのは
Timeout(timeout=5.0)
(上で言及した例と同じ)です!
再現実験
簡単に実験してみましょう。
- Python 3.10.9
- httpx 0.24.0
- Flask 2.3.2
Flaskのアプリ(app.py
)を用意します。
import time from flask import Flask app = Flask(__name__) @app.route("/wait/<int:second>") def wait_then_return(second): time.sleep(second) return "Hello, World"
URLパラメタで指定された秒数待つエンドポイントを実装しました。
flask --app app run
で動かし、別のターミナルから以下のスクリプトを実行します。
import sys import httpx with httpx.Client() as client: print("----- Request start -----") r = client.get(f"http://127.0.0.1:5000/wait/{sys.argv[1]}") print("----- Request end -----") print(r)
% python client.py 2 ----- Request start ----- ----- Request end ----- <Response [200 OK]>
デフォルトのTimeout(timeout=5.0)
を超える待ち時間を指定すると、ReadTimeout
でスクリプトは落ちます(「Request end」)がありません。
% python client.py 6 ----- Request start ----- Traceback (most recent call last): (... 省略 ...) httpx.ReadTimeout: timed out
read timeoutを10秒まで伸ばしてみましょう。
import sys import httpx -with httpx.Client() as client: +with httpx.Client(timeout=httpx.Timeout(5.0, read=10.0)) as client: print("----- Request start -----") r = client.get(f"http://127.0.0.1:5000/wait/{sys.argv[1]}") print("----- Request end -----") print(r)
% python client.py 6 ----- Request start ----- ----- Request end ----- <Response [200 OK]>
6秒待つリクエストで落ちなくなりました!
終わりに
httpx
のClient
のtimeout
の設定について理解したことのアウトプットでした。
httpx
ではtimeout
には4種類ある- connect, read, write, pool
Timeout(5.0)
:4種類のtimeoutを通して5秒でtimeoutという設定- デフォルト設定
Timeout(5.0, read=10.0)
- readのtimeoutだけ10秒、ほか3つは通して5秒という指定
引き続き手になじませていき、非同期でも使いこなしたいですね
- ここだけ切り取るとなんだかやべーやつですが、こころちゃんが制服で学校に行っているということがとても心を温めてくれるのです。気になる方はU-NEXTで配信をどうぞ!↩
-
上位互換ってことになるんですかね?(
requests
詳しくないので細かいところが分からず...)↩ - If you are coming from Requests, httpx.Client() is what you can use instead of requests.Session(). https://www.python-httpx.org/advanced/#client-instances のHint↩
- 通してってどう実装しているんだろう? 時間測ってるやつがいるのかな↩