捕获requests.exceptions.RetryError时如何获取底层失败请求数据?

ely*_*ely 5 python exception python-3.x python-requests retry-logic

requests我正在使用一种有点标准的模式来在 Python 中的请求周围放置重试行为,

import requests
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry

retry_strategy = Retry(
    total=HTTP_RETRY_LIMIT,
    status_forcelist=HTTP_RETRY_CODES,
    method_whitelist=HTTP_RETRY_METHODS,
    backoff_factor=HTTP_BACKOFF_FACTOR
)
adapter = HTTPAdapter(max_retries=retry_strategy)
http = requests.Session()
http.mount("https://", adapter)
http.mount("http://", adapter)

...

try:
    response = http.get(... some request params ...)
except requests.Exceptions.RetryError as err:
    # Do logic with err to perform error handling & logging.
Run Code Online (Sandbox Code Playgroud)

不幸的是,RetryError 上的文档没有解释任何内容,当我如上所述拦截异常对象时,err.responseNone. 虽然您可以调用str(err)来获取异常的消息字符串,但这将需要不合理的字符串解析来尝试恢复特定的响应详细信息,即使有人愿意尝试,该消息实际上也会忽略必要的详细信息。例如,来自某个站点上的故意调用的一个此类响应给出了 400 秒(并不是说您真的会重试此操作,而只是为了调试),它会给出一条消息 -"(Caused by ResponseError('too many 400 error responses'))"它忽略了实际的响应详细信息,例如请求站点自己的描述文本400 错误的性质(这对于确定处理至关重要,甚至只是回传以记录错误)。

我想要做的是接收response最后一次不成功的重试尝试,并使用该特定失败的状态代码和描述来确定处理逻辑。尽管我想让它在重试后变得健壮,但在最终处理错误时,我仍然需要知道“重试次数过多”之外的潜在故障。

是否可以从重试引发的异常中提取此信息?

Niz*_*med 2

我们无法在每个异常中获得响应,因为请求可能尚未发送,或者请求或响应可能尚未到达其目的地。\n例如,这些异常不会获得响应。

\n
urllib3.exceptions.ConnectTimeoutError\nurllib3.exceptions.SSLError\nurllib3.exceptions.NewConnectionError\n
Run Code Online (Sandbox Code Playgroud)\n

urllib3.util.Retryname中有一个参数raise_on_status,默认为True. 如果成功False,则urllib3.exceptions.MaxRetryError不会被raise响应。\n如果没有异常,raise则可以肯定响应已到达。现在,将块中的块包裹response.raise_for_status在另一个块中变得很容易。elsetrytry

\n

我已更改except RetryErrorexcept Exception捕获其他异常。

\n
import requests\nfrom requests.adapters import HTTPAdapter\nfrom requests.packages.urllib3.util.retry import Retry\nfrom requests.exceptions import RetryError\n\n# DEFAULT_ALLOWED_METHODS = frozenset({'DELETE', 'GET', 'HEAD', 'OPTIONS', 'PUT', 'TRACE'})\n#     Default methods to be used for allowed_methods\n# RETRY_AFTER_STATUS_CODES = frozenset({413, 429, 503})\n#     Default status codes to be used for status_forcelist\n\nHTTP_RETRY_LIMIT = 3\nHTTP_BACKOFF_FACTOR = 0.2\n\nretry_strategy = Retry(\n    total=HTTP_RETRY_LIMIT,\n    backoff_factor=HTTP_BACKOFF_FACTOR,\n    raise_on_status=False,\n)\nadapter = HTTPAdapter(max_retries=retry_strategy)\nhttp = requests.Session()\nhttp.mount("https://", adapter)\nhttp.mount("http://", adapter)\ntry:\n    response = http.get("https://httpbin.org/status/503")\nexcept Exception as err:\n    print(err)\nelse:\n    try:\n        response.raise_for_status()\n    except Exception as e:\n        # Do logic with err to perform error handling & logging.\n        print(response.reason)\n        # Or\n        # print(e.response.reason)\n    else:\n        print(response.text)\n
Run Code Online (Sandbox Code Playgroud)\n

测试;

\n
# https://httpbin.org/user-agent\n\xe2\x9e\x9c  python requests_retry.py\n{\n  "user-agent": "python-requests/2.28.1"\n}\n\n# url =  https://httpbin.org/status/503\n\xe2\x9e\x9c  python requests_retry.py\nSERVICE UNAVAILABLE\n
Run Code Online (Sandbox Code Playgroud)\n