Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-tenacity for openSUSE:Factory checked in at 2022-11-12 17:41:15 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-tenacity (Old) and /work/SRC/openSUSE:Factory/.python-tenacity.new.1597 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-tenacity" Sat Nov 12 17:41:15 2022 rev:16 rq:1035302 version:8.1.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-tenacity/python-tenacity.changes 2022-11-01 13:43:11.828096823 +0100 +++ /work/SRC/openSUSE:Factory/.python-tenacity.new.1597/python-tenacity.changes 2022-11-12 17:41:38.114334474 +0100 @@ -1,0 +2,59 @@ +Fri Nov 11 13:21:11 UTC 2022 - pgaj...@suse.com + +- silent rpmlint + +------------------------------------------------------------------- +Fri Nov 11 11:38:22 UTC 2022 - pgaj...@suse.com + +- python-six is not required + +------------------------------------------------------------------- +Wed Nov 9 19:19:10 UTC 2022 - Yogalakshmi Arunachalam <yarunacha...@suse.com> + +- Update to version 8.1.0 (included changes also from 8.0.1, 8.0.0, 7.0.0) + * feat: Add retry_if_exception_cause_type (#362) + * Support `datetime.timedelta` as a valid wait unit type (#342) + * Show All Exception Predicates in Docs (#332) + * Implement a wait.wait_exponential_jitter per Google's storage guide (#351) + * Define a ClientError to fix Sphinx build errors (#352) + * Merge pull request #340 from jd/fix-mergify + * ci: fix Mergify config + * Merge pull request #337 from william-silversmith/master + * docs: show how to use retry_if_not_exception_type + * Rickroll a function name in one of the examples (#331) + * Fix after_log logger format (#317) + * Drop `py2` tag from the wheel name (#320) + * Add a __repr__ method to RetryCallState objects (#302) + * Add type annotations to cover all code. (#315) + * Fix #307 : Drop deprecated APIs (#314) + * Fix DeprecationWarnings in tests (#313) + * Do not package tests with tenacity (#308) + * Fix issue #288 : __name__ and other attributes for async functions (#312) + * Use f-strings instead of `str.format` as faster and easier to read (#311) + * Replace abc.ABCMeta with abc.ABC (#310) + * Remove encoding declarations (#309) + * Merge pull request #306 from penguinolog/black_full_ci + * Use black instead of "flake8-black" on CI. + * Fix #291: drop python < 3.6 (#304) + * Merge pull request #293 from and-semakin/drop_deprecated_pythons + * Drop support for deprecated Python versions (2.7 and 3.5) + * Add retry_if_not_exception_type() (#300) + * Make logger more compatible (#294) + * ci: fix pep8 error (#297) + * Merge pull request #286 from jd/reno-mergify + * Add Python 3.8 in tox.ini + * Fix upload job + * Merge pull request #202 from jd/release-auto + * Release via CircleCI + * Merge pull request #191 from bersace/context-manager + * Merge branch 'master' into context-manager + * Merge pull request #201 from bersace/async + * Ensure coroutine passes asyncio.iscoroutinefunction + * Test async keyword + * Retry code block with context manager + * Merge pull request #200 from Dectinc/master + * Fix setup.cfg typo + * Merge pull request #198 from BATS/py2-compatability-for-_asyncio-module + * Ensure that the _asyncio.py module can be compiled, even on Python 2 + +------------------------------------------------------------------- @@ -4 +63 @@ -- Update to version 8.1.0 +- Update to version 8.0.1 Old: ---- tenacity-8.0.1.tar.gz New: ---- tenacity-8.1.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-tenacity.spec ++++++ --- /var/tmp/diff_new_pack.y40xvu/_old 2022-11-12 17:41:38.594337332 +0100 +++ /var/tmp/diff_new_pack.y40xvu/_new 2022-11-12 17:41:38.594337332 +0100 @@ -17,7 +17,7 @@ Name: python-tenacity -Version: 8.0.1 +Version: 8.1.0 Release: 0 Summary: Python module for retrying code until it succeeeds License: Apache-2.0 @@ -27,13 +27,11 @@ BuildRequires: %{python_module pytest} BuildRequires: %{python_module setuptools_scm} BuildRequires: %{python_module setuptools} -BuildRequires: %{python_module six >= 1.9.0} BuildRequires: %{python_module tornado} BuildRequires: %{python_module typeguard} BuildRequires: %{python_module typing-extensions} BuildRequires: fdupes BuildRequires: python-rpm-macros -Requires: python-six >= 1.9.0 Recommends: python-tornado BuildArch: noarch %python_subpackages @@ -66,6 +64,7 @@ %files %{python_files} %license LICENSE %doc README.rst -%{python_sitelib}/* +%{python_sitelib}/tenacity +%{python_sitelib}/tenacity-*egg-info %changelog ++++++ tenacity-8.0.1.tar.gz -> tenacity-8.1.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tenacity-8.0.1/.mergify.yml new/tenacity-8.1.0/.mergify.yml --- old/tenacity-8.0.1/.mergify.yml 2021-06-30 14:37:02.000000000 +0200 +++ new/tenacity-8.1.0/.mergify.yml 2022-09-21 14:23:59.000000000 +0200 @@ -1,3 +1,13 @@ +queue_rules: + - name: default + conditions: + - "status-success=ci/circleci: pep8" + - "status-success=ci/circleci: black" + - "status-success=ci/circleci: py36" + - "status-success=ci/circleci: py37" + - "status-success=ci/circleci: py38" + - "status-success=ci/circleci: py39" + pull_request_rules: - name: warn on no changelog conditions: @@ -21,8 +31,8 @@ - "#approved-reviews-by>=1" - label=no-changelog actions: - merge: - strict: "smart" + queue: + name: default method: squash - name: automatic merge with changelog conditions: @@ -35,8 +45,8 @@ - "#approved-reviews-by>=1" - files~=^releasenotes/notes/ actions: - merge: - strict: "smart" + queue: + name: default method: squash - name: automatic merge for jd without changelog conditions: @@ -49,8 +59,8 @@ - "status-success=ci/circleci: py39" - label=no-changelog actions: - merge: - strict: "smart" + queue: + name: default method: squash - name: automatic merge for jd with changelog conditions: @@ -63,8 +73,8 @@ - "status-success=ci/circleci: py39" - files~=^releasenotes/notes/ actions: - merge: - strict: "smart" + queue: + name: default method: squash - name: dismiss reviews conditions: [] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tenacity-8.0.1/AUTHORS new/tenacity-8.1.0/AUTHORS --- old/tenacity-8.0.1/AUTHORS 2019-08-16 10:42:50.000000000 +0200 +++ new/tenacity-8.1.0/AUTHORS 1970-01-01 01:00:00.000000000 +0100 @@ -1,46 +0,0 @@ -Alex Kuang <aku...@bizo.com> -Alexey <forestbii...@gmail.com> -Boden R <boden...@gmail.com> -Brian Pandola <bpand...@hsdp.io> -Brian Williams <brian.willi...@actifio.com> -Brian Williams <briancmwilli...@gmail.com> -Brian-Williams <brian-willi...@users.noreply.github.com> -Cyrus Durgin <cy...@statelessnetworks.com> -Daniel Bennett <daniel.benn...@wpengine.com> -Daniel Nephin <dnep...@yelp.com> -Dave Hirschfeld <dave.hirschf...@gmail.com> -Derek Wilson <dwil...@domaintools.com> -Elisey Zanko <elisey.za...@gmail.com> -Gevorg Davoian <gdavo...@mirantis.com> -Hamish Downer <ham...@foobacca.co.uk> -Hannes Gräuler <han...@smasi.de> -Haïkel Guémar <hgue...@fedoraproject.org> -Hugo <hug...@users.noreply.github.com> -Jaye Doepke <jdoe...@mintel.com> -Jonathan Herriott <jherri...@bitcasa.com> -Joshua Harlow <harlo...@gmail.com> -Joshua Harlow <harlo...@yahoo-inc.com> -Joshua Harlow <jxhar...@godaddy.com> -Julien Danjou <jul...@danjou.info> -Martin Larralde <altho...@users.noreply.github.com> -Mehdi Abaakouk <sil...@sileht.net> -Michael Elsdörfer <mich...@elsdoerfer.info> -Michael Evans <michael.ev...@mwrinfosecurity.com> -MikeWooster <mikewooste...@gmail.com> -Monty Taylor <mord...@inaugust.com> -Ray Holder <ray.holder+git...@gmail.com> -Ray Holder <r...@blacklocus.com> -Ryan Peck <r...@rypeck.com> -Sam Park <sp...@goodrx.com> -Saul Shanabrook <s.shanabr...@gmail.com> -Simeon Visser <svis...@users.noreply.github.com> -Simon Dollé <simon.do...@gmail.com> -Tim Burke <tim.bu...@gmail.com> -Victor Yap <vic...@vicyap.com> -William Silversmith <william.silversm...@gmail.com> -Zane Bitter <zbit...@redhat.com> -cyrus durgin <cyr...@gmail.com> -immerrr <imme...@gmail.com> -mergify-bot <nore...@mergify.io> -mergify[bot] <mergify[bot]@users.noreply.github.com> -Ãtienne BERSAC (bersace) <etienne.ber...@people-doc.com> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tenacity-8.0.1/PKG-INFO new/tenacity-8.1.0/PKG-INFO --- old/tenacity-8.0.1/PKG-INFO 2021-07-12 11:36:06.397020600 +0200 +++ new/tenacity-8.1.0/PKG-INFO 2022-09-21 14:24:11.075567200 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: tenacity -Version: 8.0.1 +Version: 8.1.0 Summary: Retry code until it succeeds Home-page: https://github.com/jd/tenacity Author: Julien Danjou @@ -21,7 +21,6 @@ Requires-Python: >=3.6 Provides-Extra: doc License-File: LICENSE -License-File: AUTHORS Tenacity is a general-purpose retrying library to simplify the task of adding retry behavior to just about anything. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tenacity-8.0.1/README.rst new/tenacity-8.1.0/README.rst --- old/tenacity-8.0.1/README.rst 2021-01-29 16:26:35.000000000 +0100 +++ new/tenacity-8.1.0/README.rst 2022-09-21 14:23:59.000000000 +0200 @@ -95,7 +95,7 @@ .. testcode:: @retry - def never_give_up_never_surrender(): + def never_gonna_give_you_up(): print("Retry forever ignoring Exceptions, don't wait between retries") raise Exception @@ -205,11 +205,19 @@ .. testcode:: + class ClientError(Exception): + """Some type of client error.""" + @retry(retry=retry_if_exception_type(IOError)) def might_io_error(): print("Retry forever with no wait if an IOError occurs, raise any other errors") raise Exception + @retry(retry=retry_if_not_exception_type(ClientError)) + def might_client_error(): + print("Retry forever with no wait if any error other than ClientError occurs. Immediately raise ClientError.") + raise Exception + We can also use the result of the function to alter the behavior of retrying. .. testcode:: @@ -222,6 +230,21 @@ def might_return_none(): print("Retry with no wait if return value is None") +See also these methods: + +.. testcode:: + + retry_if_exception + retry_if_exception_type + retry_if_not_exception_type + retry_unless_exception_type + retry_if_result + retry_if_not_result + retry_if_exception_message + retry_if_not_exception_message + retry_any + retry_all + We can also combine several conditions: .. testcode:: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tenacity-8.0.1/doc/source/index.rst new/tenacity-8.1.0/doc/source/index.rst --- old/tenacity-8.0.1/doc/source/index.rst 2021-01-29 16:26:35.000000000 +0100 +++ new/tenacity-8.1.0/doc/source/index.rst 2022-09-21 14:23:59.000000000 +0200 @@ -95,7 +95,7 @@ .. testcode:: @retry - def never_give_up_never_surrender(): + def never_gonna_give_you_up(): print("Retry forever ignoring Exceptions, don't wait between retries") raise Exception @@ -205,11 +205,19 @@ .. testcode:: + class ClientError(Exception): + """Some type of client error.""" + @retry(retry=retry_if_exception_type(IOError)) def might_io_error(): print("Retry forever with no wait if an IOError occurs, raise any other errors") raise Exception + @retry(retry=retry_if_not_exception_type(ClientError)) + def might_client_error(): + print("Retry forever with no wait if any error other than ClientError occurs. Immediately raise ClientError.") + raise Exception + We can also use the result of the function to alter the behavior of retrying. .. testcode:: @@ -222,6 +230,21 @@ def might_return_none(): print("Retry with no wait if return value is None") +See also these methods: + +.. testcode:: + + retry_if_exception + retry_if_exception_type + retry_if_not_exception_type + retry_unless_exception_type + retry_if_result + retry_if_not_result + retry_if_exception_message + retry_if_not_exception_message + retry_any + retry_all + We can also combine several conditions: .. testcode:: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tenacity-8.0.1/releasenotes/notes/add_retry_if_exception_cause_type-d16b918ace4ae0ad.yaml new/tenacity-8.1.0/releasenotes/notes/add_retry_if_exception_cause_type-d16b918ace4ae0ad.yaml --- old/tenacity-8.0.1/releasenotes/notes/add_retry_if_exception_cause_type-d16b918ace4ae0ad.yaml 1970-01-01 01:00:00.000000000 +0100 +++ new/tenacity-8.1.0/releasenotes/notes/add_retry_if_exception_cause_type-d16b918ace4ae0ad.yaml 2022-09-21 14:23:59.000000000 +0200 @@ -0,0 +1,5 @@ +--- +features: + - | + Add a new `retry_base` class called `retry_if_exception_cause_type` that + checks, recursively, if any of the causes of the raised exception is of a certain type. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tenacity-8.0.1/releasenotes/notes/sphinx_define_error-642c9cd5c165d39a.yaml new/tenacity-8.1.0/releasenotes/notes/sphinx_define_error-642c9cd5c165d39a.yaml --- old/tenacity-8.0.1/releasenotes/notes/sphinx_define_error-642c9cd5c165d39a.yaml 1970-01-01 01:00:00.000000000 +0100 +++ new/tenacity-8.1.0/releasenotes/notes/sphinx_define_error-642c9cd5c165d39a.yaml 2022-09-21 14:23:59.000000000 +0200 @@ -0,0 +1,2 @@ +--- +fixes: Sphinx build error where Sphinx complains about an undefined class. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tenacity-8.0.1/releasenotes/notes/support-timedelta-wait-unit-type-5ba1e9fc0fe45523.yaml new/tenacity-8.1.0/releasenotes/notes/support-timedelta-wait-unit-type-5ba1e9fc0fe45523.yaml --- old/tenacity-8.0.1/releasenotes/notes/support-timedelta-wait-unit-type-5ba1e9fc0fe45523.yaml 1970-01-01 01:00:00.000000000 +0100 +++ new/tenacity-8.1.0/releasenotes/notes/support-timedelta-wait-unit-type-5ba1e9fc0fe45523.yaml 2022-09-21 14:23:59.000000000 +0200 @@ -0,0 +1,3 @@ +--- +features: + - Add ``datetime.timedelta`` as accepted wait unit type. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tenacity-8.0.1/releasenotes/notes/wait_exponential_jitter-6ffc81dddcbaa6d3.yaml new/tenacity-8.1.0/releasenotes/notes/wait_exponential_jitter-6ffc81dddcbaa6d3.yaml --- old/tenacity-8.0.1/releasenotes/notes/wait_exponential_jitter-6ffc81dddcbaa6d3.yaml 1970-01-01 01:00:00.000000000 +0100 +++ new/tenacity-8.1.0/releasenotes/notes/wait_exponential_jitter-6ffc81dddcbaa6d3.yaml 2022-09-21 14:23:59.000000000 +0200 @@ -0,0 +1,5 @@ +--- +features: + - | + Implement a wait.wait_exponential_jitter per Google's storage retry guide. + See https://cloud.google.com/storage/docs/retry-strategy diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tenacity-8.0.1/tenacity/__init__.py new/tenacity-8.1.0/tenacity/__init__.py --- old/tenacity-8.0.1/tenacity/__init__.py 2021-07-08 09:31:14.000000000 +0200 +++ new/tenacity-8.1.0/tenacity/__init__.py 2022-09-21 14:23:59.000000000 +0200 @@ -33,6 +33,7 @@ from .retry import retry_any # noqa from .retry import retry_if_exception # noqa from .retry import retry_if_exception_type # noqa +from .retry import retry_if_exception_cause_type # noqa from .retry import retry_if_not_exception_type # noqa from .retry import retry_if_not_result # noqa from .retry import retry_if_result # noqa @@ -63,6 +64,7 @@ from .wait import wait_random # noqa from .wait import wait_random_exponential # noqa from .wait import wait_random_exponential as wait_full_jitter # noqa +from .wait import wait_exponential_jitter # noqa # Import all built-in before strategies for easier usage. from .before import before_log # noqa diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tenacity-8.0.1/tenacity/retry.py new/tenacity-8.1.0/tenacity/retry.py --- old/tenacity-8.0.1/tenacity/retry.py 2021-06-30 14:37:02.000000000 +0200 +++ new/tenacity-8.1.0/tenacity/retry.py 2022-09-21 14:23:59.000000000 +0200 @@ -117,6 +117,33 @@ return self.predicate(retry_state.outcome.exception()) +class retry_if_exception_cause_type(retry_base): + """Retries if any of the causes of the raised exception is of one or more types. + + The check on the type of the cause of the exception is done recursively (until finding + an exception in the chain that has no `__cause__`) + """ + + def __init__( + self, + exception_types: typing.Union[ + typing.Type[BaseException], + typing.Tuple[typing.Type[BaseException], ...], + ] = Exception, + ) -> None: + self.exception_cause_types = exception_types + + def __call__(self, retry_state: "RetryCallState") -> bool: + if retry_state.outcome.failed: + exc = retry_state.outcome.exception() + while exc is not None: + if isinstance(exc.__cause__, self.exception_cause_types): + return True + exc = exc.__cause__ + + return False + + class retry_if_result(retry_base): """Retries if the result verifies a predicate.""" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tenacity-8.0.1/tenacity/wait.py new/tenacity-8.1.0/tenacity/wait.py --- old/tenacity-8.0.1/tenacity/wait.py 2021-07-08 09:31:14.000000000 +0200 +++ new/tenacity-8.1.0/tenacity/wait.py 2022-09-21 14:23:59.000000000 +0200 @@ -17,12 +17,19 @@ import abc import random import typing +from datetime import timedelta from tenacity import _utils if typing.TYPE_CHECKING: from tenacity import RetryCallState +wait_unit_type = typing.Union[int, float, timedelta] + + +def to_seconds(wait_unit: wait_unit_type) -> float: + return float(wait_unit.total_seconds() if isinstance(wait_unit, timedelta) else wait_unit) + class wait_base(abc.ABC): """Abstract base class for wait strategies.""" @@ -44,8 +51,8 @@ class wait_fixed(wait_base): """Wait strategy that waits a fixed amount of time between each retry.""" - def __init__(self, wait: float) -> None: - self.wait_fixed = wait + def __init__(self, wait: wait_unit_type) -> None: + self.wait_fixed = to_seconds(wait) def __call__(self, retry_state: "RetryCallState") -> float: return self.wait_fixed @@ -61,9 +68,9 @@ class wait_random(wait_base): """Wait strategy that waits a random amount of time between min/max.""" - def __init__(self, min: typing.Union[int, float] = 0, max: typing.Union[int, float] = 1) -> None: # noqa - self.wait_random_min = min - self.wait_random_max = max + def __init__(self, min: wait_unit_type = 0, max: wait_unit_type = 1) -> None: # noqa + self.wait_random_min = to_seconds(min) + self.wait_random_max = to_seconds(max) def __call__(self, retry_state: "RetryCallState") -> float: return self.wait_random_min + (random.random() * (self.wait_random_max - self.wait_random_min)) @@ -113,13 +120,13 @@ def __init__( self, - start: typing.Union[int, float] = 0, - increment: typing.Union[int, float] = 100, - max: typing.Union[int, float] = _utils.MAX_WAIT, # noqa + start: wait_unit_type = 0, + increment: wait_unit_type = 100, + max: wait_unit_type = _utils.MAX_WAIT, # noqa ) -> None: - self.start = start - self.increment = increment - self.max = max + self.start = to_seconds(start) + self.increment = to_seconds(increment) + self.max = to_seconds(max) def __call__(self, retry_state: "RetryCallState") -> float: result = self.start + (self.increment * (retry_state.attempt_number - 1)) @@ -142,13 +149,13 @@ def __init__( self, multiplier: typing.Union[int, float] = 1, - max: typing.Union[int, float] = _utils.MAX_WAIT, # noqa + max: wait_unit_type = _utils.MAX_WAIT, # noqa exp_base: typing.Union[int, float] = 2, - min: typing.Union[int, float] = 0, # noqa + min: wait_unit_type = 0, # noqa ) -> None: self.multiplier = multiplier - self.min = min - self.max = max + self.min = to_seconds(min) + self.max = to_seconds(max) self.exp_base = exp_base def __call__(self, retry_state: "RetryCallState") -> float: @@ -189,3 +196,37 @@ def __call__(self, retry_state: "RetryCallState") -> float: high = super().__call__(retry_state=retry_state) return random.uniform(0, high) + + +class wait_exponential_jitter(wait_base): + """Wait strategy that applies exponential backoff and jitter. + + It allows for a customized initial wait, maximum wait and jitter. + + This implements the strategy described here: + https://cloud.google.com/storage/docs/retry-strategy + + The wait time is min(initial * (2**n + random.uniform(0, jitter)), maximum) + where n is the retry count. + """ + + def __init__( + self, + initial: float = 1, + max: float = _utils.MAX_WAIT, # noqa + exp_base: float = 2, + jitter: float = 1, + ) -> None: + self.initial = initial + self.max = max + self.exp_base = exp_base + self.jitter = jitter + + def __call__(self, retry_state: "RetryCallState") -> float: + jitter = random.uniform(0, self.jitter) + try: + exp = self.exp_base ** (retry_state.attempt_number - 1) + result = self.initial * exp + jitter + except OverflowError: + result = self.max + return max(0, min(result, self.max)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tenacity-8.0.1/tenacity.egg-info/PKG-INFO new/tenacity-8.1.0/tenacity.egg-info/PKG-INFO --- old/tenacity-8.0.1/tenacity.egg-info/PKG-INFO 2021-07-12 11:36:06.000000000 +0200 +++ new/tenacity-8.1.0/tenacity.egg-info/PKG-INFO 2022-09-21 14:24:11.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: tenacity -Version: 8.0.1 +Version: 8.1.0 Summary: Retry code until it succeeds Home-page: https://github.com/jd/tenacity Author: Julien Danjou @@ -21,7 +21,6 @@ Requires-Python: >=3.6 Provides-Extra: doc License-File: LICENSE -License-File: AUTHORS Tenacity is a general-purpose retrying library to simplify the task of adding retry behavior to just about anything. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tenacity-8.0.1/tenacity.egg-info/SOURCES.txt new/tenacity-8.1.0/tenacity.egg-info/SOURCES.txt --- old/tenacity-8.0.1/tenacity.egg-info/SOURCES.txt 2021-07-12 11:36:06.000000000 +0200 +++ new/tenacity-8.1.0/tenacity.egg-info/SOURCES.txt 2022-09-21 14:24:11.000000000 +0200 @@ -2,7 +2,6 @@ .gitignore .mergify.yml .readthedocs.yml -AUTHORS LICENSE README.rst pyproject.toml @@ -18,6 +17,7 @@ releasenotes/notes/Use--for-formatting-and-validate-using-black-39ec9d57d4691778.yaml releasenotes/notes/add-reno-d1ab5710f272650a.yaml releasenotes/notes/add-retry_except_exception_type-31b31da1924d55f4.yaml +releasenotes/notes/add_retry_if_exception_cause_type-d16b918ace4ae0ad.yaml releasenotes/notes/after_log-50f4d73b24ce9203.yaml releasenotes/notes/allow-mocking-of-nap-sleep-6679c50e702446f1.yaml releasenotes/notes/annotate_code-197b93130df14042.yaml @@ -30,6 +30,9 @@ releasenotes/notes/pr320-py3-only-wheel-tag.yaml releasenotes/notes/py36_plus-c425fb3aa17c6682.yaml releasenotes/notes/retrycallstate-repr-94947f7b00ee15e1.yaml +releasenotes/notes/sphinx_define_error-642c9cd5c165d39a.yaml +releasenotes/notes/support-timedelta-wait-unit-type-5ba1e9fc0fe45523.yaml +releasenotes/notes/wait_exponential_jitter-6ffc81dddcbaa6d3.yaml tenacity/__init__.py tenacity/_asyncio.py tenacity/_utils.py @@ -45,7 +48,6 @@ tenacity.egg-info/PKG-INFO tenacity.egg-info/SOURCES.txt tenacity.egg-info/dependency_links.txt -tenacity.egg-info/pbr.json tenacity.egg-info/requires.txt tenacity.egg-info/top_level.txt tests/__init__.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tenacity-8.0.1/tenacity.egg-info/pbr.json new/tenacity-8.1.0/tenacity.egg-info/pbr.json --- old/tenacity-8.0.1/tenacity.egg-info/pbr.json 2019-08-16 10:42:49.000000000 +0200 +++ new/tenacity-8.1.0/tenacity.egg-info/pbr.json 1970-01-01 01:00:00.000000000 +0100 @@ -1 +0,0 @@ -{"git_version": "58495e5", "is_release": false} \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tenacity-8.0.1/tests/test_tenacity.py new/tenacity-8.1.0/tests/test_tenacity.py --- old/tenacity-8.0.1/tests/test_tenacity.py 2021-07-08 09:31:14.000000000 +0200 +++ new/tenacity-8.1.0/tests/test_tenacity.py 2022-09-21 14:23:59.000000000 +0200 @@ -13,6 +13,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +import datetime import logging import re import sys @@ -29,7 +30,6 @@ import tenacity from tenacity import RetryCallState, RetryError, Retrying, retry - _unset = object() @@ -180,28 +180,34 @@ self.assertEqual(0, r.wait(make_retry_state(18, 9879))) def test_fixed_sleep(self): - r = Retrying(wait=tenacity.wait_fixed(1)) - self.assertEqual(1, r.wait(make_retry_state(12, 6546))) + for wait in (1, datetime.timedelta(seconds=1)): + with self.subTest(): + r = Retrying(wait=tenacity.wait_fixed(wait)) + self.assertEqual(1, r.wait(make_retry_state(12, 6546))) def test_incrementing_sleep(self): - r = Retrying(wait=tenacity.wait_incrementing(start=500, increment=100)) - self.assertEqual(500, r.wait(make_retry_state(1, 6546))) - self.assertEqual(600, r.wait(make_retry_state(2, 6546))) - self.assertEqual(700, r.wait(make_retry_state(3, 6546))) + for start, increment in ((500, 100), (datetime.timedelta(seconds=500), datetime.timedelta(seconds=100))): + with self.subTest(): + r = Retrying(wait=tenacity.wait_incrementing(start=start, increment=increment)) + self.assertEqual(500, r.wait(make_retry_state(1, 6546))) + self.assertEqual(600, r.wait(make_retry_state(2, 6546))) + self.assertEqual(700, r.wait(make_retry_state(3, 6546))) def test_random_sleep(self): - r = Retrying(wait=tenacity.wait_random(min=1, max=20)) - times = set() - for x in range(1000): - times.add(r.wait(make_retry_state(1, 6546))) - - # this is kind of non-deterministic... - self.assertTrue(len(times) > 1) - for t in times: - self.assertTrue(t >= 1) - self.assertTrue(t < 20) + for min_, max_ in ((1, 20), (datetime.timedelta(seconds=1), datetime.timedelta(seconds=20))): + with self.subTest(): + r = Retrying(wait=tenacity.wait_random(min=min_, max=max_)) + times = set() + for _ in range(1000): + times.add(r.wait(make_retry_state(1, 6546))) + + # this is kind of non-deterministic... + self.assertTrue(len(times) > 1) + for t in times: + self.assertTrue(t >= 1) + self.assertTrue(t < 20) - def test_random_sleep_without_min(self): + def test_random_sleep_withoutmin_(self): r = Retrying(wait=tenacity.wait_random(max=2)) times = set() times.add(r.wait(make_retry_state(1, 6546))) @@ -274,18 +280,20 @@ self.assertEqual(r.wait(make_retry_state(8, 0)), 256) self.assertEqual(r.wait(make_retry_state(20, 0)), 1048576) - def test_exponential_with_min_wait_and_max_wait(self): - r = Retrying(wait=tenacity.wait_exponential(min=10, max=100)) - self.assertEqual(r.wait(make_retry_state(1, 0)), 10) - self.assertEqual(r.wait(make_retry_state(2, 0)), 10) - self.assertEqual(r.wait(make_retry_state(3, 0)), 10) - self.assertEqual(r.wait(make_retry_state(4, 0)), 10) - self.assertEqual(r.wait(make_retry_state(5, 0)), 16) - self.assertEqual(r.wait(make_retry_state(6, 0)), 32) - self.assertEqual(r.wait(make_retry_state(7, 0)), 64) - self.assertEqual(r.wait(make_retry_state(8, 0)), 100) - self.assertEqual(r.wait(make_retry_state(9, 0)), 100) - self.assertEqual(r.wait(make_retry_state(20, 0)), 100) + def test_exponential_with_min_wait_andmax__wait(self): + for min_, max_ in ((10, 100), (datetime.timedelta(seconds=10), datetime.timedelta(seconds=100))): + with self.subTest(): + r = Retrying(wait=tenacity.wait_exponential(min=min_, max=max_)) + self.assertEqual(r.wait(make_retry_state(1, 0)), 10) + self.assertEqual(r.wait(make_retry_state(2, 0)), 10) + self.assertEqual(r.wait(make_retry_state(3, 0)), 10) + self.assertEqual(r.wait(make_retry_state(4, 0)), 10) + self.assertEqual(r.wait(make_retry_state(5, 0)), 16) + self.assertEqual(r.wait(make_retry_state(6, 0)), 32) + self.assertEqual(r.wait(make_retry_state(7, 0)), 64) + self.assertEqual(r.wait(make_retry_state(8, 0)), 100) + self.assertEqual(r.wait(make_retry_state(9, 0)), 100) + self.assertEqual(r.wait(make_retry_state(20, 0)), 100) def test_legacy_explicit_wait_type(self): Retrying(wait="exponential_sleep") @@ -335,7 +343,7 @@ ) ) # Test it a few time since it's random - for i in range(1000): + for _ in range(1000): w = r.wait(make_retry_state(1, 5)) self.assertLess(w, 9) self.assertGreaterEqual(w, 6) @@ -435,6 +443,28 @@ self._assert_inclusive_epsilon(mean(attempt[8]), 30, 2.56) self._assert_inclusive_epsilon(mean(attempt[9]), 30, 2.56) + def test_wait_exponential_jitter(self): + fn = tenacity.wait_exponential_jitter(max=60) + + for _ in range(1000): + self._assert_inclusive_range(fn(make_retry_state(1, 0)), 1, 2) + self._assert_inclusive_range(fn(make_retry_state(2, 0)), 2, 3) + self._assert_inclusive_range(fn(make_retry_state(3, 0)), 4, 5) + self._assert_inclusive_range(fn(make_retry_state(4, 0)), 8, 9) + self._assert_inclusive_range(fn(make_retry_state(5, 0)), 16, 17) + self._assert_inclusive_range(fn(make_retry_state(6, 0)), 32, 33) + self.assertEqual(fn(make_retry_state(7, 0)), 60) + self.assertEqual(fn(make_retry_state(8, 0)), 60) + self.assertEqual(fn(make_retry_state(9, 0)), 60) + + fn = tenacity.wait_exponential_jitter(10, 5) + for _ in range(1000): + self.assertEqual(fn(make_retry_state(1, 0)), 5) + + # Default arguments exist + fn = tenacity.wait_exponential_jitter() + fn(make_retry_state(0, 0)) + def test_wait_retry_state_attributes(self): class ExtractCallState(Exception): pass @@ -646,6 +676,56 @@ return True +class NoNameErrorCauseAfterCount: + """Holds counter state for invoking a method several times in a row.""" + + def __init__(self, count): + self.counter = 0 + self.count = count + + def go2(self): + raise NameError("Hi there, I'm a NameError") + + def go(self): + """Raise an IOError with a NameError as cause until after count threshold has been crossed. + + Then return True. + """ + if self.counter < self.count: + self.counter += 1 + try: + self.go2() + except NameError as e: + raise IOError() from e + + return True + + +class NoIOErrorCauseAfterCount: + """Holds counter state for invoking a method several times in a row.""" + + def __init__(self, count): + self.counter = 0 + self.count = count + + def go2(self): + raise IOError("Hi there, I'm an IOError") + + def go(self): + """Raise a NameError with an IOError as cause until after count threshold has been crossed. + + Then return True. + """ + if self.counter < self.count: + self.counter += 1 + try: + self.go2() + except IOError as e: + raise NameError() from e + + return True + + class NameErrorUntilCount: """Holds counter state for invoking a method several times in a row.""" @@ -753,6 +833,11 @@ return thing.go() +@retry(retry=tenacity.retry_if_exception_cause_type(NameError)) +def _retryable_test_with_exception_cause_type(thing): + return thing.go() + + @retry(retry=tenacity.retry_if_exception_type(IOError)) def _retryable_test_with_exception_type_io(thing): return thing.go() @@ -957,6 +1042,15 @@ s = _retryable_test_if_not_exception_message_message.retry.statistics self.assertTrue(s["attempt_number"] == 1) + def test_retry_if_exception_cause_type(self): + self.assertTrue(_retryable_test_with_exception_cause_type(NoNameErrorCauseAfterCount(5))) + + try: + _retryable_test_with_exception_cause_type(NoIOErrorCauseAfterCount(5)) + self.fail("Expected exception without NameError as cause") + except NameError: + pass + def test_defaults(self): self.assertTrue(_retryable_default(NoNameErrorAfterCount(5))) self.assertTrue(_retryable_default_f(NoNameErrorAfterCount(5)))