python-backoff 2.2.2rc2__tar.gz → 2.3.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- python_backoff-2.3.1/.gitignore +40 -0
- {python_backoff-2.2.2rc2 → python_backoff-2.3.1}/PKG-INFO +109 -59
- {python_backoff-2.2.2rc2 → python_backoff-2.3.1}/README.rst +107 -56
- {python_backoff-2.2.2rc2 → python_backoff-2.3.1}/backoff/__init__.py +11 -11
- {python_backoff-2.2.2rc2 → python_backoff-2.3.1}/backoff/_async.py +56 -44
- {python_backoff-2.2.2rc2 → python_backoff-2.3.1}/backoff/_common.py +15 -14
- {python_backoff-2.2.2rc2 → python_backoff-2.3.1}/backoff/_decorator.py +39 -34
- {python_backoff-2.2.2rc2 → python_backoff-2.3.1}/backoff/_jitter.py +0 -2
- {python_backoff-2.2.2rc2 → python_backoff-2.3.1}/backoff/_sync.py +51 -39
- {python_backoff-2.2.2rc2 → python_backoff-2.3.1}/backoff/_typing.py +15 -17
- {python_backoff-2.2.2rc2 → python_backoff-2.3.1}/backoff/_wait_gen.py +7 -11
- {python_backoff-2.2.2rc2 → python_backoff-2.3.1}/backoff/types.py +1 -2
- {python_backoff-2.2.2rc2 → python_backoff-2.3.1}/pyproject.toml +28 -8
- {python_backoff-2.2.2rc2 → python_backoff-2.3.1}/tests/common.py +3 -4
- {python_backoff-2.2.2rc2 → python_backoff-2.3.1}/tests/test_backoff.py +278 -254
- {python_backoff-2.2.2rc2 → python_backoff-2.3.1}/tests/test_backoff_async.py +262 -238
- {python_backoff-2.2.2rc2 → python_backoff-2.3.1}/tests/test_integration.py +0 -2
- {python_backoff-2.2.2rc2 → python_backoff-2.3.1}/tests/test_jitter.py +0 -1
- python_backoff-2.3.1/tests/test_package.py +32 -0
- {python_backoff-2.2.2rc2 → python_backoff-2.3.1}/tests/test_types.py +0 -2
- {python_backoff-2.2.2rc2 → python_backoff-2.3.1}/tests/test_typing.py +2 -1
- {python_backoff-2.2.2rc2 → python_backoff-2.3.1}/tests/test_wait_gen.py +16 -9
- python_backoff-2.2.2rc2/.gitignore +0 -15
- {python_backoff-2.2.2rc2 → python_backoff-2.3.1}/LICENSE +0 -0
- {python_backoff-2.2.2rc2 → python_backoff-2.3.1}/backoff/py.typed +0 -0
- {python_backoff-2.2.2rc2 → python_backoff-2.3.1}/tests/__init__.py +0 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.pyc
|
|
3
|
+
.coverage
|
|
4
|
+
virtualenv
|
|
5
|
+
MANIFEST
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
.cache/
|
|
9
|
+
*.egg-info
|
|
10
|
+
.vscode
|
|
11
|
+
.tox/
|
|
12
|
+
coverage.xml
|
|
13
|
+
|
|
14
|
+
# mkdocs
|
|
15
|
+
site/
|
|
16
|
+
|
|
17
|
+
# General
|
|
18
|
+
.DS_Store
|
|
19
|
+
__MACOSX/
|
|
20
|
+
.AppleDouble
|
|
21
|
+
.LSOverride
|
|
22
|
+
|
|
23
|
+
# Thumbnails
|
|
24
|
+
._*
|
|
25
|
+
|
|
26
|
+
# Files that might appear in the root of a volume
|
|
27
|
+
.DocumentRevisions-V100
|
|
28
|
+
.fseventsd
|
|
29
|
+
.Spotlight-V100
|
|
30
|
+
.TemporaryItems
|
|
31
|
+
.Trashes
|
|
32
|
+
.VolumeIcon.icns
|
|
33
|
+
.com.apple.timemachine.donotpresent
|
|
34
|
+
|
|
35
|
+
# Directories potentially created on remote AFP share
|
|
36
|
+
.AppleDB
|
|
37
|
+
.AppleDesktop
|
|
38
|
+
Network Trash Folder
|
|
39
|
+
Temporary Items
|
|
40
|
+
.apdisk
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-backoff
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.1
|
|
4
4
|
Summary: Function decoration for backoff and retry
|
|
5
5
|
Project-URL: Documentation, https://python-backoff.github.io/backoff
|
|
6
6
|
Project-URL: Repository, https://github.com/python-backoff/backoff
|
|
@@ -16,7 +16,6 @@ Classifier: Natural Language :: English
|
|
|
16
16
|
Classifier: Operating System :: OS Independent
|
|
17
17
|
Classifier: Programming Language :: Python
|
|
18
18
|
Classifier: Programming Language :: Python :: 3
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
20
19
|
Classifier: Programming Language :: Python :: 3.8
|
|
21
20
|
Classifier: Programming Language :: Python :: 3.9
|
|
22
21
|
Classifier: Programming Language :: Python :: 3.10
|
|
@@ -28,7 +27,7 @@ Classifier: Programming Language :: Python :: 3.15
|
|
|
28
27
|
Classifier: Topic :: Internet :: WWW/HTTP
|
|
29
28
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
30
29
|
Classifier: Topic :: Utilities
|
|
31
|
-
Requires-Python: >=3.
|
|
30
|
+
Requires-Python: >=3.8
|
|
32
31
|
Description-Content-Type: text/x-rst
|
|
33
32
|
|
|
34
33
|
backoff
|
|
@@ -59,7 +58,7 @@ for asynchronous code.
|
|
|
59
58
|
Examples
|
|
60
59
|
========
|
|
61
60
|
|
|
62
|
-
Since Kenneth Reitz's `requests <
|
|
61
|
+
Since Kenneth Reitz's `requests <https://requests.readthedocs.io>`_ module
|
|
63
62
|
has become a defacto standard for synchronous HTTP clients in Python,
|
|
64
63
|
networking examples below are written using it, but it is in no way required
|
|
65
64
|
by the backoff module.
|
|
@@ -73,8 +72,10 @@ is raised. Here's an example using exponential backoff when any
|
|
|
73
72
|
|
|
74
73
|
.. code-block:: python
|
|
75
74
|
|
|
76
|
-
@backoff.on_exception(
|
|
77
|
-
|
|
75
|
+
@backoff.on_exception(
|
|
76
|
+
backoff.expo,
|
|
77
|
+
requests.exceptions.RequestException,
|
|
78
|
+
)
|
|
78
79
|
def get_url(url):
|
|
79
80
|
return requests.get(url)
|
|
80
81
|
|
|
@@ -83,9 +84,13 @@ the same backoff behavior is desired for more than one exception type:
|
|
|
83
84
|
|
|
84
85
|
.. code-block:: python
|
|
85
86
|
|
|
86
|
-
@backoff.on_exception(
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
@backoff.on_exception(
|
|
88
|
+
backoff.expo,
|
|
89
|
+
(
|
|
90
|
+
requests.exceptions.Timeout,
|
|
91
|
+
requests.exceptions.ConnectionError,
|
|
92
|
+
),
|
|
93
|
+
)
|
|
89
94
|
def get_url(url):
|
|
90
95
|
return requests.get(url)
|
|
91
96
|
|
|
@@ -99,9 +104,11 @@ of total time in seconds that can elapse before giving up.
|
|
|
99
104
|
|
|
100
105
|
.. code-block:: python
|
|
101
106
|
|
|
102
|
-
@backoff.on_exception(
|
|
103
|
-
|
|
104
|
-
|
|
107
|
+
@backoff.on_exception(
|
|
108
|
+
backoff.expo,
|
|
109
|
+
requests.exceptions.RequestException,
|
|
110
|
+
max_time=60,
|
|
111
|
+
)
|
|
105
112
|
def get_url(url):
|
|
106
113
|
return requests.get(url)
|
|
107
114
|
|
|
@@ -111,10 +118,12 @@ to make to the target function before giving up.
|
|
|
111
118
|
|
|
112
119
|
.. code-block:: python
|
|
113
120
|
|
|
114
|
-
@backoff.on_exception(
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
121
|
+
@backoff.on_exception(
|
|
122
|
+
backoff.expo,
|
|
123
|
+
requests.exceptions.RequestException,
|
|
124
|
+
max_tries=8,
|
|
125
|
+
jitter=None,
|
|
126
|
+
)
|
|
118
127
|
def get_url(url):
|
|
119
128
|
return requests.get(url)
|
|
120
129
|
|
|
@@ -130,10 +139,13 @@ be retried:
|
|
|
130
139
|
def fatal_code(e):
|
|
131
140
|
return 400 <= e.response.status_code < 500
|
|
132
141
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
142
|
+
|
|
143
|
+
@backoff.on_exception(
|
|
144
|
+
backoff.expo,
|
|
145
|
+
requests.exceptions.RequestException,
|
|
146
|
+
max_time=300,
|
|
147
|
+
giveup=fatal_code,
|
|
148
|
+
)
|
|
137
149
|
def get_url(url):
|
|
138
150
|
return requests.get(url)
|
|
139
151
|
|
|
@@ -151,11 +163,14 @@ case, regardless of the logic in the `on_exception` handler.
|
|
|
151
163
|
def fatal_code(e):
|
|
152
164
|
return 400 <= e.response.status_code < 500
|
|
153
165
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
166
|
+
|
|
167
|
+
@backoff.on_exception(
|
|
168
|
+
backoff.expo,
|
|
169
|
+
requests.exceptions.RequestException,
|
|
170
|
+
max_time=300,
|
|
171
|
+
raise_on_giveup=False,
|
|
172
|
+
giveup=fatal_code,
|
|
173
|
+
)
|
|
159
174
|
def get_url(url):
|
|
160
175
|
return requests.get(url)
|
|
161
176
|
|
|
@@ -175,7 +190,11 @@ return value of the target function is the empty list:
|
|
|
175
190
|
|
|
176
191
|
.. code-block:: python
|
|
177
192
|
|
|
178
|
-
@backoff.on_predicate(
|
|
193
|
+
@backoff.on_predicate(
|
|
194
|
+
backoff.fibo,
|
|
195
|
+
lambda x: x == [],
|
|
196
|
+
max_value=13,
|
|
197
|
+
)
|
|
179
198
|
def poll_for_messages(queue):
|
|
180
199
|
return queue.get()
|
|
181
200
|
|
|
@@ -197,11 +216,15 @@ gets a non-falsey result could be defined like like this:
|
|
|
197
216
|
|
|
198
217
|
.. code-block:: python
|
|
199
218
|
|
|
200
|
-
@backoff.on_predicate(
|
|
219
|
+
@backoff.on_predicate(
|
|
220
|
+
backoff.constant,
|
|
221
|
+
jitter=None,
|
|
222
|
+
interval=1,
|
|
223
|
+
)
|
|
201
224
|
def poll_for_message(queue):
|
|
202
225
|
return queue.get()
|
|
203
226
|
|
|
204
|
-
The jitter is disabled in order to keep the polling frequency fixed.
|
|
227
|
+
The jitter is disabled in order to keep the polling frequency fixed.
|
|
205
228
|
|
|
206
229
|
@backoff.runtime
|
|
207
230
|
----------------
|
|
@@ -250,12 +273,16 @@ backoff behavior for different cases:
|
|
|
250
273
|
.. code-block:: python
|
|
251
274
|
|
|
252
275
|
@backoff.on_predicate(backoff.fibo, max_value=13)
|
|
253
|
-
@backoff.on_exception(
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
276
|
+
@backoff.on_exception(
|
|
277
|
+
backoff.expo,
|
|
278
|
+
requests.exceptions.HTTPError,
|
|
279
|
+
max_time=60,
|
|
280
|
+
)
|
|
281
|
+
@backoff.on_exception(
|
|
282
|
+
backoff.expo,
|
|
283
|
+
requests.exceptions.Timeout,
|
|
284
|
+
max_time=300,
|
|
285
|
+
)
|
|
259
286
|
def poll_for_message(queue):
|
|
260
287
|
return queue.get()
|
|
261
288
|
|
|
@@ -278,9 +305,13 @@ runtime to obtain the value:
|
|
|
278
305
|
# and that it has a dictionary-like 'config' property
|
|
279
306
|
return app.config["BACKOFF_MAX_TIME"]
|
|
280
307
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
308
|
+
|
|
309
|
+
@backoff.on_exception(
|
|
310
|
+
backoff.expo,
|
|
311
|
+
ValueError,
|
|
312
|
+
max_time=lookup_max_time,
|
|
313
|
+
)
|
|
314
|
+
def my_function(): ...
|
|
284
315
|
|
|
285
316
|
Event handlers
|
|
286
317
|
--------------
|
|
@@ -308,13 +339,18 @@ implemented like so:
|
|
|
308
339
|
.. code-block:: python
|
|
309
340
|
|
|
310
341
|
def backoff_hdlr(details):
|
|
311
|
-
print
|
|
312
|
-
|
|
313
|
-
|
|
342
|
+
print(
|
|
343
|
+
"Backing off {wait:0.1f} seconds after {tries} tries "
|
|
344
|
+
"calling function {target} with args {args} and kwargs "
|
|
345
|
+
"{kwargs}".format(**details)
|
|
346
|
+
)
|
|
347
|
+
|
|
314
348
|
|
|
315
|
-
@backoff.on_exception(
|
|
316
|
-
|
|
317
|
-
|
|
349
|
+
@backoff.on_exception(
|
|
350
|
+
backoff.expo,
|
|
351
|
+
requests.exceptions.RequestException,
|
|
352
|
+
on_backoff=backoff_hdlr,
|
|
353
|
+
)
|
|
318
354
|
def get_url(url):
|
|
319
355
|
return requests.get(url)
|
|
320
356
|
|
|
@@ -326,9 +362,14 @@ handler functions as the value of the ``on_backoff`` keyword arg:
|
|
|
326
362
|
|
|
327
363
|
.. code-block:: python
|
|
328
364
|
|
|
329
|
-
@backoff.on_exception(
|
|
330
|
-
|
|
331
|
-
|
|
365
|
+
@backoff.on_exception(
|
|
366
|
+
backoff.expo,
|
|
367
|
+
requests.exceptions.RequestException,
|
|
368
|
+
on_backoff=[
|
|
369
|
+
backoff_hdlr1,
|
|
370
|
+
backoff_hdlr2,
|
|
371
|
+
],
|
|
372
|
+
)
|
|
332
373
|
def get_url(url):
|
|
333
374
|
return requests.get(url)
|
|
334
375
|
|
|
@@ -359,9 +400,13 @@ asynchronous HTTP client/server library.
|
|
|
359
400
|
|
|
360
401
|
.. code-block:: python
|
|
361
402
|
|
|
362
|
-
@backoff.on_exception(
|
|
403
|
+
@backoff.on_exception(
|
|
404
|
+
backoff.expo,
|
|
405
|
+
aiohttp.ClientError,
|
|
406
|
+
max_time=60,
|
|
407
|
+
)
|
|
363
408
|
async def get_url(url):
|
|
364
|
-
async with aiohttp.ClientSession(raise_for_status=True) as session:
|
|
409
|
+
async with aiohttp.ClientSession(raise_for_status=True) as session: # noqa: SIM117
|
|
365
410
|
async with session.get(url) as response:
|
|
366
411
|
return await response.text()
|
|
367
412
|
|
|
@@ -376,7 +421,7 @@ as:
|
|
|
376
421
|
|
|
377
422
|
.. code-block:: python
|
|
378
423
|
|
|
379
|
-
logging.getLogger(
|
|
424
|
+
logging.getLogger("backoff").addHandler(logging.StreamHandler())
|
|
380
425
|
|
|
381
426
|
The default logging level is INFO, which corresponds to logging
|
|
382
427
|
anytime a retry event occurs. If you would instead like to log
|
|
@@ -384,7 +429,7 @@ only when a giveup event occurs, set the logger level to ERROR.
|
|
|
384
429
|
|
|
385
430
|
.. code-block:: python
|
|
386
431
|
|
|
387
|
-
logging.getLogger(
|
|
432
|
+
logging.getLogger("backoff").setLevel(logging.ERROR)
|
|
388
433
|
|
|
389
434
|
It is also possible to specify an alternate logger with the ``logger``
|
|
390
435
|
keyword argument. If a string value is specified the logger will be
|
|
@@ -392,25 +437,30 @@ looked up by name.
|
|
|
392
437
|
|
|
393
438
|
.. code-block:: python
|
|
394
439
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
440
|
+
@backoff.on_exception(
|
|
441
|
+
backoff.expo,
|
|
442
|
+
requests.exceptions.RequestException,
|
|
443
|
+
logger="my_logger",
|
|
444
|
+
)
|
|
445
|
+
def my_function(): ...
|
|
399
446
|
|
|
400
447
|
It is also supported to specify a Logger (or LoggerAdapter) object
|
|
401
448
|
directly.
|
|
402
449
|
|
|
403
450
|
.. code-block:: python
|
|
404
451
|
|
|
405
|
-
my_logger = logging.getLogger(
|
|
452
|
+
my_logger = logging.getLogger("my_logger")
|
|
406
453
|
my_handler = logging.StreamHandler()
|
|
407
454
|
my_logger.addHandler(my_handler)
|
|
408
455
|
my_logger.setLevel(logging.ERROR)
|
|
409
456
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
457
|
+
|
|
458
|
+
@backoff.on_exception(
|
|
459
|
+
backoff.expo,
|
|
460
|
+
requests.exceptions.RequestException,
|
|
461
|
+
logger=my_logger,
|
|
462
|
+
)
|
|
463
|
+
def my_function(): ...
|
|
414
464
|
|
|
415
465
|
Default logging can be disabled all together by specifying
|
|
416
466
|
``logger=None``. In this case, if desired alternative logging behavior
|
|
@@ -26,7 +26,7 @@ for asynchronous code.
|
|
|
26
26
|
Examples
|
|
27
27
|
========
|
|
28
28
|
|
|
29
|
-
Since Kenneth Reitz's `requests <
|
|
29
|
+
Since Kenneth Reitz's `requests <https://requests.readthedocs.io>`_ module
|
|
30
30
|
has become a defacto standard for synchronous HTTP clients in Python,
|
|
31
31
|
networking examples below are written using it, but it is in no way required
|
|
32
32
|
by the backoff module.
|
|
@@ -40,8 +40,10 @@ is raised. Here's an example using exponential backoff when any
|
|
|
40
40
|
|
|
41
41
|
.. code-block:: python
|
|
42
42
|
|
|
43
|
-
@backoff.on_exception(
|
|
44
|
-
|
|
43
|
+
@backoff.on_exception(
|
|
44
|
+
backoff.expo,
|
|
45
|
+
requests.exceptions.RequestException,
|
|
46
|
+
)
|
|
45
47
|
def get_url(url):
|
|
46
48
|
return requests.get(url)
|
|
47
49
|
|
|
@@ -50,9 +52,13 @@ the same backoff behavior is desired for more than one exception type:
|
|
|
50
52
|
|
|
51
53
|
.. code-block:: python
|
|
52
54
|
|
|
53
|
-
@backoff.on_exception(
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
@backoff.on_exception(
|
|
56
|
+
backoff.expo,
|
|
57
|
+
(
|
|
58
|
+
requests.exceptions.Timeout,
|
|
59
|
+
requests.exceptions.ConnectionError,
|
|
60
|
+
),
|
|
61
|
+
)
|
|
56
62
|
def get_url(url):
|
|
57
63
|
return requests.get(url)
|
|
58
64
|
|
|
@@ -66,9 +72,11 @@ of total time in seconds that can elapse before giving up.
|
|
|
66
72
|
|
|
67
73
|
.. code-block:: python
|
|
68
74
|
|
|
69
|
-
@backoff.on_exception(
|
|
70
|
-
|
|
71
|
-
|
|
75
|
+
@backoff.on_exception(
|
|
76
|
+
backoff.expo,
|
|
77
|
+
requests.exceptions.RequestException,
|
|
78
|
+
max_time=60,
|
|
79
|
+
)
|
|
72
80
|
def get_url(url):
|
|
73
81
|
return requests.get(url)
|
|
74
82
|
|
|
@@ -78,10 +86,12 @@ to make to the target function before giving up.
|
|
|
78
86
|
|
|
79
87
|
.. code-block:: python
|
|
80
88
|
|
|
81
|
-
@backoff.on_exception(
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
89
|
+
@backoff.on_exception(
|
|
90
|
+
backoff.expo,
|
|
91
|
+
requests.exceptions.RequestException,
|
|
92
|
+
max_tries=8,
|
|
93
|
+
jitter=None,
|
|
94
|
+
)
|
|
85
95
|
def get_url(url):
|
|
86
96
|
return requests.get(url)
|
|
87
97
|
|
|
@@ -97,10 +107,13 @@ be retried:
|
|
|
97
107
|
def fatal_code(e):
|
|
98
108
|
return 400 <= e.response.status_code < 500
|
|
99
109
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
110
|
+
|
|
111
|
+
@backoff.on_exception(
|
|
112
|
+
backoff.expo,
|
|
113
|
+
requests.exceptions.RequestException,
|
|
114
|
+
max_time=300,
|
|
115
|
+
giveup=fatal_code,
|
|
116
|
+
)
|
|
104
117
|
def get_url(url):
|
|
105
118
|
return requests.get(url)
|
|
106
119
|
|
|
@@ -118,11 +131,14 @@ case, regardless of the logic in the `on_exception` handler.
|
|
|
118
131
|
def fatal_code(e):
|
|
119
132
|
return 400 <= e.response.status_code < 500
|
|
120
133
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
134
|
+
|
|
135
|
+
@backoff.on_exception(
|
|
136
|
+
backoff.expo,
|
|
137
|
+
requests.exceptions.RequestException,
|
|
138
|
+
max_time=300,
|
|
139
|
+
raise_on_giveup=False,
|
|
140
|
+
giveup=fatal_code,
|
|
141
|
+
)
|
|
126
142
|
def get_url(url):
|
|
127
143
|
return requests.get(url)
|
|
128
144
|
|
|
@@ -142,7 +158,11 @@ return value of the target function is the empty list:
|
|
|
142
158
|
|
|
143
159
|
.. code-block:: python
|
|
144
160
|
|
|
145
|
-
@backoff.on_predicate(
|
|
161
|
+
@backoff.on_predicate(
|
|
162
|
+
backoff.fibo,
|
|
163
|
+
lambda x: x == [],
|
|
164
|
+
max_value=13,
|
|
165
|
+
)
|
|
146
166
|
def poll_for_messages(queue):
|
|
147
167
|
return queue.get()
|
|
148
168
|
|
|
@@ -164,11 +184,15 @@ gets a non-falsey result could be defined like like this:
|
|
|
164
184
|
|
|
165
185
|
.. code-block:: python
|
|
166
186
|
|
|
167
|
-
@backoff.on_predicate(
|
|
187
|
+
@backoff.on_predicate(
|
|
188
|
+
backoff.constant,
|
|
189
|
+
jitter=None,
|
|
190
|
+
interval=1,
|
|
191
|
+
)
|
|
168
192
|
def poll_for_message(queue):
|
|
169
193
|
return queue.get()
|
|
170
194
|
|
|
171
|
-
The jitter is disabled in order to keep the polling frequency fixed.
|
|
195
|
+
The jitter is disabled in order to keep the polling frequency fixed.
|
|
172
196
|
|
|
173
197
|
@backoff.runtime
|
|
174
198
|
----------------
|
|
@@ -217,12 +241,16 @@ backoff behavior for different cases:
|
|
|
217
241
|
.. code-block:: python
|
|
218
242
|
|
|
219
243
|
@backoff.on_predicate(backoff.fibo, max_value=13)
|
|
220
|
-
@backoff.on_exception(
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
244
|
+
@backoff.on_exception(
|
|
245
|
+
backoff.expo,
|
|
246
|
+
requests.exceptions.HTTPError,
|
|
247
|
+
max_time=60,
|
|
248
|
+
)
|
|
249
|
+
@backoff.on_exception(
|
|
250
|
+
backoff.expo,
|
|
251
|
+
requests.exceptions.Timeout,
|
|
252
|
+
max_time=300,
|
|
253
|
+
)
|
|
226
254
|
def poll_for_message(queue):
|
|
227
255
|
return queue.get()
|
|
228
256
|
|
|
@@ -245,9 +273,13 @@ runtime to obtain the value:
|
|
|
245
273
|
# and that it has a dictionary-like 'config' property
|
|
246
274
|
return app.config["BACKOFF_MAX_TIME"]
|
|
247
275
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
276
|
+
|
|
277
|
+
@backoff.on_exception(
|
|
278
|
+
backoff.expo,
|
|
279
|
+
ValueError,
|
|
280
|
+
max_time=lookup_max_time,
|
|
281
|
+
)
|
|
282
|
+
def my_function(): ...
|
|
251
283
|
|
|
252
284
|
Event handlers
|
|
253
285
|
--------------
|
|
@@ -275,13 +307,18 @@ implemented like so:
|
|
|
275
307
|
.. code-block:: python
|
|
276
308
|
|
|
277
309
|
def backoff_hdlr(details):
|
|
278
|
-
print
|
|
279
|
-
|
|
280
|
-
|
|
310
|
+
print(
|
|
311
|
+
"Backing off {wait:0.1f} seconds after {tries} tries "
|
|
312
|
+
"calling function {target} with args {args} and kwargs "
|
|
313
|
+
"{kwargs}".format(**details)
|
|
314
|
+
)
|
|
315
|
+
|
|
281
316
|
|
|
282
|
-
@backoff.on_exception(
|
|
283
|
-
|
|
284
|
-
|
|
317
|
+
@backoff.on_exception(
|
|
318
|
+
backoff.expo,
|
|
319
|
+
requests.exceptions.RequestException,
|
|
320
|
+
on_backoff=backoff_hdlr,
|
|
321
|
+
)
|
|
285
322
|
def get_url(url):
|
|
286
323
|
return requests.get(url)
|
|
287
324
|
|
|
@@ -293,9 +330,14 @@ handler functions as the value of the ``on_backoff`` keyword arg:
|
|
|
293
330
|
|
|
294
331
|
.. code-block:: python
|
|
295
332
|
|
|
296
|
-
@backoff.on_exception(
|
|
297
|
-
|
|
298
|
-
|
|
333
|
+
@backoff.on_exception(
|
|
334
|
+
backoff.expo,
|
|
335
|
+
requests.exceptions.RequestException,
|
|
336
|
+
on_backoff=[
|
|
337
|
+
backoff_hdlr1,
|
|
338
|
+
backoff_hdlr2,
|
|
339
|
+
],
|
|
340
|
+
)
|
|
299
341
|
def get_url(url):
|
|
300
342
|
return requests.get(url)
|
|
301
343
|
|
|
@@ -326,9 +368,13 @@ asynchronous HTTP client/server library.
|
|
|
326
368
|
|
|
327
369
|
.. code-block:: python
|
|
328
370
|
|
|
329
|
-
@backoff.on_exception(
|
|
371
|
+
@backoff.on_exception(
|
|
372
|
+
backoff.expo,
|
|
373
|
+
aiohttp.ClientError,
|
|
374
|
+
max_time=60,
|
|
375
|
+
)
|
|
330
376
|
async def get_url(url):
|
|
331
|
-
async with aiohttp.ClientSession(raise_for_status=True) as session:
|
|
377
|
+
async with aiohttp.ClientSession(raise_for_status=True) as session: # noqa: SIM117
|
|
332
378
|
async with session.get(url) as response:
|
|
333
379
|
return await response.text()
|
|
334
380
|
|
|
@@ -343,7 +389,7 @@ as:
|
|
|
343
389
|
|
|
344
390
|
.. code-block:: python
|
|
345
391
|
|
|
346
|
-
logging.getLogger(
|
|
392
|
+
logging.getLogger("backoff").addHandler(logging.StreamHandler())
|
|
347
393
|
|
|
348
394
|
The default logging level is INFO, which corresponds to logging
|
|
349
395
|
anytime a retry event occurs. If you would instead like to log
|
|
@@ -351,7 +397,7 @@ only when a giveup event occurs, set the logger level to ERROR.
|
|
|
351
397
|
|
|
352
398
|
.. code-block:: python
|
|
353
399
|
|
|
354
|
-
logging.getLogger(
|
|
400
|
+
logging.getLogger("backoff").setLevel(logging.ERROR)
|
|
355
401
|
|
|
356
402
|
It is also possible to specify an alternate logger with the ``logger``
|
|
357
403
|
keyword argument. If a string value is specified the logger will be
|
|
@@ -359,25 +405,30 @@ looked up by name.
|
|
|
359
405
|
|
|
360
406
|
.. code-block:: python
|
|
361
407
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
408
|
+
@backoff.on_exception(
|
|
409
|
+
backoff.expo,
|
|
410
|
+
requests.exceptions.RequestException,
|
|
411
|
+
logger="my_logger",
|
|
412
|
+
)
|
|
413
|
+
def my_function(): ...
|
|
366
414
|
|
|
367
415
|
It is also supported to specify a Logger (or LoggerAdapter) object
|
|
368
416
|
directly.
|
|
369
417
|
|
|
370
418
|
.. code-block:: python
|
|
371
419
|
|
|
372
|
-
my_logger = logging.getLogger(
|
|
420
|
+
my_logger = logging.getLogger("my_logger")
|
|
373
421
|
my_handler = logging.StreamHandler()
|
|
374
422
|
my_logger.addHandler(my_handler)
|
|
375
423
|
my_logger.setLevel(logging.ERROR)
|
|
376
424
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
425
|
+
|
|
426
|
+
@backoff.on_exception(
|
|
427
|
+
backoff.expo,
|
|
428
|
+
requests.exceptions.RequestException,
|
|
429
|
+
logger=my_logger,
|
|
430
|
+
)
|
|
431
|
+
def my_function(): ...
|
|
381
432
|
|
|
382
433
|
Default logging can be disabled all together by specifying
|
|
383
434
|
``logger=None``. In this case, if desired alternative logging behavior
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
# coding:utf-8
|
|
2
1
|
"""
|
|
3
2
|
Function decoration for backoff and retry
|
|
4
3
|
|
|
@@ -12,20 +11,21 @@ polling resources for externally generated content.
|
|
|
12
11
|
For examples and full documentation see the README at
|
|
13
12
|
https://github.com/python-backoff/backoff
|
|
14
13
|
"""
|
|
14
|
+
|
|
15
15
|
from backoff._decorator import on_exception, on_predicate
|
|
16
16
|
from backoff._jitter import full_jitter, random_jitter
|
|
17
17
|
from backoff._wait_gen import constant, expo, fibo, runtime, decay
|
|
18
18
|
|
|
19
19
|
__all__ = [
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
20
|
+
"on_predicate",
|
|
21
|
+
"on_exception",
|
|
22
|
+
"constant",
|
|
23
|
+
"expo",
|
|
24
|
+
"decay",
|
|
25
|
+
"fibo",
|
|
26
|
+
"runtime",
|
|
27
|
+
"full_jitter",
|
|
28
|
+
"random_jitter",
|
|
29
29
|
]
|
|
30
30
|
|
|
31
|
-
__version__ = "2.
|
|
31
|
+
__version__ = "2.3.1"
|