Hello community,

here is the log from the commit of package python-tenacity for openSUSE:Factory 
checked in at 2018-10-22 11:23:51
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-tenacity (Old)
 and      /work/SRC/openSUSE:Factory/.python-tenacity.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-tenacity"

Mon Oct 22 11:23:51 2018 rev:5 rq:643129 version:4.12.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-tenacity/python-tenacity.changes  
2018-01-13 21:47:40.198011952 +0100
+++ /work/SRC/openSUSE:Factory/.python-tenacity.new/python-tenacity.changes     
2018-10-22 11:23:55.863120521 +0200
@@ -1,0 +2,27 @@
+Thu Oct 18 21:56:38 UTC 2018 - Jan Engelhardt <[email protected]>
+
+- Use noun phrase in summary.
+
+-------------------------------------------------------------------
+Thu Oct 11 11:56:31 UTC 2018 - Dirk Mueller <[email protected]>
+
+- update to 4.12.0:
+  * add retry\_error\_callback param
+  * Fix Mergify conf
+  * Enable mergify
+  * Implement before\_sleep logging hook
+  * Rename tenacity.async to tenacity.\_asyncio
+  * Remove useless install of nose
+  * Switch to pytest
+  * Fix codeblock formatting
+  * Document how to use Trio/curio
+  * Catch BaseException rather than just Exception
+  * Fix pep8 errors
+  * Only install monotonic on Python 2
+  * Stop using pbr to build documentation
+  * Add \`license\` key to \`setup.cfg\`
+  * Avoid inspect.getargspec deprecation warning
+  * Don't fall over if an old version of tornado is installed
+  * Allow to specify RetryError
+
+-------------------------------------------------------------------

Old:
----
  tenacity-4.8.0.tar.gz

New:
----
  tenacity-4.12.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-tenacity.spec ++++++
--- /var/tmp/diff_new_pack.2uQ4CS/_old  2018-10-22 11:23:56.327120057 +0200
+++ /var/tmp/diff_new_pack.2uQ4CS/_new  2018-10-22 11:23:56.331120053 +0200
@@ -12,16 +12,16 @@
 # license that conforms to the Open Source Definition (Version 1.9)
 # published by the Open Source Initiative.
 
-# Please submit bugfixes or comments via http://bugs.opensuse.org/
+# Please submit bugfixes or comments via https://bugs.opensuse.org/
 #
 
 
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 %bcond_without  test
 Name:           python-tenacity
-Version:        4.8.0
+Version:        4.12.0
 Release:        0
-Summary:        Retry code until it succeeeds
+Summary:        Python module for retrying code until it succeeeds
 License:        Apache-2.0
 Group:          Development/Languages/Python
 Url:            https://github.com/jd/tenacity
@@ -29,6 +29,7 @@
 BuildRequires:  %{python_module devel}
 BuildRequires:  %{python_module monotonic >= 0.6}
 BuildRequires:  %{python_module pbr}
+BuildRequires:  %{python_module pytest}
 BuildRequires:  %{python_module setuptools}
 BuildRequires:  %{python_module six >= 1.7.0}
 BuildRequires:  fdupes
@@ -72,12 +73,13 @@
 
 %if %{with test}
 %check
-%python_exec setup.py nosetests --ignore-files '.*async.py'
+%python_exec setup.py nosetests --ignore-files '.*asyncio.py'
 %endif
 
 %files %{python_files}
 %defattr(-,root,root,-)
-%doc AUTHORS ChangeLog LICENSE README.rst
+%license LICENSE
+%doc ChangeLog README.rst
 %{python_sitelib}/*
 
 %changelog

++++++ tenacity-4.8.0.tar.gz -> tenacity-4.12.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tenacity-4.8.0/.mergify.yml 
new/tenacity-4.12.0/.mergify.yml
--- old/tenacity-4.8.0/.mergify.yml     1970-01-01 01:00:00.000000000 +0100
+++ new/tenacity-4.12.0/.mergify.yml    2018-05-01 12:18:58.000000000 +0200
@@ -0,0 +1,9 @@
+rules:
+  default:
+    protection:
+      required_status_checks:
+        strict: True
+        contexts:
+          - continuous-integration/travis-ci
+      required_pull_request_reviews:
+        required_approving_review_count: 1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tenacity-4.8.0/.travis.yml 
new/tenacity-4.12.0/.travis.yml
--- old/tenacity-4.8.0/.travis.yml      2017-12-14 09:53:01.000000000 +0100
+++ new/tenacity-4.12.0/.travis.yml     2018-05-01 12:18:58.000000000 +0200
@@ -6,7 +6,7 @@
   - 3.6
   - pypy
 
-install: pip install tox tox-travis nose
+install: pip install tox tox-travis
 
 script: tox
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tenacity-4.8.0/AUTHORS new/tenacity-4.12.0/AUTHORS
--- old/tenacity-4.8.0/AUTHORS  2017-12-14 09:53:45.000000000 +0100
+++ new/tenacity-4.12.0/AUTHORS 2018-05-01 12:19:48.000000000 +0200
@@ -1,10 +1,15 @@
 Brian Williams <[email protected]>
 Brian-Williams <[email protected]>
+Daniel Bennett <[email protected]>
 Elisey Zanko <[email protected]>
+Hannes Gräuler <[email protected]>
+Jaye Doepke <[email protected]>
 Joshua Harlow <[email protected]>
 Julien Danjou <[email protected]>
+Martin Larralde <[email protected]>
+Michael Elsdörfer <[email protected]>
 Michael Evans <[email protected]>
+Tim Burke <[email protected]>
 Victor Yap <[email protected]>
 William Silversmith <[email protected]>
 Zane Bitter <[email protected]>
-Étienne BERSAC (bersace) <[email protected]>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tenacity-4.8.0/ChangeLog 
new/tenacity-4.12.0/ChangeLog
--- old/tenacity-4.8.0/ChangeLog        2017-12-14 09:53:45.000000000 +0100
+++ new/tenacity-4.12.0/ChangeLog       2018-05-01 12:19:47.000000000 +0200
@@ -1,6 +1,39 @@
 CHANGES
 =======
 
+4.12.0
+------
+
+* add retry\_error\_callback param
+* Fix Mergify conf
+* Enable mergify
+
+4.11.0
+------
+
+* Implement before\_sleep logging hook
+* Rename tenacity.async to tenacity.\_asyncio
+* Remove useless install of nose
+* Switch to pytest
+* Fix codeblock formatting
+* Document how to use Trio/curio
+
+4.10.0
+------
+
+* Catch BaseException rather than just Exception
+* Fix pep8 errors
+* Only install monotonic on Python 2
+* Stop using pbr to build documentation
+* Add \`license\` key to \`setup.cfg\`
+
+4.9.0
+-----
+
+* Avoid inspect.getargspec deprecation warning
+* Don't fall over if an old version of tornado is installed
+* Allow to specify RetryError
+
 4.8.0
 -----
 
@@ -83,40 +116,3 @@
 -----
 
 * Add \_\_call\_\_ on BaseRetrying class
-* Document before and after keywords
-* Remove useless MANIFEST
-* Remove non-working PyPI download image
-* Bump hacking to 0.13
-* Use Python 3 for pep8 tox target
-* Remove deprecated wait\_jitter
-
-3.7.1
------
-
-* Fix pep8 errors
-
-3.7.0
------
-
-* Correctly set the exception if we TryAgain for ever
-
-3.6.0
------
-
-* Retry on coroutines
-* Run flake8 only with latest python
-* Deduplicate retry decorator logic
-* Extract controller IOs in subclass
-
-3.5.0
------
-
-* Allow to combine stop conditions
-* Add SayThanks
-* retry: implement bitwise operators on retry strategies
-* retry: add retry\_all
-
-3.4.0
------
-
-* Deprecate wait\_jitter for wait\_random
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tenacity-4.8.0/PKG-INFO new/tenacity-4.12.0/PKG-INFO
--- old/tenacity-4.8.0/PKG-INFO 2017-12-14 09:53:45.000000000 +0100
+++ new/tenacity-4.12.0/PKG-INFO        2018-05-01 12:19:48.000000000 +0200
@@ -1,12 +1,11 @@
 Metadata-Version: 1.1
 Name: tenacity
-Version: 4.8.0
+Version: 4.12.0
 Summary: Retry code until it succeeeds
 Home-page: https://github.com/jd/tenacity
 Author: Julien Danjou
 Author-email: [email protected]
-License: UNKNOWN
-Description-Content-Type: UNKNOWN
+License: Apache 2.0
 Description: Tenacity
         ========
         .. image:: https://img.shields.io/pypi/v/tenacity.svg
@@ -263,6 +262,25 @@
             def raise_my_exception():
                 raise MyException("Fail")
         
+        Similarly, you can call a custom callback function after all retries 
failed, without raising an exception (or you can re-raise or do anything really)
+        
+        .. testcode::
+        
+            def return_last_value(last_attempt):
+                """return the result of the last call attempt"""
+                return last_attempt.result()
+        
+            def is_false(value):
+                """Return True if value is False"""
+                return value is False
+        
+            # will return False after trying 3 times to get a different result
+            @retry(stop=stop_after_attempt(3),
+                   retry_error_callback=return_last_value,
+                   retry=retry_if_result(is_false))
+            def eventually_return_false():
+                return False
+        
         You can access the statistics about the retry made over a function by 
using the
         `retry` attribute attached to the function and its `statistics` 
attribute:
         
@@ -320,6 +338,14 @@
             @tornado.gen.coroutine
             def my_async_function(http_client, url):
                 yield http_client.fetch(url)
+                
+        You can even use alternative event loops such as `curio` or `Trio` by 
passing the correct sleep function:
+        
+        .. code-block:: python
+        
+            @retry(sleep=trio.sleep)
+            async def my_async_function(loop):
+                await asks.get('https://example.org')
         
         Contribute
         ----------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tenacity-4.8.0/README.rst 
new/tenacity-4.12.0/README.rst
--- old/tenacity-4.8.0/README.rst       2017-12-14 09:53:01.000000000 +0100
+++ new/tenacity-4.12.0/README.rst      2018-05-01 12:18:58.000000000 +0200
@@ -254,6 +254,25 @@
     def raise_my_exception():
         raise MyException("Fail")
 
+Similarly, you can call a custom callback function after all retries failed, 
without raising an exception (or you can re-raise or do anything really)
+
+.. testcode::
+
+    def return_last_value(last_attempt):
+        """return the result of the last call attempt"""
+        return last_attempt.result()
+
+    def is_false(value):
+        """Return True if value is False"""
+        return value is False
+
+    # will return False after trying 3 times to get a different result
+    @retry(stop=stop_after_attempt(3),
+           retry_error_callback=return_last_value,
+           retry=retry_if_result(is_false))
+    def eventually_return_false():
+        return False
+
 You can access the statistics about the retry made over a function by using the
 `retry` attribute attached to the function and its `statistics` attribute:
 
@@ -311,6 +330,14 @@
     @tornado.gen.coroutine
     def my_async_function(http_client, url):
         yield http_client.fetch(url)
+        
+You can even use alternative event loops such as `curio` or `Trio` by passing 
the correct sleep function:
+
+.. code-block:: python
+
+    @retry(sleep=trio.sleep)
+    async def my_async_function(loop):
+        await asks.get('https://example.org')
 
 Contribute
 ----------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tenacity-4.8.0/doc/source/index.rst 
new/tenacity-4.12.0/doc/source/index.rst
--- old/tenacity-4.8.0/doc/source/index.rst     2017-12-14 09:53:01.000000000 
+0100
+++ new/tenacity-4.12.0/doc/source/index.rst    2018-05-01 12:18:58.000000000 
+0200
@@ -254,6 +254,25 @@
     def raise_my_exception():
         raise MyException("Fail")
 
+Similarly, you can call a custom callback function after all retries failed, 
without raising an exception (or you can re-raise or do anything really)
+
+.. testcode::
+
+    def return_last_value(last_attempt):
+        """return the result of the last call attempt"""
+        return last_attempt.result()
+
+    def is_false(value):
+        """Return True if value is False"""
+        return value is False
+
+    # will return False after trying 3 times to get a different result
+    @retry(stop=stop_after_attempt(3),
+           retry_error_callback=return_last_value,
+           retry=retry_if_result(is_false))
+    def eventually_return_false():
+        return False
+
 You can access the statistics about the retry made over a function by using the
 `retry` attribute attached to the function and its `statistics` attribute:
 
@@ -311,6 +330,14 @@
     @tornado.gen.coroutine
     def my_async_function(http_client, url):
         yield http_client.fetch(url)
+        
+You can even use alternative event loops such as `curio` or `Trio` by passing 
the correct sleep function:
+
+.. code-block:: python
+
+    @retry(sleep=trio.sleep)
+    async def my_async_function(loop):
+        await asks.get('https://example.org')
 
 Contribute
 ----------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tenacity-4.8.0/requirements.txt 
new/tenacity-4.12.0/requirements.txt
--- old/tenacity-4.8.0/requirements.txt 2017-12-14 09:53:01.000000000 +0100
+++ new/tenacity-4.12.0/requirements.txt        2018-05-01 12:18:58.000000000 
+0200
@@ -1,3 +1,3 @@
 six>=1.9.0
 futures>=3.0;python_version=='2.7'
-monotonic>=0.6 # Apache-2.0
+monotonic>=0.6;python_version=='2.7'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tenacity-4.8.0/setup.cfg 
new/tenacity-4.12.0/setup.cfg
--- old/tenacity-4.8.0/setup.cfg        2017-12-14 09:53:45.000000000 +0100
+++ new/tenacity-4.12.0/setup.cfg       2018-05-01 12:19:48.000000000 +0200
@@ -1,5 +1,6 @@
 [metadata]
 name = tenacity
+license = Apache 2.0
 url = https://github.com/jd/tenacity
 summary = Retry code until it succeeeds
 description-file = 
@@ -26,11 +27,6 @@
 [wheel]
 universal = 1
 
-[build_sphinx]
-source-dir = doc/source
-build-dir = doc/_build
-builder = doctest,html
-
 [egg_info]
 tag_build = 
 tag_date = 0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tenacity-4.8.0/tenacity/__init__.py 
new/tenacity-4.12.0/tenacity/__init__.py
--- old/tenacity-4.8.0/tenacity/__init__.py     2017-12-14 09:53:01.000000000 
+0100
+++ new/tenacity-4.12.0/tenacity/__init__.py    2018-05-01 12:18:58.000000000 
+0200
@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
+# Copyright 2016-2018 Julien Danjou
 # Copyright 2017 Elisey Zanko
 # Copyright 2016 Étienne Bersac
-# Copyright 2016 Julien Danjou
 # Copyright 2016 Joshua Harlow
 # Copyright 2013-2014 Ray Holder
 #
@@ -27,13 +27,10 @@
 except ImportError:
     tornado = None
 
-import inspect
 import sys
 import threading
 from concurrent import futures
 
-from monotonic import monotonic as now
-
 import six
 
 from tenacity import _utils
@@ -81,6 +78,10 @@
 from .after import after_log  # noqa
 from .after import after_nothing  # noqa
 
+# Import all built-in after strategies for easier usage.
+from .before_sleep import before_sleep_log  # noqa
+from .before_sleep import before_sleep_nothing  # noqa
+
 
 def retry(*dargs, **dkw):
     """Wrap a function with a new `Retrying` object.
@@ -95,7 +96,8 @@
         def wrap(f):
             if asyncio and asyncio.iscoroutinefunction(f):
                 r = AsyncRetrying(*dargs, **dkw)
-            elif tornado and tornado.gen.is_coroutine_function(f):
+            elif tornado and hasattr(tornado.gen, 'is_coroutine_function') \
+                    and tornado.gen.is_coroutine_function(f):
                 r = TornadoRetrying(*dargs, **dkw)
             else:
                 r = Retrying(*dargs, **dkw)
@@ -123,6 +125,21 @@
 _unset = object()
 
 
+class RetryError(Exception):
+    """Encapsulates the last attempt instance right before giving up."""
+
+    def __init__(self, last_attempt):
+        self.last_attempt = last_attempt
+
+    def reraise(self):
+        if self.last_attempt.failed:
+            raise self.last_attempt.result()
+        raise self
+
+    def __str__(self):
+        return "{0}[{1}]".format(self.__class__.__name__, self.last_attempt)
+
+
 class BaseRetrying(object):
 
     def __init__(self,
@@ -131,23 +148,32 @@
                  retry=retry_if_exception_type(),
                  before=before_nothing,
                  after=after_nothing,
-                 reraise=False):
+                 before_sleep=before_sleep_nothing,
+                 reraise=False,
+                 retry_error_cls=RetryError,
+                 retry_error_callback=None):
         self.sleep = sleep
         self.stop = stop
         self.wait = wait
         self.retry = retry
         self.before = before
         self.after = after
+        self.before_sleep = before_sleep
         self.reraise = reraise
         self._local = threading.local()
         # This will allow for passing in the result and handling
         # the older versions of these functions that do not take
         # the prior result.
         self._wait_takes_result = self._waiter_takes_last_result(wait)
+        self.retry_error_cls = retry_error_cls
+        self.retry_error_callback = retry_error_callback
 
     def copy(self, sleep=_unset, stop=_unset, wait=_unset,
-             retry=_unset, before=_unset, after=_unset, reraise=_unset):
+             retry=_unset, before=_unset, after=_unset, before_sleep=_unset,
+             reraise=_unset):
         """Copy this object with some parameters changed if needed."""
+        if before_sleep is _unset:
+            before_sleep = self.before_sleep
         return self.__class__(
             sleep=self.sleep if sleep is _unset else sleep,
             stop=self.stop if stop is _unset else stop,
@@ -155,6 +181,7 @@
             retry=self.retry if retry is _unset else retry,
             before=self.before if before is _unset else before,
             after=self.after if after is _unset else after,
+            before_sleep=before_sleep,
             reraise=self.reraise if after is _unset else reraise,
         )
 
@@ -164,7 +191,7 @@
             return False
         if isinstance(waiter, _wait.wait_base):
             waiter = waiter.__call__
-        waiter_spec = inspect.getargspec(waiter)
+        waiter_spec = _utils.getargspec(waiter)
         return 'last_result' in waiter_spec.args
 
     def __repr__(self):
@@ -224,18 +251,18 @@
     def begin(self, fn):
         self.fn = fn
         self.statistics.clear()
-        self.statistics['start_time'] = now()
+        self.statistics['start_time'] = _utils.now()
         self.statistics['attempt_number'] = 1
         self.statistics['idle_for'] = 0
 
-    def iter(self, result, exc_info, start_time):
+    def iter(self, result, exc_info, start_time):  # noqa
         fut = Future(self.statistics['attempt_number'])
         if result is not NO_RESULT:
-            trial_end_time = now()
+            trial_end_time = _utils.now()
             fut.set_result(result)
             retry = self.retry(fut)
         elif exc_info:
-            trial_end_time = now()
+            trial_end_time = _utils.now()
             t, e, tb = exc_info
             _utils.capture(fut, exc_info)
             if isinstance(e, TryAgain):
@@ -256,14 +283,19 @@
             self.after(self.fn, self.statistics['attempt_number'],
                        trial_time_taken)
 
-        delay_since_first_attempt = now() - self.statistics['start_time']
+        delay_since_first_attempt = (
+            _utils.now() - self.statistics['start_time']
+        )
         self.statistics['delay_since_first_attempt'] = \
             delay_since_first_attempt
         if self.stop(self.statistics['attempt_number'],
                      delay_since_first_attempt):
+            if self.retry_error_callback:
+                return self.retry_error_callback(fut)
+            retry_exc = self.retry_error_cls(fut)
             if self.reraise:
-                raise RetryError(fut).reraise()
-            six.raise_from(RetryError(fut), fut.exception())
+                raise retry_exc.reraise()
+            six.raise_from(retry_exc, fut.exception())
 
         if self.wait:
             if self._wait_takes_result:
@@ -277,6 +309,9 @@
         self.statistics['idle_for'] += sleep
         self.statistics['attempt_number'] += 1
 
+        if self.before_sleep is not None:
+            self.before_sleep(self, sleep=sleep, last_result=fut)
+
         return DoSleep(sleep)
 
 
@@ -288,7 +323,7 @@
 
         result = NO_RESULT
         exc_info = None
-        start_time = now()
+        start_time = _utils.now()
 
         while True:
             do = self.iter(result=result, exc_info=exc_info,
@@ -297,7 +332,7 @@
                 try:
                     result = fn(*args, **kwargs)
                     continue
-                except Exception:
+                except BaseException:
                     exc_info = sys.exc_info()
                     continue
             elif isinstance(do, DoSleep):
@@ -333,23 +368,8 @@
         return fut
 
 
-class RetryError(Exception):
-    """Encapsulates the last attempt instance right before giving up."""
-
-    def __init__(self, last_attempt):
-        self.last_attempt = last_attempt
-
-    def reraise(self):
-        if self.last_attempt.failed:
-            raise self.last_attempt.result()
-        raise self
-
-    def __str__(self):
-        return "RetryError[{0}]".format(self.last_attempt)
-
-
 if asyncio:
-    from tenacity.async import AsyncRetrying
+    from tenacity._asyncio import AsyncRetrying
 
 if tornado:
     from tenacity.tornadoweb import TornadoRetrying
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tenacity-4.8.0/tenacity/_asyncio.py 
new/tenacity-4.12.0/tenacity/_asyncio.py
--- old/tenacity-4.8.0/tenacity/_asyncio.py     1970-01-01 01:00:00.000000000 
+0100
+++ new/tenacity-4.12.0/tenacity/_asyncio.py    2018-05-01 12:18:58.000000000 
+0200
@@ -0,0 +1,62 @@
+# -*- coding: utf-8 -*-
+# Copyright 2016 Étienne Bersac
+# Copyright 2016 Julien Danjou
+# Copyright 2016 Joshua Harlow
+# Copyright 2013-2014 Ray Holder
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# 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 asyncio
+import sys
+
+from tenacity import BaseRetrying
+from tenacity import DoAttempt
+from tenacity import DoSleep
+from tenacity import NO_RESULT
+from tenacity import _utils
+
+
+class AsyncRetrying(BaseRetrying):
+
+    def __init__(self,
+                 sleep=asyncio.sleep,
+                 **kwargs):
+        super(AsyncRetrying, self).__init__(**kwargs)
+        self.sleep = sleep
+
+    @asyncio.coroutine
+    def call(self, fn, *args, **kwargs):
+        self.begin(fn)
+
+        result = NO_RESULT
+        exc_info = None
+        start_time = _utils.now()
+
+        while True:
+            do = self.iter(result=result, exc_info=exc_info,
+                           start_time=start_time)
+            if isinstance(do, DoAttempt):
+                try:
+                    result = yield from fn(*args, **kwargs)
+                    exc_info = None
+                    continue
+                except BaseException:
+                    result = NO_RESULT
+                    exc_info = sys.exc_info()
+                    continue
+            elif isinstance(do, DoSleep):
+                result = NO_RESULT
+                exc_info = None
+                yield from self.sleep(do)
+            else:
+                return do
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tenacity-4.8.0/tenacity/_utils.py 
new/tenacity-4.12.0/tenacity/_utils.py
--- old/tenacity-4.8.0/tenacity/_utils.py       2017-12-14 09:53:01.000000000 
+0100
+++ new/tenacity-4.12.0/tenacity/_utils.py      2018-05-01 12:18:58.000000000 
+0200
@@ -16,6 +16,7 @@
 
 import inspect
 import sys
+import time
 
 import six
 
@@ -31,10 +32,17 @@
         # TODO(harlowja): delete this in future, since its
         # has to repeatedly calculate this crap.
         fut.set_exception_info(tb[1], tb[2])
+
+    def getargspec(func):
+        # This was deprecated in Python 3.
+        return inspect.getargspec(func)
 else:
     def capture(fut, tb):
         fut.set_exception(tb[1])
 
+    def getargspec(func):
+        return inspect.getfullargspec(func)
+
 
 def visible_attrs(obj, attrs=None):
     if attrs is None:
@@ -97,3 +105,9 @@
         except AttributeError:
             pass
         return ".".join(segments)
+
+
+try:
+    now = time.monotonic  # noqa
+except AttributeError:
+    from monotonic import monotonic as now  # noqa
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tenacity-4.8.0/tenacity/async.py 
new/tenacity-4.12.0/tenacity/async.py
--- old/tenacity-4.8.0/tenacity/async.py        2017-12-14 09:53:01.000000000 
+0100
+++ new/tenacity-4.12.0/tenacity/async.py       1970-01-01 01:00:00.000000000 
+0100
@@ -1,63 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright 2016 Étienne Bersac
-# Copyright 2016 Julien Danjou
-# Copyright 2016 Joshua Harlow
-# Copyright 2013-2014 Ray Holder
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# 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 asyncio
-import sys
-
-from monotonic import monotonic as now
-
-from tenacity import BaseRetrying
-from tenacity import DoAttempt
-from tenacity import DoSleep
-from tenacity import NO_RESULT
-
-
-class AsyncRetrying(BaseRetrying):
-
-    def __init__(self,
-                 sleep=asyncio.sleep,
-                 **kwargs):
-        super(AsyncRetrying, self).__init__(**kwargs)
-        self.sleep = sleep
-
-    @asyncio.coroutine
-    def call(self, fn, *args, **kwargs):
-        self.begin(fn)
-
-        result = NO_RESULT
-        exc_info = None
-        start_time = now()
-
-        while True:
-            do = self.iter(result=result, exc_info=exc_info,
-                           start_time=start_time)
-            if isinstance(do, DoAttempt):
-                try:
-                    result = yield from fn(*args, **kwargs)
-                    exc_info = None
-                    continue
-                except Exception:
-                    result = NO_RESULT
-                    exc_info = sys.exc_info()
-                    continue
-            elif isinstance(do, DoSleep):
-                result = NO_RESULT
-                exc_info = None
-                yield from self.sleep(do)
-            else:
-                return do
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tenacity-4.8.0/tenacity/before_sleep.py 
new/tenacity-4.12.0/tenacity/before_sleep.py
--- old/tenacity-4.8.0/tenacity/before_sleep.py 1970-01-01 01:00:00.000000000 
+0100
+++ new/tenacity-4.12.0/tenacity/before_sleep.py        2018-05-01 
12:18:58.000000000 +0200
@@ -0,0 +1,33 @@
+# Copyright 2016 Julien Danjou
+# Copyright 2016 Joshua Harlow
+# Copyright 2013-2014 Ray Holder
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# 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.
+
+from tenacity import _utils
+
+
+def before_sleep_nothing(retry_obj, sleep, last_result):
+    """Before call strategy that does nothing."""
+
+
+def before_sleep_log(logger, log_level):
+    """Before call strategy that logs to some logger the attempt."""
+    def log_it(retry_obj, sleep, last_result):
+        logger.log(log_level,
+                   "Retrying %s in %d seconds as it raised %s.",
+                   _utils.get_callback_name(retry_obj.fn),
+                   sleep,
+                   last_result.exception())
+
+    return log_it
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tenacity-4.8.0/tenacity/tests/test_async.py 
new/tenacity-4.12.0/tenacity/tests/test_async.py
--- old/tenacity-4.8.0/tenacity/tests/test_async.py     2017-12-14 
09:53:01.000000000 +0100
+++ new/tenacity-4.12.0/tenacity/tests/test_async.py    1970-01-01 
01:00:00.000000000 +0100
@@ -1,57 +0,0 @@
-# coding: utf-8
-# Copyright 2016 Étienne Bersac
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# 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 asyncio
-import unittest
-
-import six
-
-from tenacity import async
-from tenacity import retry
-from tenacity.tests.test_tenacity import NoIOErrorAfterCount
-
-
-def asynctest(callable_):
-    callable_ = asyncio.coroutine(callable_)
-
-    @six.wraps(callable_)
-    def wrapper(*a, **kw):
-        loop = asyncio.get_event_loop()
-        return loop.run_until_complete(callable_(*a, **kw))
-
-    return wrapper
-
-
-@retry
[email protected]
-def _retryable_coroutine(thing):
-    yield from asyncio.sleep(0.00001)
-    thing.go()
-
-
-class TestAsync(unittest.TestCase):
-    @asynctest
-    def test_retry(self):
-        assert asyncio.iscoroutinefunction(_retryable_coroutine)
-        thing = NoIOErrorAfterCount(5)
-        yield from _retryable_coroutine(thing)
-        assert thing.counter == thing.count
-
-    def test_repr(self):
-        repr(async.AsyncRetrying())
-
-
-if __name__ == '__main__':
-    unittest.main()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tenacity-4.8.0/tenacity/tests/test_asyncio.py 
new/tenacity-4.12.0/tenacity/tests/test_asyncio.py
--- old/tenacity-4.8.0/tenacity/tests/test_asyncio.py   1970-01-01 
01:00:00.000000000 +0100
+++ new/tenacity-4.12.0/tenacity/tests/test_asyncio.py  2018-05-01 
12:18:58.000000000 +0200
@@ -0,0 +1,57 @@
+# coding: utf-8
+# Copyright 2016 Étienne Bersac
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# 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 asyncio
+import unittest
+
+import six
+
+from tenacity import _asyncio as tasyncio
+from tenacity import retry
+from tenacity.tests.test_tenacity import NoIOErrorAfterCount
+
+
+def asynctest(callable_):
+    callable_ = asyncio.coroutine(callable_)
+
+    @six.wraps(callable_)
+    def wrapper(*a, **kw):
+        loop = asyncio.get_event_loop()
+        return loop.run_until_complete(callable_(*a, **kw))
+
+    return wrapper
+
+
+@retry
[email protected]
+def _retryable_coroutine(thing):
+    yield from asyncio.sleep(0.00001)
+    thing.go()
+
+
+class TestAsync(unittest.TestCase):
+    @asynctest
+    def test_retry(self):
+        assert asyncio.iscoroutinefunction(_retryable_coroutine)
+        thing = NoIOErrorAfterCount(5)
+        yield from _retryable_coroutine(thing)
+        assert thing.counter == thing.count
+
+    def test_repr(self):
+        repr(tasyncio.AsyncRetrying())
+
+
+if __name__ == '__main__':
+    unittest.main()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tenacity-4.8.0/tenacity/tests/test_tenacity.py 
new/tenacity-4.12.0/tenacity/tests/test_tenacity.py
--- old/tenacity-4.8.0/tenacity/tests/test_tenacity.py  2017-12-14 
09:53:01.000000000 +0100
+++ new/tenacity-4.12.0/tenacity/tests/test_tenacity.py 2018-05-01 
12:18:58.000000000 +0200
@@ -720,6 +720,27 @@
 
         self.assertTrue(TestBeforeAfterAttempts._attempt_number is 2)
 
+    def test_before_sleep(self):
+        TestBeforeAfterAttempts._attempt_number = 0
+
+        def _before_sleep(retry_obj, sleep, last_result):
+            self.assertGreater(sleep, 0)
+            TestBeforeAfterAttempts._attempt_number = \
+                retry_obj.statistics['attempt_number']
+
+        @retry(wait=tenacity.wait_fixed(0.1),
+               stop=tenacity.stop_after_attempt(3),
+               before_sleep=_before_sleep)
+        def _test_before_sleep():
+            if TestBeforeAfterAttempts._attempt_number < 2:
+                raise Exception("testing before_sleep_attempts handler")
+            else:
+                pass
+
+        _test_before_sleep()
+
+        self.assertTrue(TestBeforeAfterAttempts._attempt_number is 2)
+
 
 class TestReraiseExceptions(unittest.TestCase):
 
@@ -810,5 +831,31 @@
         self.assertEqual(2, _foobar.retry.statistics['attempt_number'])
 
 
+class TestRetryErrorCallback(unittest.TestCase):
+
+    def setUp(self):
+        self._attempt_number = 0
+        self._callback_called = False
+
+    def _callback(self, fut):
+        self._callback_called = True
+        return fut
+
+    def test_retry_error_callback(self):
+        num_attempts = 3
+
+        @retry(stop=tenacity.stop_after_attempt(num_attempts),
+               retry_error_callback=self._callback)
+        def _foobar():
+            self._attempt_number += 1
+            raise Exception("This exception should not be raised")
+
+        result = _foobar()
+
+        self.assertTrue(self._callback_called)
+        self.assertEqual(num_attempts, self._attempt_number)
+        self.assertIsInstance(result, tenacity.Future)
+
+
 if __name__ == '__main__':
     unittest.main()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tenacity-4.8.0/tenacity/tests/test_tornado.py 
new/tenacity-4.12.0/tenacity/tests/test_tornado.py
--- old/tenacity-4.8.0/tenacity/tests/test_tornado.py   2017-12-14 
09:53:01.000000000 +0100
+++ new/tenacity-4.12.0/tenacity/tests/test_tornado.py  2018-05-01 
12:18:58.000000000 +0200
@@ -41,6 +41,19 @@
     def test_repr(self):
         repr(tornadoweb.TornadoRetrying())
 
+    def test_old_tornado(self):
+        old_attr = gen.is_coroutine_function
+        try:
+            del gen.is_coroutine_function
+
+            # is_coroutine_function was introduced in tornado 4.5;
+            # verify that we don't *completely* fall over on old versions
+            @retry
+            def retryable(thing):
+                pass
+        finally:
+            gen.is_coroutine_function = old_attr
+
 
 if __name__ == '__main__':
     unittest.main()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tenacity-4.8.0/tenacity/tornadoweb.py 
new/tenacity-4.12.0/tenacity/tornadoweb.py
--- old/tenacity-4.8.0/tenacity/tornadoweb.py   2017-12-14 09:53:01.000000000 
+0100
+++ new/tenacity-4.12.0/tenacity/tornadoweb.py  2018-05-01 12:18:58.000000000 
+0200
@@ -15,12 +15,11 @@
 
 import sys
 
-from monotonic import monotonic as now
-
 from tenacity import BaseRetrying
 from tenacity import DoAttempt
 from tenacity import DoSleep
 from tenacity import NO_RESULT
+from tenacity import _utils
 
 from tornado import gen
 
@@ -39,7 +38,7 @@
 
         result = NO_RESULT
         exc_info = None
-        start_time = now()
+        start_time = _utils.now()
 
         while True:
             do = self.iter(result=result, exc_info=exc_info,
@@ -49,7 +48,7 @@
                     result = yield fn(*args, **kwargs)
                     exc_info = None
                     continue
-                except Exception:
+                except BaseException:
                     result = NO_RESULT
                     exc_info = sys.exc_info()
                     continue
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tenacity-4.8.0/tenacity.egg-info/PKG-INFO 
new/tenacity-4.12.0/tenacity.egg-info/PKG-INFO
--- old/tenacity-4.8.0/tenacity.egg-info/PKG-INFO       2017-12-14 
09:53:45.000000000 +0100
+++ new/tenacity-4.12.0/tenacity.egg-info/PKG-INFO      2018-05-01 
12:19:48.000000000 +0200
@@ -1,12 +1,11 @@
 Metadata-Version: 1.1
 Name: tenacity
-Version: 4.8.0
+Version: 4.12.0
 Summary: Retry code until it succeeeds
 Home-page: https://github.com/jd/tenacity
 Author: Julien Danjou
 Author-email: [email protected]
-License: UNKNOWN
-Description-Content-Type: UNKNOWN
+License: Apache 2.0
 Description: Tenacity
         ========
         .. image:: https://img.shields.io/pypi/v/tenacity.svg
@@ -263,6 +262,25 @@
             def raise_my_exception():
                 raise MyException("Fail")
         
+        Similarly, you can call a custom callback function after all retries 
failed, without raising an exception (or you can re-raise or do anything really)
+        
+        .. testcode::
+        
+            def return_last_value(last_attempt):
+                """return the result of the last call attempt"""
+                return last_attempt.result()
+        
+            def is_false(value):
+                """Return True if value is False"""
+                return value is False
+        
+            # will return False after trying 3 times to get a different result
+            @retry(stop=stop_after_attempt(3),
+                   retry_error_callback=return_last_value,
+                   retry=retry_if_result(is_false))
+            def eventually_return_false():
+                return False
+        
         You can access the statistics about the retry made over a function by 
using the
         `retry` attribute attached to the function and its `statistics` 
attribute:
         
@@ -320,6 +338,14 @@
             @tornado.gen.coroutine
             def my_async_function(http_client, url):
                 yield http_client.fetch(url)
+                
+        You can even use alternative event loops such as `curio` or `Trio` by 
passing the correct sleep function:
+        
+        .. code-block:: python
+        
+            @retry(sleep=trio.sleep)
+            async def my_async_function(loop):
+                await asks.get('https://example.org')
         
         Contribute
         ----------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tenacity-4.8.0/tenacity.egg-info/SOURCES.txt 
new/tenacity-4.12.0/tenacity.egg-info/SOURCES.txt
--- old/tenacity-4.8.0/tenacity.egg-info/SOURCES.txt    2017-12-14 
09:53:45.000000000 +0100
+++ new/tenacity-4.12.0/tenacity.egg-info/SOURCES.txt   2018-05-01 
12:19:48.000000000 +0200
@@ -1,3 +1,4 @@
+.mergify.yml
 .travis.yml
 AUTHORS
 ChangeLog
@@ -10,10 +11,11 @@
 doc/source/conf.py
 doc/source/index.rst
 tenacity/__init__.py
+tenacity/_asyncio.py
 tenacity/_utils.py
 tenacity/after.py
-tenacity/async.py
 tenacity/before.py
+tenacity/before_sleep.py
 tenacity/nap.py
 tenacity/retry.py
 tenacity/stop.py
@@ -27,6 +29,6 @@
 tenacity.egg-info/requires.txt
 tenacity.egg-info/top_level.txt
 tenacity/tests/__init__.py
-tenacity/tests/test_async.py
+tenacity/tests/test_asyncio.py
 tenacity/tests/test_tenacity.py
 tenacity/tests/test_tornado.py
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tenacity-4.8.0/tenacity.egg-info/pbr.json 
new/tenacity-4.12.0/tenacity.egg-info/pbr.json
--- old/tenacity-4.8.0/tenacity.egg-info/pbr.json       2017-12-14 
09:53:45.000000000 +0100
+++ new/tenacity-4.12.0/tenacity.egg-info/pbr.json      2018-05-01 
12:19:48.000000000 +0200
@@ -1 +1 @@
-{"git_version": "69cf4a7", "is_release": true}
\ No newline at end of file
+{"git_version": "723ba6a", "is_release": true}
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tenacity-4.8.0/tenacity.egg-info/requires.txt 
new/tenacity-4.12.0/tenacity.egg-info/requires.txt
--- old/tenacity-4.8.0/tenacity.egg-info/requires.txt   2017-12-14 
09:53:45.000000000 +0100
+++ new/tenacity-4.12.0/tenacity.egg-info/requires.txt  2018-05-01 
12:19:48.000000000 +0200
@@ -1,5 +1,5 @@
 six>=1.9.0
-monotonic>=0.6
 
 [:(python_version=='2.7')]
 futures>=3.0
+monotonic>=0.6
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tenacity-4.8.0/tox.ini new/tenacity-4.12.0/tox.ini
--- old/tenacity-4.8.0/tox.ini  2017-12-14 09:53:01.000000000 +0100
+++ new/tenacity-4.12.0/tox.ini 2018-05-01 12:18:58.000000000 +0200
@@ -11,13 +11,14 @@
 usedevelop = True
 sitepackages = False
 deps =
-    nose
+    pytest
     sphinx
-    tornado
+    tornado>=4.5
 commands =
-    py{27,py}: python setup.py nosetests --ignore-files '.*async.py'
-    py3{5,6}: python setup.py nosetests
-    python setup.py build_sphinx
+    py{27,py}: pytest --ignore='tenacity/tests/test_asyncio.py' '{posargs}'
+    py3{5,6}: pytest '{posargs}'
+    sphinx-build -a -E -W -b doctest doc/source doc/build
+    sphinx-build -a -E -W -b html doc/source doc/build
 
 [testenv:pep8]
 basepython = python3


Reply via email to