Python 使用requests/urllib3在每次重试尝试时添加回调函数

Python 使用requests/urllib3在每次重试尝试时添加回调函数,python,callback,python-requests,urllib3,Python,Callback,Python Requests,Urllib3,我已经实现了一种重试机制,可以使用建议的和来执行请求会话 现在,我试图找出添加回调函数的最佳方法,该函数将在每次重试尝试时被调用 更详细地解释一下,如果Retry对象或requestsget方法能够添加回调函数,那就太好了。可能是这样的: import requests from requests.packages.urllib3.util.retry import Retry from requests.adapters import HTTPAdapter def retry_callba

我已经实现了一种重试机制,可以使用建议的和来执行
请求
会话

现在,我试图找出添加回调函数的最佳方法,该函数将在每次重试尝试时被调用

更详细地解释一下,如果
Retry
对象或requests
get
方法能够添加回调函数,那就太好了。可能是这样的:

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

def retry_callback(url):
    print url   

s = requests.Session()
retries = Retry(total=5, status_forcelist=[ 500, 502, 503, 504 ])
s.mount('http://', HTTPAdapter(max_retries=retries))

url = 'http://httpstat.us/500'
s.get(url, callback=retry_callback, callback_params=[url])

我知道,对于打印url,我可以使用日志记录,但这只是一个简单的示例,用于更复杂的使用。

您可以对
重试
类进行子类化,以添加该功能

这是与给定连接尝试的
重试
实例的完整交互流:

  • 每当引发异常,或返回30倍重定向响应,或
    Retry.is\u Retry()
    方法返回true时,将使用当前方法、url、响应对象(如果有)和异常(如果有)调用
    Retry.increment()
    • .increment()
      将重新引发错误(如果有),并且对象已配置为不重试该特定类别的错误
    • .increment()
      调用
      重试.new()
      创建一个更新的实例,更新所有相关计数器,并用新的(命名元组)修改
      历史
      属性
    • 如果
      重试,则
      .increment()
      将引发
      MaxRetryError
      异常。在
      重试的返回值上调用
      已耗尽()
      。new()
      为true<当它跟踪的任何计数器下降到0以下时(设置为
      None
      的计数器被忽略),code>is_defined()
      返回true
    • .increment()
      返回新的
      重试
      实例
  • Retry.increment()
    的返回值替换跟踪的旧
    Retry
    实例。如果有重定向,则调用
    Retry.sleep\u for_Retry()
    (如果在
    头之后有
    Retry,则调用睡眠),否则调用
    Retry.sleep()
    (调用
    self.sleep\u for_Retry()
    以执行
    头之后重试,否则,如果有退避策略,则只需睡眠)。然后使用新的
    Retry
    实例进行递归连接调用
这给了你3个好的回调点;在
.increment()
开始时,在创建新的
重试
实例时,以及在
super().increment()
周围的上下文管理器中,让回调否决异常或在退出时更新返回的重试策略

这就是在
.increment()
的开头放置钩子的方式:

import logging

logger = getLogger(__name__)

class CallbackRetry(Retry):
    def __init__(self, *args, **kwargs):
        self._callback = kwargs.pop('callback', None)
        super(CallbackRetry, self).__init__(*args, **kwargs)
    def new(self, **kw):
        # pass along the subclass additional information when creating
        # a new instance.
        kw['callback'] = self._callback
        return super(CallbackRetry, self).new(**kw)
    def increment(self, method, url, *args, **kwargs):
        if self._callback:
            try:
                self._callback(url)
            except Exception:
                logger.exception('Callback raised an exception, ignoring')
        return super(CallbackRetry, self).increment(method, url, *args, **kwargs)
注意,
url
参数实际上只是url路径,请求的净位置部分被省略(您必须从
\u pool
参数中提取,它有
.scheme
.host
.port
属性)

演示:

>>定义重试\u回调(url):
...     打印('调用回调',url)
...
>>>s=请求。会话()
>>>retries=CallbackRetry(总计=5,状态\u forcelist=[500502503504],callback=retry\u callback)
>>>s.mount('http://',HTTPAdapter(最大重试次数=重试次数))
>>>美国http://httpstat.us/500')
使用/500调用回调
使用/500调用回调
使用/500调用回调
使用/500调用回调
使用/500调用回调
使用/500调用回调
回溯(最近一次呼叫最后一次):
文件“/…/lib/python3.6/site packages/requests/adapters.py”,第440行,在send中
超时=超时
文件“/…/lib/python3.6/site packages/urllib3/connectionpool.py”,第732行,在urlopen中
车身位置=车身位置,**响应功率(千瓦)
文件“/…/lib/python3.6/site packages/urllib3/connectionpool.py”,第732行,在urlopen中
车身位置=车身位置,**响应功率(千瓦)
文件“/…/lib/python3.6/site packages/urllib3/connectionpool.py”,第732行,在urlopen中
车身位置=车身位置,**响应功率(千瓦)
[上一行又重复了1次]
文件“/…/lib/python3.6/site packages/urllib3/connectionpool.py”,第712行,在urlopen中
retries=retries.increment(方法,url,响应=response,_pool=self)
文件“”,第8行,增量
文件“/…/lib/python3.6/site packages/urllib3/util/retry.py”,第388行,增量
引发MaxRetryError(_池、url、错误或响应错误(原因))
urllib3.exceptions.MaxRetryError:HTTPConnectionPool(host='httpstat.us',port=80):url超过了最大重试次数:/500(由ResponseError(“错误响应太多了500个”),导致)
在处理上述异常期间,发生了另一个异常:
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
get中的文件“/…/lib/python3.6/site packages/requests/sessions.py”,第521行
返回self.request('GET',url,**kwargs)
文件“/…/lib/python3.6/site packages/requests/sessions.py”,第508行,在request中
resp=自我发送(准备,**发送)
文件“/…/lib/python3.6/site packages/requests/sessions.py”,第618行,在send中
r=适配器.send(请求,**kwargs)
文件“/…/lib/python3.6/site packages/requests/adapters.py”,第499行,在send中
raise RetryError(e,请求=请求)
requests.exceptions.RetryError:HTTPConnectionPool(host='httpstat.us',port=80):url超过了最大重试次数:/500(由ResponseError('错误响应太多',)引起)

.new()
方法中放置一个钩子将允许您为下一次尝试调整策略,并允许您反思
.history
属性,但无法避免重新引发异常。

Wow。谢谢你的详细回答。因此,如果我还想添加一个
callback\u params
参数,我可以像处理
callback
一样,在调用回调函数本身时传递它们,对吗?@a.Sarid:是的,您可以向子类添加任意数量的附加属性,一个
>>> def retry_callback(url):
...     print('Callback invoked with', url)
...
>>> s = requests.Session()
>>> retries = CallbackRetry(total=5, status_forcelist=[500, 502, 503, 504], callback=retry_callback)
>>> s.mount('http://', HTTPAdapter(max_retries=retries))
>>> s.get('http://httpstat.us/500')
Callback invoked with /500
Callback invoked with /500
Callback invoked with /500
Callback invoked with /500
Callback invoked with /500
Callback invoked with /500
Traceback (most recent call last):
  File "/.../lib/python3.6/site-packages/requests/adapters.py", line 440, in send
    timeout=timeout
  File "/.../lib/python3.6/site-packages/urllib3/connectionpool.py", line 732, in urlopen
    body_pos=body_pos, **response_kw)
  File "/.../lib/python3.6/site-packages/urllib3/connectionpool.py", line 732, in urlopen
    body_pos=body_pos, **response_kw)
  File "/.../lib/python3.6/site-packages/urllib3/connectionpool.py", line 732, in urlopen
    body_pos=body_pos, **response_kw)
  [Previous line repeated 1 more times]
  File "/.../lib/python3.6/site-packages/urllib3/connectionpool.py", line 712, in urlopen
    retries = retries.increment(method, url, response=response, _pool=self)
  File "<stdin>", line 8, in increment
  File "/.../lib/python3.6/site-packages/urllib3/util/retry.py", line 388, in increment
    raise MaxRetryError(_pool, url, error or ResponseError(cause))
urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='httpstat.us', port=80): Max retries exceeded with url: /500 (Caused by ResponseError('too many 500 error responses',))

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/.../lib/python3.6/site-packages/requests/sessions.py", line 521, in get
    return self.request('GET', url, **kwargs)
  File "/.../lib/python3.6/site-packages/requests/sessions.py", line 508, in request
    resp = self.send(prep, **send_kwargs)
  File "/.../lib/python3.6/site-packages/requests/sessions.py", line 618, in send
    r = adapter.send(request, **kwargs)
  File "/.../lib/python3.6/site-packages/requests/adapters.py", line 499, in send
    raise RetryError(e, request=request)
requests.exceptions.RetryError: HTTPConnectionPool(host='httpstat.us', port=80): Max retries exceeded with url: /500 (Caused by ResponseError('too many 500 error responses',))