diff --git a/.gitignore b/.gitignore index 5e524c3..209873e 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ .installed.cfg .Python .tox/ +.pytest_cache/ build/ coverage.xml develop-eggs/ diff --git a/AUTHORS b/AUTHORS index 3ae1286..256ed6f 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,5 +1,9 @@ +Richard O'Dwyer +Rémy Rémy Rémy Greinhofer +invl invlpg -Richard O'Dwyer +invlpg@gmail.com williara +TreantBG \ No newline at end of file diff --git a/ChangeLog b/ChangeLog index f537717..45c628c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,23 +1,48 @@ CHANGES ======= +* Add default return param +* Add configuration param +0.9.4 +----- + +* Bump version +0.9.3 +----- + +* Trim setup.py +* Move NullHandler to .compat +* Expose retry.retry\_call +* Relative import +* Move tests out of package +* Bump version +* Fix badges + 0.9.2 ----- +* Update ChangeLog +* Update AUTHORS +* Don't pin pytest +* tox: Add py35 +* Bump version * Updating requirements.txt to allow for any decorators >=3.4.2 0.9.1 ----- -* Fix dependency issues with other packages caused by explicit dep verions in requirements +* Updates setup.cfg version info +* Updates authors and changelog +* Adds version ranges to requirements, widening compatibility with other packages and their deps 0.9.0 ----- +* Fix typo in classifier * Add AUTHORS and ChangeLog files * Packaging the application using PBR * Update documentation -* Add retry_call function +* Add retry\_call function * Update tox.ini * Update requirements * Move the tests to the appropriate package @@ -35,7 +60,7 @@ CHANGES * v0.8.0 * dos2unix * Add argument jitter for retry() -* Add argument max_delay for retry() +* Add argument max\_delay for retry() * Refactor retry() 0.7.0 @@ -46,8 +71,8 @@ CHANGES * retry(): Update docstring * retry(): Change default tries to -1 * Move decorator() to .compat -* Add test_tries_minus1() -* Add test_tries_inf() +* Add test\_tries\_minus1() +* Add test\_tries\_inf() * Mock time.sleep in test case * Refactor retry() @@ -57,7 +82,7 @@ CHANGES * v0.6.0 * Fix inaccurate attempt counter * logger is now optional -* Extract logging_logger +* Extract logging\_logger * Make decorator module optional 0.5.0 @@ -74,14 +99,14 @@ CHANGES * Add tox.ini * Extract retry/api.py * Require pytest -* Add test_retry.py +* Add test\_retry.py * Added tag 0.4.2 for changeset 315f5f1229f6 0.4.2 ----- * Version 0.4.2 -* python2.6 support +* (untested) python2.6 support * README.rst: Add installation * Add classifiers * Add LICENSE @@ -89,7 +114,7 @@ CHANGES * Fix rST h1 h2 for README.rst * Add url * Add README.rst -* Ignore *.egg-info +* Ignore \*.egg-info * Ignore .env * Ignore .ropeproject * Ignore .git @@ -100,7 +125,7 @@ CHANGES * Version 0.4.1 * Add license -* Add long_description +* Add long\_description * Add docstring for retry() * Added tag 0.4.0 for changeset e053cae4b105 diff --git a/retry/api.py b/retry/api.py index 4a404b9..b826684 100644 --- a/retry/api.py +++ b/retry/api.py @@ -4,14 +4,15 @@ from functools import partial +from retry.retry_config import RetryConfig from .compat import decorator - logging_logger = logging.getLogger(__name__) +RISE_EXCEPTION = 'rise_exception' def __retry_internal(f, exceptions=Exception, tries=-1, delay=0, max_delay=None, backoff=1, jitter=0, - logger=logging_logger): + logger=logging_logger, default_return=RISE_EXCEPTION, configuration: RetryConfig = None): """ Executes a function and retries it if it failed. @@ -25,16 +26,25 @@ def __retry_internal(f, exceptions=Exception, tries=-1, delay=0, max_delay=None, fixed if a number, random if a range tuple (min, max) :param logger: logger.warning(fmt, error, delay) will be called on failed attempts. default: retry.logging_logger. if None, logging is disabled. + :param default_return: insted of rising exception return default value + :param configuration: pass configuration that can be changed dinamically :returns: the result of the f function. """ - _tries, _delay = tries, delay + + if configuration: + _tries, _delay = configuration.tries, configuration.delay + else: + _tries, _delay = tries, delay + while _tries: try: return f() except exceptions as e: _tries -= 1 if not _tries: - raise + if default_return is RISE_EXCEPTION: + raise + return default_return if logger is not None: logger.warning('%s, retrying in %s seconds...', e, _delay) @@ -51,7 +61,8 @@ def __retry_internal(f, exceptions=Exception, tries=-1, delay=0, max_delay=None, _delay = min(_delay, max_delay) -def retry(exceptions=Exception, tries=-1, delay=0, max_delay=None, backoff=1, jitter=0, logger=logging_logger): +def retry(exceptions=Exception, tries=-1, delay=0, max_delay=None, backoff=1, jitter=0, logger=logging_logger, + default_return=RISE_EXCEPTION, configuration=None): """Returns a retry decorator. :param exceptions: an exception or a tuple of exceptions to catch. default: Exception. @@ -63,6 +74,8 @@ def retry(exceptions=Exception, tries=-1, delay=0, max_delay=None, backoff=1, ji fixed if a number, random if a range tuple (min, max) :param logger: logger.warning(fmt, error, delay) will be called on failed attempts. default: retry.logging_logger. if None, logging is disabled. + :param default_return: insted of rising exception return default value + :param configuration: pass configuration that can be changed dinamically :returns: a retry decorator. """ @@ -71,14 +84,13 @@ def retry_decorator(f, *fargs, **fkwargs): args = fargs if fargs else list() kwargs = fkwargs if fkwargs else dict() return __retry_internal(partial(f, *args, **kwargs), exceptions, tries, delay, max_delay, backoff, jitter, - logger) + logger, default_return, configuration) return retry_decorator def retry_call(f, fargs=None, fkwargs=None, exceptions=Exception, tries=-1, delay=0, max_delay=None, backoff=1, - jitter=0, - logger=logging_logger): + jitter=0, logger=logging_logger, default_return=RISE_EXCEPTION, configuration=None): """ Calls a function and re-executes it if it failed. @@ -94,8 +106,11 @@ def retry_call(f, fargs=None, fkwargs=None, exceptions=Exception, tries=-1, dela fixed if a number, random if a range tuple (min, max) :param logger: logger.warning(fmt, error, delay) will be called on failed attempts. default: retry.logging_logger. if None, logging is disabled. + :param default_return: insted of rising exception return default value + :param configuration: pass configuration that can be changed dinamically :returns: the result of the f function. """ args = fargs if fargs else list() kwargs = fkwargs if fkwargs else dict() - return __retry_internal(partial(f, *args, **kwargs), exceptions, tries, delay, max_delay, backoff, jitter, logger) + return __retry_internal(partial(f, *args, **kwargs), exceptions, tries, delay, max_delay, backoff, jitter, logger, + default_return, configuration) diff --git a/retry/retry_config.py b/retry/retry_config.py new file mode 100644 index 0000000..8fe33c1 --- /dev/null +++ b/retry/retry_config.py @@ -0,0 +1,4 @@ +class RetryConfig: + def __init__(self): + self.tries = -1 + self.delay = 0 diff --git a/setup.cfg b/setup.cfg index d723671..e285fa1 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = retry -version = 0.9.3 +version = 0.9.4 author = invl author-email = invlpg@gmail.com summary = Easy to use retry decorator. diff --git a/tox.ini b/tox.ini index 2f183e5..d6d3c02 100644 --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,7 @@ # and then run "tox" from this directory. [tox] -envlist = flake8, py26, py27, py34, py35, pypy +envlist = flake8, py26, py27, py34, py35, py37, pypy [flake8] max-line-length = 120