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)))

Reply via email to