概要
Google API Python Client ライブラリで timeout を設定する方法について、ドキュメントに明記されていない部分が多いため、備忘録としてまとめます。
TL;DR(概要)
- 何もしない場合のデフォルトは 60秒
- socket.setdefaulttimeout() を使えばグローバルに設定可能
- httplib2.Http を自分で作成すれば細かく制御可能
1. 何もしない
設定しなければ socket.getdefaulttimeout() が None のため、ライブラリ内部でデフォルトの 60秒 が使われます。
import google.auth
from googleapiclient.discovery import build
def _create_service(scopes: List[str]):
credentials, project = google.auth.default(scopes=scopes)
return build('admin', 'directory_v1', num_retries=3, credentials=credentials)
ライブラリ内部実装
def build_http():
if socket.getdefaulttimeout() is not None:
http_timeout = socket.getdefaulttimeout()
else:
http_timeout = DEFAULT_HTTP_TIMEOUT_SEC # <- デフォルト値60s
2. socket.setdefaulttimeout() でグローバルに設定
Python の標準ライブラリ socket を使って、プログラム全体で使われるデフォルトのタイムアウトを設定できます。
import google.auth
import socket
from googleapiclient.discovery import build
def _create_service(scopes: List[str]):
credentials, project = google.auth.default(scopes=scopes)
socket.setdefaulttimeout(5) # 5秒に設定
return build('admin', 'directory_v1', num_retries=3, credentials=credentials)
ライブラリ内部実装
def build_http():
if socket.getdefaulttimeout() is not None:
http_timeout = socket.getdefaulttimeout() # <- ここが利用されます
else:
http_timeout = DEFAULT_HTTP_TIMEOUT_SEC
⚠️ この方法は、全てのソケット通信に影響するため注意が必要です。
3. httplib2.Http オブジェクトを明示的に挿入
googleapiclient.discovery.build は、任意の httplib2.Http オブジェクトを渡せるため、そこにタイムアウトを設定することが可能です。
import google.auth
from googleapiclient.discovery import build
from google_auth_httplib2 import AuthorizedHttp
import httplib2
def _create_service(scopes: List[str]):
credentials, project = google.auth.default(scopes=scopes)
http = httplib2.Http(timeout=120) # 明示的にタイムアウトを設定
authed_http = AuthorizedHttp(credentials, http=http)
return build('admin', 'directory_v1', num_retries=3, http=authed_http)
⚠️
googleapiclient.discovery.build
に http パラメータを渡す場合、同時に credentials を渡すことはできません。google_auth_httplib2.AuthorizedHttp
を利用してください
ライブラリ内部実装
if http is not None:
# if http is passed, the user cannot provide credentials
banned_options = [
(credentials, "credentials"), #ここで引っかかる
(client_options.credentials_file, "client_options.credentials_file"),
]
for option, name in banned_options:
if option is not None:
raise ValueError(
"Arguments http and {} are mutually exclusive".format(name)
)