Codebase list backoff / 5c4c8aa
Import upstream version 1.11.1 Kali Janitor 2 years ago
11 changed file(s) with 257 addition(s) and 43 deletion(s). Raw diff Collapse all Expand all
0 # For most projects, this workflow file will not need changing; you simply need
1 # to commit it to your repository.
2 #
3 # You may wish to alter this file to override the set of languages analyzed,
4 # or to provide custom queries or build logic.
5 name: "CodeQL"
6
7 on:
8 push:
9 branches: [master]
10 pull_request:
11 # The branches below must be a subset of the branches above
12 branches: [master]
13 schedule:
14 - cron: '0 5 * * 6'
15
16 jobs:
17 analyze:
18 name: Analyze
19 runs-on: ubuntu-latest
20
21 strategy:
22 fail-fast: false
23 matrix:
24 # Override automatic language detection by changing the below list
25 # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python']
26 language: ['python']
27 # Learn more...
28 # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection
29
30 steps:
31 - name: Checkout repository
32 uses: actions/checkout@v2
33 with:
34 # We must fetch at least the immediate parents so that if this is
35 # a pull request then we can checkout the head.
36 fetch-depth: 2
37
38 # If this run was triggered by a pull request event, then checkout
39 # the head of the pull request instead of the merge commit.
40 - run: git checkout HEAD^2
41 if: ${{ github.event_name == 'pull_request' }}
42
43 # Initializes the CodeQL tools for scanning.
44 - name: Initialize CodeQL
45 uses: github/codeql-action/init@v1
46 with:
47 languages: ${{ matrix.language }}
48 # If you wish to specify custom queries, you can do so here or in a config file.
49 # By default, queries listed here will override any specified in a config file.
50 # Prefix the list here with "+" to use these queries and those in the config file.
51 # queries: ./path/to/local/query, your-org/your-repo/queries@main
52
53 # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
54 # If this step fails, then you should remove it and run the build manually (see below)
55 - name: Autobuild
56 uses: github/codeql-action/autobuild@v1
57
58 # ℹī¸ Command-line programs to run using the OS shell.
59 # 📚 https://git.io/JvXDl
60
61 # ✏ī¸ If the Autobuild fails above, remove it and uncomment the following three lines
62 # and modify them (or add more) to build your code if your project
63 # uses a compiled language
64
65 #- run: |
66 # make bootstrap
67 # make release
68
69 - name: Perform CodeQL Analysis
70 uses: github/codeql-action/analyze@v1
44 - "3.6"
55 - "3.7"
66 - "3.8"
7 - "3.9"
78 matrix:
89 include:
910 - python: "3.5"
1415 env: PYTHONASYNCIODEBUG=x
1516 - python: "3.8"
1617 env: PYTHONASYNCIODEBUG=x
18 - python: "3.9"
19 env: PYTHONASYNCIODEBUG=x
1720
1821 before_install:
1922 - pip install poetry more-itertools
141141
142142 - Don't include tests and changelog in distribution
143143
144 ## [v1.10.0] 2019-12-7
144 ## [v1.10.0] 2019-12-07
145145 ### Changed
146146
147147 - Allow sync decorator call from async function
148
149 ## [v1.11.0] 2021-07-12
150 ### Changed
151
152 - Configurable logging levels for backoff and giveup events
153 - Minor documentation fixes
154
155 ### NOTE
156
157 THIS WILL BE THE FINAL PYTHON 2.7 COMPATIBLE RELEASE.
158
159 ## [v1.11.1] 2021-07-14
160 ### Changed
161
162 - Update __version__ in backoff module
1313
1414 flake8:
1515 ifeq ($(PY_GTE_35),1)
16 @flake8 backoff tests
16 @flake8 --ignore=E741,W503,W504 backoff tests
1717 else
18 @flake8 --exclude tests/python35,backoff/_async.py backoff tests
18 @flake8 --ignore=E741,W503,W504 --exclude tests/python35,backoff/_async.py backoff tests
1919 endif
2020
2121 clean:
228228 .. code-block:: python
229229
230230 def backoff_hdlr(details):
231 print ("Backing off {wait:0.1f} seconds afters {tries} tries "
231 print ("Backing off {wait:0.1f} seconds after {tries} tries "
232232 "calling function {target} with args {args} and kwargs "
233233 "{kwargs}".format(**details))
234234
279279
280280 @backoff.on_exception(backoff.expo, aiohttp.ClientError, max_time=60)
281281 async def get_url(url):
282 async with aiohttp.ClientSession() as session:
282 async with aiohttp.ClientSession(raise_for_status=True) as session:
283283 async with session.get(url) as response:
284284 return await response.text()
285285
311311 .. code-block:: python
312312
313313 @backoff.on_exception(backoff.expo,
314 requests.exception.RequestException,
314 requests.exceptions.RequestException,
315315 logger='my_logger')
316316 # ...
317317
322322
323323 my_logger = logging.getLogger('my_logger')
324324 my_handler = logging.StreamHandler()
325 my_logger.add_handler(my_handler)
325 my_logger.addHandler(my_handler)
326326 my_logger.setLevel(logging.ERROR)
327327
328328 @backoff.on_exception(backoff.expo,
329 requests.exception.RequestException,
330 logger=my_logger)
329 requests.exceptions.RequestException,
330 logger=my_logger)
331331 # ...
332332
333333 Default logging can be disabled all together by specifying
1111 For examples and full documentation see the README at
1212 https://github.com/litl/backoff
1313 """
14 import sys
15 import warnings
16
1417 from backoff._decorator import on_predicate, on_exception
1518 from backoff._jitter import full_jitter, random_jitter
1619 from backoff._wait_gen import constant, expo, fibo
2225 'expo',
2326 'fibo',
2427 'full_jitter',
25 'random_jitter'
28 'random_jitter',
2629 ]
2730
28 __version__ = '1.10.0'
31 __version__ = '1.11.1'
32
33
34 if sys.version_info[0] < 3:
35 warnings.warn(
36 "Python 2.7 support is deprecated and will be dropped "
37 "in the next release",
38 DeprecationWarning,
39 ) # pragma: no cover
44 import sys
55 import traceback
66 import warnings
7
8
9 # python 2.7 -> 3.x compatibility for str and unicode
10 try:
11 basestring
12 except NameError: # pragma: python=3.5
13 basestring = str
714
815
916 # Use module-specific logger with a default null handler.
4047
4148 seconds = value + jitter()
4249
43 # don't sleep longer than remaining alloted max_time
50 # don't sleep longer than remaining allotted max_time
4451 if max_time is not None:
4552 seconds = min(seconds, max_time - elapsed)
4653
4754 return seconds
4855
4956
57 def _prepare_logger(logger):
58 if isinstance(logger, basestring):
59 logger = logging.getLogger(logger)
60 return logger
61
62
5063 # Configure handler list with user specified handler and optionally
5164 # with a default handler bound to the specified logger.
52 def _config_handlers(user_handlers, default_handler=None, logger=None):
65 def _config_handlers(
66 user_handlers, default_handler=None, logger=None, log_level=None
67 ):
5368 handlers = []
5469 if logger is not None:
70 assert log_level is not None, "Log level is not specified"
5571 # bind the specified logger to the default log handler
56 log_handler = functools.partial(default_handler, logger=logger)
72 log_handler = functools.partial(
73 default_handler, logger=logger, log_level=log_level
74 )
5775 handlers.append(log_handler)
5876
5977 if user_handlers is None:
7290
7391
7492 # Default backoff handler
75 def _log_backoff(details, logger):
93 def _log_backoff(details, logger, log_level):
7694 msg = "Backing off %s(...) for %.1fs (%s)"
7795 log_args = [details['target'].__name__, details['wait']]
7896
82100 log_args.append(exc_fmt.rstrip("\n"))
83101 else:
84102 log_args.append(details['value'])
85 logger.info(msg, *log_args)
103 logger.log(log_level, msg, *log_args)
86104
87105
88106 # Default giveup handler
89 def _log_giveup(details, logger):
107 def _log_giveup(details, logger, log_level):
90108 msg = "Giving up %s(...) after %d tries (%s)"
91109 log_args = [details['target'].__name__, details['tries']]
92110
97115 else:
98116 log_args.append(details['value'])
99117
100 logger.error(msg, *log_args)
118 logger.log(log_level, msg, *log_args)
44 import operator
55 import sys
66
7 from backoff._common import (_config_handlers, _log_backoff, _log_giveup)
7 from backoff._common import (
8 _prepare_logger,
9 _config_handlers,
10 _log_backoff,
11 _log_giveup
12 )
813 from backoff._jitter import full_jitter
914 from backoff import _sync
10
11
12 # python 2.7 -> 3.x compatibility for str and unicode
13 try:
14 basestring
15 except NameError: # pragma: python=3.5
16 basestring = str
1715
1816
1917 def on_predicate(wait_gen,
2523 on_backoff=None,
2624 on_giveup=None,
2725 logger='backoff',
26 backoff_log_level=logging.INFO,
27 giveup_log_level=logging.ERROR,
2828 **wait_gen_kwargs):
2929 """Returns decorator for backoff and retry triggered by predicate.
3030
6262 about the invocation.
6363 logger: Name of logger or Logger object to log to. Defaults to
6464 'backoff'.
65 backoff_log_level: log level for the backoff event. Defaults to "INFO"
66 giveup_log_level: log level for the give up event. Defaults to "ERROR"
6567 **wait_gen_kwargs: Any additional keyword args specified will be
6668 passed to wait_gen when it is initialized. Any callable
6769 args will first be evaluated and their return values passed.
6971 """
7072 def decorate(target):
7173 # change names because python 2.x doesn't have nonlocal
72 logger_ = logger
73 if isinstance(logger_, basestring):
74 logger_ = logging.getLogger(logger_)
74 logger_ = _prepare_logger(logger)
75
7576 on_success_ = _config_handlers(on_success)
76 on_backoff_ = _config_handlers(on_backoff, _log_backoff, logger_)
77 on_giveup_ = _config_handlers(on_giveup, _log_giveup, logger_)
77 on_backoff_ = _config_handlers(
78 on_backoff, _log_backoff, logger_, backoff_log_level
79 )
80 on_giveup_ = _config_handlers(
81 on_giveup, _log_giveup, logger_, giveup_log_level
82 )
7883
7984 retry = None
8085 if sys.version_info >= (3, 5): # pragma: python=3.5
106111 on_backoff=None,
107112 on_giveup=None,
108113 logger='backoff',
114 backoff_log_level=logging.INFO,
115 giveup_log_level=logging.ERROR,
109116 **wait_gen_kwargs):
110117 """Returns decorator for backoff and retry triggered by exception.
111118
116123 backoff.
117124 max_tries: The maximum number of attempts to make before giving
118125 up. Once exhausted, the exception will be allowed to escape.
119 The default value of None means their is no limit to the
126 The default value of None means there is no limit to the
120127 number of tries. If a callable is passed, it will be
121128 evaluated at runtime and its return value used.
122129 max_time: The maximum total amount of time to try for before
143150 is exceeded. The parameter is a dict containing details
144151 about the invocation.
145152 logger: Name or Logger object to log to. Defaults to 'backoff'.
153 backoff_log_level: log level for the backoff event. Defaults to "INFO"
154 giveup_log_level: log level for the give up event. Defaults to "ERROR"
146155 **wait_gen_kwargs: Any additional keyword args specified will be
147156 passed to wait_gen when it is initialized. Any callable
148157 args will first be evaluated and their return values passed.
150159 """
151160 def decorate(target):
152161 # change names because python 2.x doesn't have nonlocal
153 logger_ = logger
154 if isinstance(logger_, basestring):
155 logger_ = logging.getLogger(logger_)
162 logger_ = _prepare_logger(logger)
163
156164 on_success_ = _config_handlers(on_success)
157 on_backoff_ = _config_handlers(on_backoff, _log_backoff, logger_)
158 on_giveup_ = _config_handlers(on_giveup, _log_giveup, logger_)
165 on_backoff_ = _config_handlers(
166 on_backoff, _log_backoff, logger_, backoff_log_level
167 )
168 on_giveup_ = _config_handlers(
169 on_giveup, _log_giveup, logger_, giveup_log_level
170 )
159171
160172 retry = None
161173 if sys.version_info[:2] >= (3, 5): # pragma: python=3.5
77
88 Args:
99 base: The mathematical base of the exponentiation operation
10 factor: Factor to multiply the exponentation by.
10 factor: Factor to multiply the exponentiation by.
1111 max_value: The maximum value to yield. Once the value in the
1212 true exponential sequence exceeds this, the value
1313 of max_value will forever after be yielded.
00 [tool.poetry]
11 name = "backoff"
2 version = "1.10.0"
2 version = "1.11.1"
33 description = "Function decoration for backoff and retry"
4 authors = ["Bob Green <[email protected]>"]
4 authors = ["Bob Green <[email protected]>"]
55 readme = "README.rst"
66 repository = "https://github.com/litl/backoff"
77 license = "MIT"
2020 'Programming Language :: Python :: 3.6',
2121 'Programming Language :: Python :: 3.7',
2222 'Programming Language :: Python :: 3.8',
23 'Topic :: Internet :: WWW/HTTP',
23 'Programming Language :: Python :: 3.9',
24 'Topic :: Internet :: WWW/HTTP',
2425 'Topic :: Software Development :: Libraries :: Python Modules',
2526 'Topic :: Utilities']
2627 packages = [
3536 pytest = "^4.0"
3637 pytest-cov = "^2.6"
3738 pytest-asyncio = {version = "^0.10.0",python = "^3.5"}
39 autopep8 = "^1.5.7"
3840
3941 [build-system]
40 requires = ["poetry>=0.12"]
41 build-backend = "poetry.masonry.api"
42 requires = ["poetry-core"]
43 build-backend = "poetry.core.masonry.api"
00 # coding:utf-8
11 import datetime
2 import itertools
23 import logging
34 import random
5 import re
46 import sys
57 import threading
8 import unittest.mock
69
710 import pytest
811
732735 assert len(caplog.records) == 3 # 2 backoffs and 1 giveup
733736 for record in caplog.records:
734737 assert record.name == 'my-logger'
738
739
740 def _on_exception_factory(
741 backoff_log_level, giveup_log_level, max_tries
742 ):
743 @backoff.on_exception(
744 backoff.expo,
745 ValueError,
746 max_tries=max_tries,
747 backoff_log_level=backoff_log_level,
748 giveup_log_level=giveup_log_level,
749 )
750 def value_error():
751 raise ValueError
752
753 def func():
754 with pytest.raises(ValueError):
755 value_error()
756
757 return func
758
759
760 def _on_predicate_factory(
761 backoff_log_level, giveup_log_level, max_tries
762 ):
763 @backoff.on_predicate(
764 backoff.expo,
765 max_tries=max_tries,
766 backoff_log_level=backoff_log_level,
767 giveup_log_level=giveup_log_level,
768 )
769 def func():
770 return False
771
772 return func
773
774
775 @pytest.mark.parametrize(
776 ("func_factory", "backoff_log_level", "giveup_log_level"),
777 (
778 (factory, backoff_log_level, giveup_log_level)
779 for backoff_log_level, giveup_log_level in itertools.product(
780 (
781 logging.DEBUG,
782 logging.INFO,
783 logging.WARNING,
784 logging.ERROR,
785 logging.CRITICAL,
786 ),
787 repeat=2,
788 )
789 for factory in (_on_predicate_factory, _on_exception_factory)
790 )
791 )
792 def test_event_log_levels(
793 caplog, func_factory, backoff_log_level, giveup_log_level
794 ):
795 max_tries = 3
796 func = func_factory(backoff_log_level, giveup_log_level, max_tries)
797
798 with unittest.mock.patch('time.sleep', return_value=None):
799 with caplog.at_level(
800 min(backoff_log_level, giveup_log_level), logger="backoff"
801 ):
802 func()
803
804 backoff_re = re.compile("backing off", re.IGNORECASE)
805 giveup_re = re.compile("giving up", re.IGNORECASE)
806
807 backoff_log_count = 0
808 giveup_log_count = 0
809 for logger_name, level, message in caplog.record_tuples:
810 if level == backoff_log_level and backoff_re.match(message):
811 backoff_log_count += 1
812 elif level == giveup_log_level and giveup_re.match(message):
813 giveup_log_count += 1
814
815 assert backoff_log_count == max_tries - 1
816 assert giveup_log_count == 1