Hello community,

here is the log from the commit of package python-backoff for openSUSE:Factory 
checked in at 2019-06-06 18:16:46
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-backoff (Old)
 and      /work/SRC/openSUSE:Factory/.python-backoff.new.4811 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-backoff"

Thu Jun  6 18:16:46 2019 rev:3 rq:707407 version:1.8.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-backoff/python-backoff.changes    
2018-12-12 17:25:54.395073315 +0100
+++ /work/SRC/openSUSE:Factory/.python-backoff.new.4811/python-backoff.changes  
2019-06-06 18:16:47.116695464 +0200
@@ -1,0 +2,12 @@
+Tue Jun  4 06:33:28 UTC 2019 - [email protected]
+
+- version update to 1.8.0
+  - Change default log level from ERROR to INFO
+  - Log retries on exception as INFO
+  - Support Python 3.7
+  - Give up on StopIteration raised in wait generators
+  - Iterable intervals for constant wait_gen for predefined wait sequences
+  - Nullary jitter signature deprecation warning
+  - Custom loggers
+
+-------------------------------------------------------------------

Old:
----
  backoff-1.5.0.tar.gz

New:
----
  backoff-1.8.0.tar.gz
  tests.tar.bz2

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

Other differences:
------------------
++++++ python-backoff.spec ++++++
--- /var/tmp/diff_new_pack.RJUx2Z/_old  2019-06-06 18:16:47.984695210 +0200
+++ /var/tmp/diff_new_pack.RJUx2Z/_new  2019-06-06 18:16:47.992695208 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package python-backoff
 #
-# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany.
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -17,16 +17,21 @@
 
 
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
-%bcond_without test
 Name:           python-backoff
-Version:        1.5.0
+Version:        1.8.0
 Release:        0
 Summary:        Function decoration for backoff and retry
 License:        MIT
 Group:          Development/Languages/Python
 Url:            https://github.com/litl/backoff
-Source:         
https://files.pythonhosted.org/packages/source/b/backoff/backoff-%{version}.tar.gz
+Source0:        
https://files.pythonhosted.org/packages/source/b/backoff/backoff-%{version}.tar.gz
+# https://github.com/litl/backoff/issues/75
+# github repo does not have setup.py
+Source1:        tests.tar.bz2
 BuildRequires:  %{python_module setuptools}
+# SECTION test requirements
+BuildRequires:  %{python_module pytest}
+# /SECTION
 BuildRequires:  fdupes
 BuildRequires:  python-rpm-macros
 BuildArch:      noarch
@@ -46,7 +51,7 @@
 for asynchronous code.
 
 %prep
-%setup -q -n backoff-%{version}
+%setup -q -n backoff-%{version} -a1
 
 %build
 %python_build
@@ -55,6 +60,11 @@
 %python_install
 %python_expand %fdupes %{buildroot}%{$python_sitelib}
 
+%check
+# will not work with python 2.7
+rm -r tests/python35
+%pytest
+
 %files %{python_files}
 %defattr(-,root,root,-)
 %doc README.rst

++++++ backoff-1.5.0.tar.gz -> backoff-1.8.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/backoff-1.5.0/PKG-INFO new/backoff-1.8.0/PKG-INFO
--- old/backoff-1.5.0/PKG-INFO  2018-04-11 17:03:41.000000000 +0200
+++ new/backoff-1.8.0/PKG-INFO  1970-01-01 01:00:00.000000000 +0100
@@ -1,350 +1,26 @@
-Metadata-Version: 1.1
+Metadata-Version: 2.1
 Name: backoff
-Version: 1.5.0
+Version: 1.8.0
 Summary: Function decoration for backoff and retry
 Home-page: https://github.com/litl/backoff
 Author: Bob Green
 Author-email: [email protected]
-License: MIT
-Download-URL: https://github.com/litl/backoff/tarball/v1.5.0
-Description: backoff
-        =======
-        
-        .. image:: https://travis-ci.org/litl/backoff.svg?branch=master
-            :target: https://travis-ci.org/litl/backoff?branch=master
-        .. image:: 
https://coveralls.io/repos/litl/backoff/badge.svg?branch=master
-            :target: https://coveralls.io/r/litl/backoff?branch=master
-        .. image:: https://img.shields.io/pypi/v/backoff.svg
-            :target: https://pypi.python.org/pypi/backoff
-        
-        **Function decoration for backoff and retry**
-        
-        This module provides function decorators which can be used to wrap a
-        function such that it will be retried until some condition is met. It
-        is meant to be of use when accessing unreliable resources with the
-        potential for intermittent failures i.e. network resources and external
-        APIs. Somewhat more generally, it may also be of use for dynamically
-        polling resources for externally generated content.
-        
-        Decorators support both regular functions for synchronous code and
-        `asyncio <https://docs.python.org/3/library/asyncio.html>`_'s 
coroutines
-        for asynchronous code.
-        
-        Examples
-        ========
-        
-        Since Kenneth Reitz's `requests <http://python-requests.org>`_ module
-        has become a defacto standard for synchronous HTTP clients in Python,
-        networking examples below are written using it, but it is in no way 
required
-        by the backoff module.
-        
-        @backoff.on_exception
-        ---------------------
-        
-        The ``on_exception`` decorator is used to retry when a specified 
exception
-        is raised. Here's an example using exponential backoff when any
-        ``requests`` exception is raised:
-        
-        .. code-block:: python
-        
-            @backoff.on_exception(backoff.expo,
-                                  requests.exceptions.RequestException)
-            def get_url(url):
-                return requests.get(url)
-        
-        The decorator will also accept a tuple of exceptions for cases where
-        you want the same backoff behavior for more than one exception type:
-        
-        .. code-block:: python
-        
-            @backoff.on_exception(backoff.expo,
-                                  (requests.exceptions.Timeout,
-                                   requests.exceptions.ConnectionError))
-            def get_url(url):
-                return requests.get(url)
-        
-        **Give Up Conditions**
-        
-        Optional keyword arguments can specify conditions under which to give
-        up.
-        
-        The keyword argument ``max_time`` specifies the maximum amount
-        of total time in seconds that can elapse before giving up.
-        
-        .. code-block:: python
-        
-            @backoff.on_exception(backoff.expo,
-                                  requests.exceptions.RequestException,
-                                  max_time=60)
-            def get_url(url):
-                return requests.get(url)
-        
-        
-        Keyword argument ``max_tries`` specifies the maximum number of calls
-        to make to the target function before giving up.
-        
-        .. code-block:: python
-        
-            @backoff.on_exception(backoff.expo,
-                                  requests.exceptions.RequestException,
-                                  max_tries=8,
-                                  jitter=None)
-            def get_url(url):
-                return requests.get(url)
-        
-        
-        In some cases the raised exception instance itself may need to be
-        inspected in order to determine if it is a retryable condition. The
-        ``giveup`` keyword arg can be used to specify a function which accepts
-        the exception and returns a truthy value if the exception should not
-        be retried:
-        
-        .. code-block:: python
-        
-            def fatal_code(e):
-                return 400 <= e.response.status_code < 500
-        
-            @backoff.on_exception(backoff.expo,
-                                  requests.exceptions.RequestException,
-                                  max_time=300,
-                                  giveup=fatal_code)
-            def get_url(url):
-                return requests.get(url)
-        
-        When a give up event occurs, the exception in question is reraised
-        and so code calling an `on_exception`-decorated function may still
-        need to do exception handling.
-        
-        @backoff.on_predicate
-        ---------------------
-        
-        The ``on_predicate`` decorator is used to retry when a particular
-        condition is true of the return value of the target function.  This may
-        be useful when polling a resource for externally generated content.
-        
-        Here's an example which uses a fibonacci sequence backoff when the
-        return value of the target function is the empty list:
-        
-        .. code-block:: python
-        
-            @backoff.on_predicate(backoff.fibo, lambda x: x == [], 
max_value=13)
-            def poll_for_messages(queue):
-                return queue.get()
-        
-        Extra keyword arguments are passed when initializing the
-        wait generator, so the ``max_value`` param above is passed as a keyword
-        arg when initializing the fibo generator.
-        
-        When not specified, the predicate param defaults to the falsey test,
-        so the above can more concisely be written:
-        
-        .. code-block:: python
-        
-            @backoff.on_predicate(backoff.fibo, max_value=13)
-            def poll_for_message(queue)
-                return queue.get()
-        
-        More simply, a function which continues polling every second until it
-        gets a non-falsey result could be defined like like this:
-        
-        .. code-block:: python
-        
-            @backoff.on_predicate(backoff.constant, interval=1)
-            def poll_for_message(queue)
-                return queue.get()
-        
-        Jitter
-        ------
-        
-        A jitter algorithm can be supplied with the ``jitter`` keyword arg to
-        either of the backoff decorators. This argument should be a function
-        accepting the original unadulterated backoff value and returning it's
-        jittered counterpart.
-        
-        As of version 1.2, the default jitter function ``backoff.full_jitter``
-        implements the 'Full Jitter' algorithm as defined in the AWS
-        Architecture Blog's `Exponential Backoff And Jitter
-        <https://www.awsarchitectureblog.com/2015/03/backoff.html>`_ post.
-        Note that with this algorithm, the time yielded by the wait generator
-        is actually the *maximum* amount of time to wait.
-        
-        Previous versions of backoff defaulted to adding some random number of
-        milliseconds (up to 1s) to the raw sleep value. If desired, this
-        behavior is now available as ``backoff.random_jitter``.
-        
-        Using multiple decorators
-        -------------------------
-        
-        The backoff decorators may also be combined to specify different
-        backoff behavior for different cases:
-        
-        .. code-block:: python
-        
-            @backoff.on_predicate(backoff.fibo, max_value=13)
-            @backoff.on_exception(backoff.expo,
-                                  requests.exceptions.HTTPError,
-                                  max_time=60)
-            @backoff.on_exception(backoff.expo,
-                                  requests.exceptions.TimeoutError,
-                                  max_time=300)
-            def poll_for_message(queue):
-                return queue.get()
-        
-        Runtime Configuration
-        ---------------------
-        
-        The decorator functions ``on_exception`` and ``on_predicate`` are
-        generally evaluated at import time. This is fine when the keyword args
-        are passed as constant values, but suppose we want to consult a
-        dictionary with configuration options that only become available at
-        runtime. The relevant values are not available at import time. Instead,
-        decorator functions can be passed callables which are evaluated at
-        runtime to obtain the value:
-        
-        .. code-block:: python
-        
-            def lookup_max_time():
-                # pretend we have a global reference to 'app' here
-                # and that it has a dictionary-like 'config' property
-                return app.config["BACKOFF_MAX_TIME"]
-        
-            @backoff.on_exception(backoff.expo,
-                                  ValueError,
-                                  max_time=lookup_max_time)
-        
-        Event handlers
-        --------------
-        
-        Both backoff decorators optionally accept event handler functions
-        using the keyword arguments ``on_success``, ``on_backoff``, and 
``on_giveup``.
-        This may be useful in reporting statistics or performing other custom
-        logging.
-        
-        Handlers must be callables with a unary signature accepting a dict
-        argument. This dict contains the details of the invocation. Valid keys
-        include:
-        
-        * *target*: reference to the function or method being invoked
-        * *args*: positional arguments to func
-        * *kwargs*: keyword arguments to func
-        * *tries*: number of invocation tries so far
-        * *elapsed*: elapsed time in seconds so far
-        * *wait*: seconds to wait (``on_backoff`` handler only)
-        * *value*: value triggering backoff (``on_predicate`` decorator only)
-        
-        A handler which prints the details of the backoff event could be
-        implemented like so:
-        
-        .. code-block:: python
-        
-            def backoff_hdlr(details):
-                print ("Backing off {wait:0.1f} seconds afters {tries} tries "
-                       "calling function {target} with args {args} and kwargs "
-                       "{kwargs}".format(**details))
-        
-            @backoff.on_exception(backoff.expo,
-                                  requests.exceptions.RequestException,
-                                  on_backoff=backoff_hdlr)
-            def get_url(url):
-                return requests.get(url)
-        
-        **Multiple handlers per event type**
-        
-        In all cases, iterables of handler functions are also accepted, which
-        are called in turn. For example, you might provide a simple list of
-        handler functions as the value of the ``on_backoff`` keyword arg:
-        
-        .. code-block:: python
-        
-            @backoff.on_exception(backoff.expo,
-                                  requests.exceptions.RequestException,
-                                  on_backoff=[backoff_hdlr1, backoff_hdlr2])
-            def get_url(url):
-                return requests.get(url)
-        
-        **Getting exception info**
-        
-        In the case of the ``on_exception`` decorator, all ``on_backoff`` and
-        ``on_giveup`` handlers are called from within the except block for the
-        exception being handled. Therefore exception info is available to the
-        handler functions via the python standard library, specifically
-        ``sys.exc_info()`` or the ``traceback`` module.
-        
-        Asynchronous code
-        -----------------
-        
-        To use backoff in asynchronous code based on
-        `asyncio <https://docs.python.org/3/library/asyncio.html>`_
-        you simply need to apply ``backoff.on_exception`` or 
``backoff.on_predicate``
-        to coroutines.
-        You can also use coroutines for the ``on_success``, ``on_backoff``, and
-        ``on_giveup`` event handlers, with the interface otherwise being 
identical.
-        
-        The following examples use `aiohttp <https://aiohttp.readthedocs.io/>`_
-        asynchronous HTTP client/server library.
-        
-        On Python 3.5 and above with ``async def`` and ``await`` syntax:
-        
-        .. code-block:: python
-        
-            @backoff.on_exception(backoff.expo, aiohttp.ClientError, 
max_time=60)
-            async def get_url(url):
-                async with aiohttp.ClientSession() as session:
-                    async with session.get(url) as response:
-                        return await response.text()
-        
-        In case you use Python 3.4 you can use `@asyncio.coroutine` and `yield 
from`:
-        
-        .. code-block:: python
-        
-            @backoff.on_exception(backoff.expo, aiohttp.ClientError, 
max_time=60)
-            @asyncio.coroutine
-            def get_url_py34(url):
-                with aiohttp.ClientSession() as session:
-                    response = yield from session.get(url)
-                    try:
-                        return (yield from response.text())
-                    except Exception:
-                        response.close()
-                        raise
-                    finally:
-                        yield from response.release()
-        
-        Logging configuration
-        ---------------------
-        
-        Errors and backoff and retry attempts are logged to the 'backoff'
-        logger. By default, this logger is configured with a NullHandler, so
-        there will be nothing output unless you configure a handler.
-        Programmatically, this might be accomplished with something as simple
-        as:
-        
-        .. code-block:: python
-        
-            logging.getLogger('backoff').addHandler(logging.StreamHandler())
-        
-        The default logging level is ERROR, which corresponds to logging
-        anytime a giveup event occurs. If you would instead like to log
-        anytime a retry occurs, set the logger level to INFO.
-        
-        .. code-block:: python
-        
-            logging.getLogger('backoff').setLevel(logging.INFO)
-        
-Keywords: backoff function decorator
-Platform: UNKNOWN
+Keywords: retry,backoff,decorators
+Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
 Classifier: Development Status :: 5 - Production/Stable
 Classifier: Intended Audience :: Developers
-Classifier: Programming Language :: Python
 Classifier: License :: OSI Approved :: MIT License
 Classifier: Natural Language :: English
 Classifier: Operating System :: OS Independent
 Classifier: Programming Language :: Python
-Classifier: Programming Language :: Python :: 2.6
+Classifier: Programming Language :: Python :: 2
 Classifier: Programming Language :: Python :: 2.7
 Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: Implementation
-Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
 Classifier: Topic :: Internet :: WWW/HTTP
 Classifier: Topic :: Software Development :: Libraries :: Python Modules
 Classifier: Topic :: Utilities
+Project-URL: Repository, https://github.com/litl/backoff
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/backoff-1.5.0/README.rst new/backoff-1.8.0/README.rst
--- old/backoff-1.5.0/README.rst        2018-04-11 16:55:35.000000000 +0200
+++ new/backoff-1.8.0/README.rst        2018-12-15 18:22:25.000000000 +0100
@@ -44,7 +44,7 @@
         return requests.get(url)
 
 The decorator will also accept a tuple of exceptions for cases where
-you want the same backoff behavior for more than one exception type:
+the same backoff behavior is desired for more than one exception type:
 
 .. code-block:: python
 
@@ -264,6 +264,8 @@
 Asynchronous code
 -----------------
 
+Backoff supports asynchronous execution in Python 3.5 and above.
+
 To use backoff in asynchronous code based on
 `asyncio <https://docs.python.org/3/library/asyncio.html>`_
 you simply need to apply ``backoff.on_exception`` or ``backoff.on_predicate``
@@ -274,8 +276,6 @@
 The following examples use `aiohttp <https://aiohttp.readthedocs.io/>`_
 asynchronous HTTP client/server library.
 
-On Python 3.5 and above with ``async def`` and ``await`` syntax:
-
 .. code-block:: python
 
     @backoff.on_exception(backoff.expo, aiohttp.ClientError, max_time=60)
@@ -284,27 +284,10 @@
             async with session.get(url) as response:
                 return await response.text()
 
-In case you use Python 3.4 you can use `@asyncio.coroutine` and `yield from`:
-
-.. code-block:: python
-
-    @backoff.on_exception(backoff.expo, aiohttp.ClientError, max_time=60)
-    @asyncio.coroutine
-    def get_url_py34(url):
-        with aiohttp.ClientSession() as session:
-            response = yield from session.get(url)
-            try:
-                return (yield from response.text())
-            except Exception:
-                response.close()
-                raise
-            finally:
-                yield from response.release()
-
 Logging configuration
 ---------------------
 
-Errors and backoff and retry attempts are logged to the 'backoff'
+By default, backoff and retry attempts are logged to the 'backoff'
 logger. By default, this logger is configured with a NullHandler, so
 there will be nothing output unless you configure a handler.
 Programmatically, this might be accomplished with something as simple
@@ -314,10 +297,40 @@
 
     logging.getLogger('backoff').addHandler(logging.StreamHandler())
 
-The default logging level is ERROR, which corresponds to logging
-anytime a giveup event occurs. If you would instead like to log
-anytime a retry occurs, set the logger level to INFO.
+The default logging level is INFO, which corresponds to logging
+anytime a retry event occurs. If you would instead like to log
+only when a giveup event occurs, set the logger level to ERROR.
 
 .. code-block:: python
 
-    logging.getLogger('backoff').setLevel(logging.INFO)
+    logging.getLogger('backoff').setLevel(logging.ERROR)
+
+It is also possible to specify an alternate logger with the ``logger``
+keyword argument.  If a string value is specified the logger will be
+looked up by name.
+
+.. code-block:: python
+
+   @backoff.on_exception(backoff.expo,
+                         requests.exception.RequestException,
+                        logger='my_logger')
+   # ...
+
+It is also supported to specify a Logger (or LoggerAdapter) object
+directly.
+
+.. code-block:: python
+
+    my_logger = logging.getLogger('my_logger')
+    my_handler = logging.StreamHandler()
+    my_logger.add_handler(my_handler)
+    my_logger.setLevel(logging.ERROR)
+
+    @backoff.on_exception(backoff.expo,
+                         requests.exception.RequestException,
+                        logger=my_logger)
+    # ...
+
+Default logging can be disabled all together by specifying
+``logger=None``. In this case, if desired alternative logging behavior
+could be defined by using custom event handlers.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/backoff-1.5.0/backoff/__init__.py 
new/backoff-1.8.0/backoff/__init__.py
--- old/backoff-1.5.0/backoff/__init__.py       2018-04-11 16:55:35.000000000 
+0200
+++ new/backoff-1.8.0/backoff/__init__.py       2018-12-21 06:19:40.000000000 
+0100
@@ -26,4 +26,4 @@
     'random_jitter'
 ]
 
-__version__ = '1.5.0'
+__version__ = '1.8.0'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/backoff-1.5.0/backoff/_async.py 
new/backoff-1.8.0/backoff/_async.py
--- old/backoff-1.5.0/backoff/_async.py 2018-04-11 16:55:35.000000000 +0200
+++ new/backoff-1.8.0/backoff/_async.py 2018-12-15 18:22:25.000000000 +0100
@@ -3,10 +3,9 @@
 import functools
 # Python 3.4 code and syntax is allowed in this module!
 import asyncio
+from datetime import timedelta
 
-from backoff._common import (_handlers, _init_wait_gen,
-                             _log_backoff, _log_giveup, _maybe_call,
-                             _next_wait, _total_seconds)
+from backoff._common import (_init_wait_gen, _maybe_call, _next_wait)
 
 
 def _ensure_coroutine(coro_or_func):
@@ -20,8 +19,7 @@
     return [_ensure_coroutine(f) for f in coros_or_funcs]
 
 
[email protected]
-def _call_handlers(hdlrs, target, args, kwargs, tries, elapsed, **extra):
+async def _call_handlers(hdlrs, target, args, kwargs, tries, elapsed, **extra):
     details = {
         'target': target,
         'args': args,
@@ -31,16 +29,16 @@
     }
     details.update(extra)
     for hdlr in hdlrs:
-        yield from hdlr(details)
+        await hdlr(details)
 
 
 def retry_predicate(target, wait_gen, predicate,
                     max_tries, max_time, jitter,
                     on_success, on_backoff, on_giveup,
                     wait_gen_kwargs):
-    success_hdlrs = _ensure_coroutines(_handlers(on_success))
-    backoff_hdlrs = _ensure_coroutines(_handlers(on_backoff, _log_backoff))
-    giveup_hdlrs = _ensure_coroutines(_handlers(on_giveup, _log_giveup))
+    on_success = _ensure_coroutines(on_success)
+    on_backoff = _ensure_coroutines(on_backoff)
+    on_giveup = _ensure_coroutines(on_giveup)
 
     # Easy to implement, please report if you need this.
     assert not asyncio.iscoroutinefunction(max_tries)
@@ -49,8 +47,7 @@
     assert asyncio.iscoroutinefunction(target)
 
     @functools.wraps(target)
-    @asyncio.coroutine
-    def retry(*args, **kwargs):
+    async def retry(*args, **kwargs):
 
         # change names because python 2.x doesn't have nonlocal
         max_tries_ = _maybe_call(max_tries)
@@ -61,24 +58,27 @@
         wait = _init_wait_gen(wait_gen, wait_gen_kwargs)
         while True:
             tries += 1
-            elapsed = _total_seconds(datetime.datetime.now() - start)
+            elapsed = timedelta.total_seconds(datetime.datetime.now() - start)
             details = (target, args, kwargs, tries, elapsed)
 
-            ret = yield from target(*args, **kwargs)
+            ret = await target(*args, **kwargs)
             if predicate(ret):
                 max_tries_exceeded = (tries == max_tries_)
                 max_time_exceeded = (max_time_ is not None and
                                      elapsed >= max_time_)
 
                 if max_tries_exceeded or max_time_exceeded:
-                    yield from _call_handlers(
-                        giveup_hdlrs, *details, value=ret)
+                    await _call_handlers(on_giveup, *details, value=ret)
                     break
 
-                seconds = _next_wait(wait, jitter, elapsed, max_time_)
+                try:
+                    seconds = _next_wait(wait, jitter, elapsed, max_time_)
+                except StopIteration:
+                    await _call_handlers(on_giveup, *details, value=ret)
+                    break
 
-                yield from _call_handlers(
-                    backoff_hdlrs, *details, value=ret, wait=seconds)
+                await _call_handlers(on_backoff, *details, value=ret,
+                                     wait=seconds)
 
                 # Note: there is no convenient way to pass explicit event
                 # loop to decorator, so here we assume that either default
@@ -89,10 +89,10 @@
                 # See for details:
                 #   
<https://groups.google.com/forum/#!topic/python-tulip/yF9C-rFpiKk>
                 #   <https://bugs.python.org/issue28613>
-                yield from asyncio.sleep(seconds)
+                await asyncio.sleep(seconds)
                 continue
             else:
-                yield from _call_handlers(success_hdlrs, *details, value=ret)
+                await _call_handlers(on_success, *details, value=ret)
                 break
 
         return ret
@@ -104,9 +104,9 @@
                     max_tries, max_time, jitter, giveup,
                     on_success, on_backoff, on_giveup,
                     wait_gen_kwargs):
-    success_hdlrs = _ensure_coroutines(_handlers(on_success))
-    backoff_hdlrs = _ensure_coroutines(_handlers(on_backoff, _log_backoff))
-    giveup_hdlrs = _ensure_coroutines(_handlers(on_giveup, _log_giveup))
+    on_success = _ensure_coroutines(on_success)
+    on_backoff = _ensure_coroutines(on_backoff)
+    on_giveup = _ensure_coroutines(on_giveup)
     giveup = _ensure_coroutine(giveup)
 
     # Easy to implement, please report if you need this.
@@ -114,8 +114,7 @@
     assert not asyncio.iscoroutinefunction(jitter)
 
     @functools.wraps(target)
-    @asyncio.coroutine
-    def retry(*args, **kwargs):
+    async def retry(*args, **kwargs):
         # change names because python 2.x doesn't have nonlocal
         max_tries_ = _maybe_call(max_tries)
         max_time_ = _maybe_call(max_time)
@@ -125,25 +124,28 @@
         wait = _init_wait_gen(wait_gen, wait_gen_kwargs)
         while True:
             tries += 1
-            elapsed = _total_seconds(datetime.datetime.now() - start)
+            elapsed = timedelta.total_seconds(datetime.datetime.now() - start)
             details = (target, args, kwargs, tries, elapsed)
 
             try:
-                ret = yield from target(*args, **kwargs)
+                ret = await target(*args, **kwargs)
             except exception as e:
-                giveup_result = yield from giveup(e)
+                giveup_result = await giveup(e)
                 max_tries_exceeded = (tries == max_tries_)
                 max_time_exceeded = (max_time_ is not None and
                                      elapsed >= max_time_)
 
                 if giveup_result or max_tries_exceeded or max_time_exceeded:
-                    yield from _call_handlers(giveup_hdlrs, *details)
+                    await _call_handlers(on_giveup, *details)
                     raise
 
-                seconds = _next_wait(wait, jitter, elapsed, max_time_)
+                try:
+                    seconds = _next_wait(wait, jitter, elapsed, max_time_)
+                except StopIteration:
+                    await _call_handlers(on_giveup, *details)
+                    raise e
 
-                yield from _call_handlers(
-                    backoff_hdlrs, *details, wait=seconds)
+                await _call_handlers(on_backoff, *details, wait=seconds)
 
                 # Note: there is no convenient way to pass explicit event
                 # loop to decorator, so here we assume that either default
@@ -154,9 +156,9 @@
                 # See for details:
                 #   
<https://groups.google.com/forum/#!topic/python-tulip/yF9C-rFpiKk>
                 #   <https://bugs.python.org/issue28613>
-                yield from asyncio.sleep(seconds)
+                await asyncio.sleep(seconds)
             else:
-                yield from _call_handlers(success_hdlrs, *details)
+                await _call_handlers(on_success, *details)
 
                 return ret
     return retry
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/backoff-1.5.0/backoff/_common.py 
new/backoff-1.8.0/backoff/_common.py
--- old/backoff-1.5.0/backoff/_common.py        2018-04-11 16:55:35.000000000 
+0200
+++ new/backoff-1.8.0/backoff/_common.py        2018-12-15 18:22:25.000000000 
+0100
@@ -1,22 +1,16 @@
 # coding:utf-8
 
+import functools
 import logging
 import sys
 import traceback
+import warnings
 
 
 # Use module-specific logger with a default null handler.
-logger = logging.getLogger('backoff')
-
-if sys.version_info < (2, 7, 0):  # pragma: no cover
-    class NullHandler(logging.Handler):
-        def emit(self, record):
-            pass
-    logger.addHandler(NullHandler())
-else:
-    logger.addHandler(logging.NullHandler())  # pragma: no cover
-
-logger.setLevel(logging.ERROR)
+_logger = logging.getLogger('backoff')
+_logger.addHandler(logging.NullHandler())  # pragma: no cover
+_logger.setLevel(logging.INFO)
 
 
 # Evaluate arg that can be either a fixed value or a callable.
@@ -26,8 +20,7 @@
 
 def _init_wait_gen(wait_gen, wait_gen_kwargs):
     # there are no dictionary comprehensions in python 2.6
-    kwargs = dict((k, _maybe_call(v))
-                  for k, v in wait_gen_kwargs.items())
+    kwargs = {k: _maybe_call(v) for k, v in wait_gen_kwargs.items()}
     return wait_gen(**kwargs)
 
 
@@ -39,8 +32,14 @@
         else:
             seconds = value
     except TypeError:
-        # support deprecated nullary jitter function signature
-        # which returns a delta rather than a jittered value
+        warnings.warn(
+            "Nullary jitter function signature is deprecated. Use "
+            "unary signature accepting a wait value in seconds and "
+            "returning a jittered version of it.",
+            DeprecationWarning,
+            stacklevel=2,
+        )
+
         seconds = value + jitter()
 
     # don't sleep longer than remaining alloted max_time
@@ -50,52 +49,54 @@
     return seconds
 
 
-# Create default handler list from keyword argument
-def _handlers(hdlr, default=None):
-    defaults = [default] if default is not None else []
-
-    if hdlr is None:
-        return defaults
-
-    if hasattr(hdlr, '__iter__'):
-        return defaults + list(hdlr)
+# Configure handler list with user specified handler and optionally
+# with a default handler bound to the specified logger.
+def _config_handlers(user_handlers, default_handler=None, logger=None):
+    handlers = []
+    if logger is not None:
+        # bind the specified logger to the default log handler
+        log_handler = functools.partial(default_handler, logger=logger)
+        handlers.append(log_handler)
+
+    if user_handlers is None:
+        return handlers
+
+    # user specified handlers can either be an iterable of handlers
+    # or a single handler. either way append them to the list.
+    if hasattr(user_handlers, '__iter__'):
+        # add all handlers in the iterable
+        handlers += list(user_handlers)
+    else:
+        # append a single handler
+        handlers.append(user_handlers)
 
-    return defaults + [hdlr]
+    return handlers
 
 
 # Default backoff handler
-def _log_backoff(details):
+def _log_backoff(details, logger):
     fmt = "Backing off {0}(...) for {1:.1f}s"
     msg = fmt.format(details['target'].__name__, details['wait'])
 
     exc_typ, exc, _ = sys.exc_info()
     if exc is not None:
         exc_fmt = traceback.format_exception_only(exc_typ, exc)[-1]
-        msg = "{0} ({1})".format(msg, exc_fmt.rstrip("\n"))
-        logger.error(msg)
+        msg = "{} ({})".format(msg, exc_fmt.rstrip("\n"))
     else:
-        msg = "{0} ({1})".format(msg, details['value'])
-        logger.info(msg)
+        msg = "{} ({})".format(msg, details['value'])
+    logger.info(msg)
 
 
 # Default giveup handler
-def _log_giveup(details):
+def _log_giveup(details, logger):
     fmt = "Giving up {0}(...) after {1} tries"
     msg = fmt.format(details['target'].__name__, details['tries'])
 
     exc_typ, exc, _ = sys.exc_info()
     if exc is not None:
         exc_fmt = traceback.format_exception_only(exc_typ, exc)[-1]
-        msg = "{0} ({1})".format(msg, exc_fmt.rstrip("\n"))
+        msg = "{} ({})".format(msg, exc_fmt.rstrip("\n"))
     else:
-        msg = "{0} ({1})".format(msg, details['value'])
+        msg = "{} ({})".format(msg, details['value'])
 
     logger.error(msg)
-
-
-# Python 2.6 datetime.timedelta does not have total_seconds()
-# so we do our own implementation here.
-def _total_seconds(timedelta):
-    return (
-        (timedelta.microseconds + 0.0 +
-         (timedelta.seconds + timedelta.days * 24 * 3600) * 10**6) / 10**6)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/backoff-1.5.0/backoff/_decorator.py 
new/backoff-1.8.0/backoff/_decorator.py
--- old/backoff-1.5.0/backoff/_decorator.py     2018-04-11 16:55:35.000000000 
+0200
+++ new/backoff-1.8.0/backoff/_decorator.py     2018-12-21 06:08:23.000000000 
+0100
@@ -1,13 +1,22 @@
 # coding:utf-8
 from __future__ import unicode_literals
 
+import logging
 import operator
 import sys
 
+from backoff._common import (_config_handlers, _log_backoff, _log_giveup)
 from backoff._jitter import full_jitter
 from backoff import _sync
 
 
+# python 2.7 -> 3.x compatibility for str and unicode
+try:
+    basestring
+except NameError:  # pragma: python=3.5
+    basestring = str
+
+
 def on_predicate(wait_gen,
                  predicate=operator.not_,
                  max_tries=None,
@@ -16,6 +25,7 @@
                  on_success=None,
                  on_backoff=None,
                  on_giveup=None,
+                 logger='backoff',
                  **wait_gen_kwargs):
     """Returns decorator for backoff and retry triggered by predicate.
 
@@ -51,43 +61,45 @@
             signature to be called in the event that max_tries
             is exceeded.  The parameter is a dict containing details
             about the invocation.
+        logger: Name of logger or Logger object to log to. Defaults to
+            'backoff'.
         **wait_gen_kwargs: Any additional keyword args specified will be
             passed to wait_gen when it is initialized.  Any callable
             args will first be evaluated and their return values passed.
             This is useful for runtime configuration.
     """
     def decorate(target):
+        # change names because python 2.x doesn't have nonlocal
+        logger_ = logger
+        if isinstance(logger_, basestring):
+            logger_ = logging.getLogger(logger_)
+        on_success_ = _config_handlers(on_success)
+        on_backoff_ = _config_handlers(on_backoff, _log_backoff, logger_)
+        on_giveup_ = _config_handlers(on_giveup, _log_giveup, logger_)
+
         retry = None
-        if sys.version_info[:2] >= (3, 4):  # pragma: python=3.4
+        if sys.version_info >= (3, 5):  # pragma: python=3.5
             import asyncio
 
             if asyncio.iscoroutinefunction(target):
                 import backoff._async
                 retry = backoff._async.retry_predicate
 
-            else:
+            elif _is_event_loop() and _is_current_task():
                 # Verify that sync version is not being run from coroutine
                 # (that would lead to event loop hiccups).
-                try:
-                    asyncio.get_event_loop()
-                except RuntimeError:
-                    # Event loop not set for this thread.
-                    pass
-                else:
-                    if asyncio.Task.current_task() is not None:
-                        raise TypeError(
-                            "backoff.on_predicate applied to a regular "
-                            "function inside coroutine, this will lead "
-                            "to event loop hiccups. "
-                            "Use backoff.on_predicate on coroutines in "
-                            "asynchronous code.")
+                raise TypeError(
+                    "backoff.on_predicate applied to a regular function "
+                    "inside coroutine, this will lead to event loop "
+                    "hiccups. Use backoff.on_predicate on coroutines in "
+                    "asynchronous code.")
 
         if retry is None:
             retry = _sync.retry_predicate
 
         return retry(target, wait_gen, predicate,
                      max_tries, max_time, jitter,
-                     on_success, on_backoff, on_giveup,
+                     on_success_, on_backoff_, on_giveup_,
                      wait_gen_kwargs)
 
     # Return a function which decorates a target with a retry loop.
@@ -103,6 +115,7 @@
                  on_success=None,
                  on_backoff=None,
                  on_giveup=None,
+                 logger='backoff',
                  **wait_gen_kwargs):
     """Returns decorator for backoff and retry triggered by exception.
 
@@ -139,43 +152,66 @@
             signature to be called in the event that max_tries
             is exceeded.  The parameter is a dict containing details
             about the invocation.
+        logger: Name or Logger object to log to. Defaults to 'backoff'.
         **wait_gen_kwargs: Any additional keyword args specified will be
             passed to wait_gen when it is initialized.  Any callable
             args will first be evaluated and their return values passed.
             This is useful for runtime configuration.
     """
     def decorate(target):
+        # change names because python 2.x doesn't have nonlocal
+        logger_ = logger
+        if isinstance(logger_, basestring):
+            logger_ = logging.getLogger(logger_)
+        on_success_ = _config_handlers(on_success)
+        on_backoff_ = _config_handlers(on_backoff, _log_backoff, logger_)
+        on_giveup_ = _config_handlers(on_giveup, _log_giveup, logger_)
+
         retry = None
-        if sys.version_info[:2] >= (3, 4):   # pragma: python=3.4
+        if sys.version_info[:2] >= (3, 5):   # pragma: python=3.5
             import asyncio
 
             if asyncio.iscoroutinefunction(target):
                 import backoff._async
                 retry = backoff._async.retry_exception
-            else:
+            elif _is_event_loop() and _is_current_task():
                 # Verify that sync version is not being run from coroutine
                 # (that would lead to event loop hiccups).
-                try:
-                    asyncio.get_event_loop()
-                except RuntimeError:
-                    # Event loop not set for this thread.
-                    pass
-                else:
-                    if asyncio.Task.current_task() is not None:
-                        raise TypeError(
-                            "backoff.on_exception applied to a regular "
-                            "function inside coroutine, this will lead "
-                            "to event loop hiccups. "
-                            "Use backoff.on_exception on coroutines in "
-                            "asynchronous code.")
+                raise TypeError(
+                    "backoff.on_exception applied to a regular function "
+                    "inside coroutine, this will lead to event loop "
+                    "hiccups. Use backoff.on_exception on coroutines in "
+                    "asynchronous code.")
 
         if retry is None:
             retry = _sync.retry_exception
 
         return retry(target, wait_gen, exception,
                      max_tries, max_time, jitter, giveup,
-                     on_success, on_backoff, on_giveup,
+                     on_success_, on_backoff_, on_giveup_,
                      wait_gen_kwargs)
 
     # Return a function which decorates a target with a retry loop.
     return decorate
+
+
+def _is_event_loop():  # pragma: no cover
+    import asyncio
+
+    try:
+        if sys.version_info >= (3, 7):
+            asyncio.get_running_loop()
+
+        asyncio.get_event_loop()
+    except RuntimeError:
+        return False
+    else:
+        return True
+
+
+def _is_current_task():  # pragma: no cover
+    import asyncio
+    if sys.version_info >= (3, 7):
+        return asyncio.current_task() is not None
+
+    return asyncio.Task.current_task() is not None
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/backoff-1.5.0/backoff/_sync.py 
new/backoff-1.8.0/backoff/_sync.py
--- old/backoff-1.5.0/backoff/_sync.py  2018-04-11 16:55:35.000000000 +0200
+++ new/backoff-1.8.0/backoff/_sync.py  2018-12-15 18:22:25.000000000 +0100
@@ -2,10 +2,9 @@
 import datetime
 import functools
 import time
+from datetime import timedelta
 
-from backoff._common import (_handlers, _init_wait_gen, _log_backoff,
-                             _log_giveup, _maybe_call, _next_wait,
-                             _total_seconds)
+from backoff._common import (_init_wait_gen, _maybe_call, _next_wait)
 
 
 def _call_handlers(hdlrs, target, args, kwargs, tries, elapsed, **extra):
@@ -26,10 +25,6 @@
                     on_success, on_backoff, on_giveup,
                     wait_gen_kwargs):
 
-    success_hdlrs = _handlers(on_success)
-    backoff_hdlrs = _handlers(on_backoff, _log_backoff)
-    giveup_hdlrs = _handlers(on_giveup, _log_giveup)
-
     @functools.wraps(target)
     def retry(*args, **kwargs):
 
@@ -42,7 +37,7 @@
         wait = _init_wait_gen(wait_gen, wait_gen_kwargs)
         while True:
             tries += 1
-            elapsed = _total_seconds(datetime.datetime.now() - start)
+            elapsed = timedelta.total_seconds(datetime.datetime.now() - start)
             details = (target, args, kwargs, tries, elapsed)
 
             ret = target(*args, **kwargs)
@@ -52,18 +47,22 @@
                                      elapsed >= max_time_)
 
                 if max_tries_exceeded or max_time_exceeded:
-                    _call_handlers(giveup_hdlrs, *details, value=ret)
+                    _call_handlers(on_giveup, *details, value=ret)
                     break
 
-                seconds = _next_wait(wait, jitter, elapsed, max_time_)
+                try:
+                    seconds = _next_wait(wait, jitter, elapsed, max_time_)
+                except StopIteration:
+                    _call_handlers(on_giveup, *details)
+                    break
 
-                _call_handlers(backoff_hdlrs, *details,
+                _call_handlers(on_backoff, *details,
                                value=ret, wait=seconds)
 
                 time.sleep(seconds)
                 continue
             else:
-                _call_handlers(success_hdlrs, *details, value=ret)
+                _call_handlers(on_success, *details, value=ret)
                 break
 
         return ret
@@ -76,10 +75,6 @@
                     on_success, on_backoff, on_giveup,
                     wait_gen_kwargs):
 
-    success_hdlrs = _handlers(on_success)
-    backoff_hdlrs = _handlers(on_backoff, _log_backoff)
-    giveup_hdlrs = _handlers(on_giveup, _log_giveup)
-
     @functools.wraps(target)
     def retry(*args, **kwargs):
 
@@ -92,7 +87,7 @@
         wait = _init_wait_gen(wait_gen, wait_gen_kwargs)
         while True:
             tries += 1
-            elapsed = _total_seconds(datetime.datetime.now() - start)
+            elapsed = timedelta.total_seconds(datetime.datetime.now() - start)
             details = (target, args, kwargs, tries, elapsed)
 
             try:
@@ -103,16 +98,20 @@
                                      elapsed >= max_time_)
 
                 if giveup(e) or max_tries_exceeded or max_time_exceeded:
-                    _call_handlers(giveup_hdlrs, *details)
+                    _call_handlers(on_giveup, *details)
                     raise
 
-                seconds = _next_wait(wait, jitter, elapsed, max_time_)
+                try:
+                    seconds = _next_wait(wait, jitter, elapsed, max_time_)
+                except StopIteration:
+                    _call_handlers(on_giveup, *details)
+                    raise e
 
-                _call_handlers(backoff_hdlrs, *details, wait=seconds)
+                _call_handlers(on_backoff, *details, wait=seconds)
 
                 time.sleep(seconds)
             else:
-                _call_handlers(success_hdlrs, *details)
+                _call_handlers(on_success, *details)
 
                 return ret
     return retry
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/backoff-1.5.0/backoff/_wait_gen.py 
new/backoff-1.8.0/backoff/_wait_gen.py
--- old/backoff-1.5.0/backoff/_wait_gen.py      2018-03-30 13:12:58.000000000 
+0200
+++ new/backoff-1.8.0/backoff/_wait_gen.py      2018-11-29 02:00:40.000000000 
+0100
@@ -1,5 +1,7 @@
 # coding:utf-8
 
+import itertools
+
 
 def expo(base=2, factor=1, max_value=None):
     """Generator for exponential decay.
@@ -43,7 +45,12 @@
     """Generator for constant intervals.
 
     Args:
-        interval: The constant value in seconds to yield.
+        interval: A constant value to yield or an iterable of such values.
     """
-    while True:
-        yield interval
+    try:
+        itr = iter(interval)
+    except TypeError:
+        itr = itertools.repeat(interval)
+
+    for val in itr:
+        yield val
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/backoff-1.5.0/pyproject.toml 
new/backoff-1.8.0/pyproject.toml
--- old/backoff-1.5.0/pyproject.toml    1970-01-01 01:00:00.000000000 +0100
+++ new/backoff-1.8.0/pyproject.toml    2018-12-21 06:19:07.000000000 +0100
@@ -0,0 +1,44 @@
+[tool.poetry]
+name = "backoff"
+version = "1.8.0"
+description = "Function decoration for backoff and retry"
+authors = ["Bob Green <[email protected]>"]
+readme = "README.rst"
+repository = "https://github.com/litl/backoff";
+license = "MIT"
+keywords = ["retry", "backoff", "decorators"]
+classifiers = ['Development Status :: 5 - Production/Stable',
+               'Intended Audience :: Developers',
+               'Programming Language :: Python',
+               'License :: OSI Approved :: MIT License',
+               'Natural Language :: English',
+               'Operating System :: OS Independent',
+               'Programming Language :: Python',
+               'Programming Language :: Python :: 2',
+               'Programming Language :: Python :: 2.7',
+               'Programming Language :: Python :: 3',
+               'Programming Language :: Python :: 3.4',
+               'Programming Language :: Python :: 3.5',
+               'Programming Language :: Python :: 3.6',
+               'Programming Language :: Python :: 3.7',
+               'Topic :: Internet :: WWW/HTTP',
+               'Topic :: Software Development :: Libraries :: Python Modules',
+               'Topic :: Utilities']
+packages = [
+    { include = "backoff" },
+    { include = "README.rst" },
+    { include = "LICENSE" },
+]
+
+[tool.poetry.dependencies]
+python = "^2.7 || ^3.4"
+
+[tool.poetry.dev-dependencies]
+flake8 = "^3.6"
+pytest = "^4.0"
+pytest-cov = "^2.6"
+pytest-asyncio = {version = "^0.9.0",python = "^3.5"}
+
+[build-system]
+requires = ["poetry>=0.12"]
+build-backend = "poetry.masonry.api"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/backoff-1.5.0/setup.py new/backoff-1.8.0/setup.py
--- old/backoff-1.5.0/setup.py  2018-03-10 18:06:06.000000000 +0100
+++ new/backoff-1.8.0/setup.py  1970-01-01 01:00:00.000000000 +0100
@@ -1,45 +1,27 @@
-# coding:utf-8
+# -*- coding: utf-8 -*-
+from distutils.core import setup
 
-import backoff
+packages = \
+['backoff']
 
-from distutils import core
+package_data = \
+{'': ['*']}
 
+modules = \
+['README', 'LICENSE']
+setup_kwargs = {
+    'name': 'backoff',
+    'version': '1.8.0',
+    'description': 'Function decoration for backoff and retry',
+    'long_description': 'backoff\n=======\n\n.. image:: 
https://travis-ci.org/litl/backoff.svg?branch=master\n    :target: 
https://travis-ci.org/litl/backoff?branch=master\n.. image:: 
https://coveralls.io/repos/litl/backoff/badge.svg?branch=master\n    :target: 
https://coveralls.io/r/litl/backoff?branch=master\n.. image:: 
https://img.shields.io/pypi/v/backoff.svg\n    :target: 
https://pypi.python.org/pypi/backoff\n\n**Function decoration for backoff and 
retry**\n\nThis module provides function decorators which can be used to wrap 
a\nfunction such that it will be retried until some condition is met. It\nis 
meant to be of use when accessing unreliable resources with the\npotential for 
intermittent failures i.e. network resources and external\nAPIs. Somewhat more 
generally, it may also be of use for dynamically\npolling resources for 
externally generated content.\n\nDecorators support both regular functions for 
synchronous code and\n`asyncio 
<https://docs.python.org/3/library/asyncio.html>`_\'s coroutines\nfor 
asynchronous code.\n\nExamples\n========\n\nSince Kenneth Reitz\'s `requests 
<http://python-requests.org>`_ module\nhas become a defacto standard for 
synchronous HTTP clients in Python,\nnetworking examples below are written 
using it, but it is in no way required\nby the backoff 
module.\n\[email protected]_exception\n---------------------\n\nThe ``on_exception`` 
decorator is used to retry when a specified exception\nis raised. Here\'s an 
example using exponential backoff when any\n``requests`` exception is 
raised:\n\n.. code-block:: python\n\n    @backoff.on_exception(backoff.expo,\n  
                        requests.exceptions.RequestException)\n    def 
get_url(url):\n        return requests.get(url)\n\nThe decorator will also 
accept a tuple of exceptions for cases where\nthe same backoff behavior is 
desired for more than one exception type:\n\n.. code-block:: python\n\n    
@backoff.on_exception(backoff.expo,\n                          
(requests.exceptions.Timeout,\n                           
requests.exceptions.ConnectionError))\n    def get_url(url):\n        return 
requests.get(url)\n\n**Give Up Conditions**\n\nOptional keyword arguments can 
specify conditions under which to give\nup.\n\nThe keyword argument 
``max_time`` specifies the maximum amount\nof total time in seconds that can 
elapse before giving up.\n\n.. code-block:: python\n\n    
@backoff.on_exception(backoff.expo,\n                          
requests.exceptions.RequestException,\n                          max_time=60)\n 
   def get_url(url):\n        return requests.get(url)\n\n\nKeyword argument 
``max_tries`` specifies the maximum number of calls\nto make to the target 
function before giving up.\n\n.. code-block:: python\n\n    
@backoff.on_exception(backoff.expo,\n                          
requests.exceptions.RequestException,\n                          max_tries=8,\n 
                         jitter=None)\n    def get_url(url):\n        return 
requests.get(url)\n\n\nIn some cases the raised exception instance itself may 
need to be\ninspected in order to determine if it is a retryable condition. 
The\n``giveup`` keyword arg can be used to specify a function which 
accepts\nthe exception and returns a truthy value if the exception should 
not\nbe retried:\n\n.. code-block:: python\n\n    def fatal_code(e):\n        
return 400 <= e.response.status_code < 500\n\n    
@backoff.on_exception(backoff.expo,\n                          
requests.exceptions.RequestException,\n                          
max_time=300,\n                          giveup=fatal_code)\n    def 
get_url(url):\n        return requests.get(url)\n\nWhen a give up event occurs, 
the exception in question is reraised\nand so code calling an 
`on_exception`-decorated function may still\nneed to do exception 
handling.\n\[email protected]_predicate\n---------------------\n\nThe 
``on_predicate`` decorator is used to retry when a particular\ncondition is 
true of the return value of the target function.  This may\nbe useful when 
polling a resource for externally generated content.\n\nHere\'s an example 
which uses a fibonacci sequence backoff when the\nreturn value of the target 
function is the empty list:\n\n.. code-block:: python\n\n    
@backoff.on_predicate(backoff.fibo, lambda x: x == [], max_value=13)\n    def 
poll_for_messages(queue):\n        return queue.get()\n\nExtra keyword 
arguments are passed when initializing the\nwait generator, so the 
``max_value`` param above is passed as a keyword\narg when initializing the 
fibo generator.\n\nWhen not specified, the predicate param defaults to the 
falsey test,\nso the above can more concisely be written:\n\n.. code-block:: 
python\n\n    @backoff.on_predicate(backoff.fibo, max_value=13)\n    def 
poll_for_message(queue)\n        return queue.get()\n\nMore simply, a function 
which continues polling every second until it\ngets a non-falsey result could 
be defined like like this:\n\n.. code-block:: python\n\n    
@backoff.on_predicate(backoff.constant, interval=1)\n    def 
poll_for_message(queue)\n        return queue.get()\n\nJitter\n------\n\nA 
jitter algorithm can be supplied with the ``jitter`` keyword arg to\neither of 
the backoff decorators. This argument should be a function\naccepting the 
original unadulterated backoff value and returning it\'s\njittered 
counterpart.\n\nAs of version 1.2, the default jitter function 
``backoff.full_jitter``\nimplements the \'Full Jitter\' algorithm as defined in 
the AWS\nArchitecture Blog\'s `Exponential Backoff And 
Jitter\n<https://www.awsarchitectureblog.com/2015/03/backoff.html>`_ 
post.\nNote that with this algorithm, the time yielded by the wait 
generator\nis actually the *maximum* amount of time to wait.\n\nPrevious 
versions of backoff defaulted to adding some random number of\nmilliseconds (up 
to 1s) to the raw sleep value. If desired, this\nbehavior is now available as 
``backoff.random_jitter``.\n\nUsing multiple 
decorators\n-------------------------\n\nThe backoff decorators may also be 
combined to specify different\nbackoff behavior for different cases:\n\n.. 
code-block:: python\n\n    @backoff.on_predicate(backoff.fibo, max_value=13)\n  
  @backoff.on_exception(backoff.expo,\n                          
requests.exceptions.HTTPError,\n                          max_time=60)\n    
@backoff.on_exception(backoff.expo,\n                          
requests.exceptions.TimeoutError,\n                          max_time=300)\n    
def poll_for_message(queue):\n        return queue.get()\n\nRuntime 
Configuration\n---------------------\n\nThe decorator functions 
``on_exception`` and ``on_predicate`` are\ngenerally evaluated at import time. 
This is fine when the keyword args\nare passed as constant values, but suppose 
we want to consult a\ndictionary with configuration options that only become 
available at\nruntime. The relevant values are not available at import time. 
Instead,\ndecorator functions can be passed callables which are evaluated 
at\nruntime to obtain the value:\n\n.. code-block:: python\n\n    def 
lookup_max_time():\n        # pretend we have a global reference to \'app\' 
here\n        # and that it has a dictionary-like \'config\' property\n        
return app.config["BACKOFF_MAX_TIME"]\n\n    
@backoff.on_exception(backoff.expo,\n                          ValueError,\n    
                      max_time=lookup_max_time)\n\nEvent 
handlers\n--------------\n\nBoth backoff decorators optionally accept event 
handler functions\nusing the keyword arguments ``on_success``, ``on_backoff``, 
and ``on_giveup``.\nThis may be useful in reporting statistics or performing 
other custom\nlogging.\n\nHandlers must be callables with a unary signature 
accepting a dict\nargument. This dict contains the details of the invocation. 
Valid keys\ninclude:\n\n* *target*: reference to the function or method being 
invoked\n* *args*: positional arguments to func\n* *kwargs*: keyword arguments 
to func\n* *tries*: number of invocation tries so far\n* *elapsed*: elapsed 
time in seconds so far\n* *wait*: seconds to wait (``on_backoff`` handler 
only)\n* *value*: value triggering backoff (``on_predicate`` decorator 
only)\n\nA handler which prints the details of the backoff event could 
be\nimplemented like so:\n\n.. code-block:: python\n\n    def 
backoff_hdlr(details):\n        print ("Backing off {wait:0.1f} seconds afters 
{tries} tries "\n               "calling function {target} with args {args} and 
kwargs "\n               "{kwargs}".format(**details))\n\n    
@backoff.on_exception(backoff.expo,\n                          
requests.exceptions.RequestException,\n                          
on_backoff=backoff_hdlr)\n    def get_url(url):\n        return 
requests.get(url)\n\n**Multiple handlers per event type**\n\nIn all cases, 
iterables of handler functions are also accepted, which\nare called in turn. 
For example, you might provide a simple list of\nhandler functions as the value 
of the ``on_backoff`` keyword arg:\n\n.. code-block:: python\n\n    
@backoff.on_exception(backoff.expo,\n                          
requests.exceptions.RequestException,\n                          
on_backoff=[backoff_hdlr1, backoff_hdlr2])\n    def get_url(url):\n        
return requests.get(url)\n\n**Getting exception info**\n\nIn the case of the 
``on_exception`` decorator, all ``on_backoff`` and\n``on_giveup`` handlers are 
called from within the except block for the\nexception being handled. Therefore 
exception info is available to the\nhandler functions via the python standard 
library, specifically\n``sys.exc_info()`` or the ``traceback`` 
module.\n\nAsynchronous code\n-----------------\n\nBackoff supports 
asynchronous execution in Python 3.5 and above.\n\nTo use backoff in 
asynchronous code based on\n`asyncio 
<https://docs.python.org/3/library/asyncio.html>`_\nyou simply need to apply 
``backoff.on_exception`` or ``backoff.on_predicate``\nto coroutines.\nYou can 
also use coroutines for the ``on_success``, ``on_backoff``, and\n``on_giveup`` 
event handlers, with the interface otherwise being identical.\n\nThe following 
examples use `aiohttp <https://aiohttp.readthedocs.io/>`_\nasynchronous HTTP 
client/server library.\n\n.. code-block:: python\n\n    
@backoff.on_exception(backoff.expo, aiohttp.ClientError, max_time=60)\n    
async def get_url(url):\n        async with aiohttp.ClientSession() as 
session:\n            async with session.get(url) as response:\n                
return await response.text()\n\nLogging 
configuration\n---------------------\n\nBy default, backoff and retry attempts 
are logged to the \'backoff\'\nlogger. By default, this logger is configured 
with a NullHandler, so\nthere will be nothing output unless you configure a 
handler.\nProgrammatically, this might be accomplished with something as 
simple\nas:\n\n.. code-block:: python\n\n    
logging.getLogger(\'backoff\').addHandler(logging.StreamHandler())\n\nThe 
default logging level is INFO, which corresponds to logging\nanytime a retry 
event occurs. If you would instead like to log\nonly when a giveup event 
occurs, set the logger level to ERROR.\n\n.. code-block:: python\n\n    
logging.getLogger(\'backoff\').setLevel(logging.ERROR)\n\nIt is also possible 
to specify an alternate logger with the ``logger``\nkeyword argument.  If a 
string value is specified the logger will be\nlooked up by name.\n\n.. 
code-block:: python\n\n   @backoff.on_exception(backoff.expo,\n                 
        requests.exception.RequestException,\n\t\t\t logger=\'my_logger\')\n   
# ...\n\nIt is also supported to specify a Logger (or LoggerAdapter) 
object\ndirectly.\n\n.. code-block:: python\n\n    my_logger = 
logging.getLogger(\'my_logger\')\n    my_handler = logging.StreamHandler()\n    
my_logger.add_handler(my_handler)\n    my_logger.setLevel(logging.ERROR)\n\n    
@backoff.on_exception(backoff.expo,\n                         
requests.exception.RequestException,\n\t\t\t logger=my_logger)\n    # 
...\n\nDefault logging can be disabled all together by 
specifying\n``logger=None``. In this case, if desired alternative logging 
behavior\ncould be defined by using custom event handlers.\n',
+    'author': 'Bob Green',
+    'author_email': '[email protected]',
+    'url': 'https://github.com/litl/backoff',
+    'packages': packages,
+    'package_data': package_data,
+    'py_modules': modules,
+    'python_requires': '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*',
+}
 
-classifiers = ['Development Status :: 5 - Production/Stable',
-               'Intended Audience :: Developers',
-               'Programming Language :: Python',
-               'License :: OSI Approved :: MIT License',
-               'Natural Language :: English',
-               'Operating System :: OS Independent',
-               'Programming Language :: Python',
-               'Programming Language :: Python :: 2.6',
-               'Programming Language :: Python :: 2.7',
-               'Programming Language :: Python :: 3',
-               'Programming Language :: Python :: Implementation',
-               'Programming Language :: Python :: Implementation :: CPython',
-               'Topic :: Internet :: WWW/HTTP',
-               'Topic :: Software Development :: Libraries :: Python Modules',
-               'Topic :: Utilities']
 
-version = backoff.__version__
-url = "https://github.com/litl/backoff";
-tarball_url = "%s/tarball/v%s" % (url, version)
-
-
-def readme():
-    with open("README.rst", "r") as infile:
-        return infile.read()
-
-
-core.setup(name='backoff',
-           version=version,
-           description="Function decoration for backoff and retry",
-           long_description=readme(),
-           packages=['backoff'],
-           author="Bob Green",
-           author_email="[email protected]",
-           keywords = "backoff function decorator",
-           url=url,
-           download_url=tarball_url,
-           license="MIT",
-           classifiers=classifiers)
+setup(**setup_kwargs)


Reply via email to