[Python-modules-commits] [python-tenacity] 01/13: Import python-tenacity_3.1.1.orig.tar.xz

Ondrej Koblizek kobla-guest at moszumanska.debian.org
Sat Aug 5 19:27:56 UTC 2017


This is an automated email from the git hooks/post-receive script.

kobla-guest pushed a commit to branch master
in repository python-tenacity.

commit 86ea9f408974b54a1437558b4c088db882970469
Author: Ondřej Kobližek <ondrej.koblizek at firma.seznam.cz>
Date:   Fri Aug 4 18:45:25 2017 -0400

    Import python-tenacity_3.1.1.orig.tar.xz
---
 .gitignore                      |   9 +
 .travis.yml                     |  13 ++
 LICENSE                         | 202 ++++++++++++++++
 MANIFEST.in                     |   1 +
 NOTICE                          |  13 ++
 README.rst                      |   1 +
 doc/source/conf.py              |   2 +
 doc/source/index.rst            | 209 +++++++++++++++++
 requirements.txt                |   3 +
 setup.cfg                       |  27 +++
 setup.py                        |  20 ++
 tenacity/__init__.py            | 226 ++++++++++++++++++
 tenacity/_utils.py              |  99 ++++++++
 tenacity/after.py               |  34 +++
 tenacity/before.py              |  33 +++
 tenacity/retry.py               |  66 ++++++
 tenacity/stop.py                |  40 ++++
 tenacity/tests/__init__.py      |   0
 tenacity/tests/test_tenacity.py | 498 ++++++++++++++++++++++++++++++++++++++++
 tenacity/wait.py                | 134 +++++++++++
 tox.ini                         |  16 ++
 21 files changed, 1646 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..151b766
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,9 @@
+.idea
+dist
+*.pyc
+*.egg-info
+build
+.tox/
+AUTHORS
+ChangeLog
+.eggs/
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..6af8e93
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,13 @@
+language: python
+
+python:
+  - 2.7
+  - 3.4
+  - 3.5
+  - pypy
+
+install: pip install tox nose
+
+script:
+  - tox -e pep8
+  - python setup.py nosetests
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..7a4a3ea
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   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.
\ No newline at end of file
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..5602966
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1 @@
+include README.rst HISTORY.rst AUTHORS.rst LICENSE NOTICE requirements.txt
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..18434bc
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,13 @@
+Copyright 2013 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.
diff --git a/README.rst b/README.rst
new file mode 120000
index 0000000..c560c78
--- /dev/null
+++ b/README.rst
@@ -0,0 +1 @@
+doc/source/index.rst
\ No newline at end of file
diff --git a/doc/source/conf.py b/doc/source/conf.py
new file mode 100644
index 0000000..933ffab
--- /dev/null
+++ b/doc/source/conf.py
@@ -0,0 +1,2 @@
+master_doc = 'index'
+project = "Tenacity"
diff --git a/doc/source/index.rst b/doc/source/index.rst
new file mode 100644
index 0000000..0da12ba
--- /dev/null
+++ b/doc/source/index.rst
@@ -0,0 +1,209 @@
+Tenacity
+========
+.. image:: https://img.shields.io/pypi/v/tenacity.svg
+    :target: https://pypi.python.org/pypi/tenacity
+
+.. image:: https://img.shields.io/travis/jd/tenacity.svg
+    :target: https://travis-ci.org/jd/tenacity
+
+.. image:: https://img.shields.io/pypi/dm/tenacity.svg
+    :target: https://pypi.python.org/pypi/tenacity
+
+Tenacity is an Apache 2.0 licensed general-purpose retrying library, written in
+Python, to simplify the task of adding retry behavior to just about anything.
+It originates from a fork of `Retrying`_
+
+.. _Retrying: https://github.com/rholder/retrying
+
+The simplest use case is retrying a flaky function whenever an `Exception`
+occurs until a value is returned.
+
+.. code-block:: python
+
+    import random
+    from tenacity import retry
+
+    @retry
+    def do_something_unreliable():
+        if random.randint(0, 10) > 1:
+            raise IOError("Broken sauce, everything is hosed!!!111one")
+        else:
+            return "Awesome sauce!"
+
+    print do_something_unreliable()
+
+
+Features
+--------
+
+- Generic Decorator API
+- Specify stop condition (i.e. limit by number of attempts)
+- Specify wait condition (i.e. exponential backoff sleeping between attempts)
+- Customize retrying on Exceptions
+- Customize retrying on expected returned result
+
+
+Installation
+------------
+
+To install *tenacity*, simply:
+
+.. code-block:: bash
+
+    $ pip install tenacity
+
+
+Examples
+----------
+
+As you saw above, the default behavior is to retry forever without waiting.
+
+.. code-block:: python
+
+    @retry
+    def never_give_up_never_surrender():
+        print "Retry forever ignoring Exceptions, don't wait between retries"
+
+Let's be a little less persistent and set some boundaries, such as the number
+of attempts before giving up.
+
+.. code-block:: python
+
+    @retry(stop=stop_after_attempt(7))
+    def stop_after_7_attempts():
+        print "Stopping after 7 attempts"
+
+We don't have all day, so let's set a boundary for how long we should be
+retrying stuff.
+
+.. code-block:: python
+
+    @retry(stop=stop_after_delay(10))
+    def stop_after_10_s():
+        print "Stopping after 10 seconds"
+
+Most things don't like to be polled as fast as possible, so let's just wait 2
+seconds between retries.
+
+.. code-block:: python
+
+    @retry(wait=wait_fixed(2))
+    def wait_2_s():
+        print "Wait 2 second between retries"
+
+
+Some things perform best with a bit of randomness injected.
+
+.. code-block:: python
+
+    @retry(wait=wait_random(min=1, max=2))
+    def wait_random_1_to_2_s():
+        print "Randomly wait 1 to 2 seconds between retries"
+
+Then again, it's hard to beat exponential backoff when retrying distributed
+services and other remote endpoints.
+
+.. code-block:: python
+
+    @retry(wait=wait_exponential(multiplier=1, max=10))
+    def wait_exponential_1():
+        print "Wait 2^x * 1 second between each retry, up to 10 seconds, then 10 seconds afterwards"
+
+
+Then again, it's hard to beat exponential backoff when retrying distributed
+services and other remote endpoints.
+
+.. code-block:: python
+
+    @retry(wait=wait_combine(wait_fixed(3), wait_jitter(2)))
+    def wait_fixed_jitter():
+        print "Wait at least 3 seconds, and add up to 2 seconds of random delay"
+
+
+Sometimes it's necessary to build a chain of backoffs.
+
+.. code-block:: python
+
+    @retry(wait=wait_chain(*[wait_fixed(3) for i in range(3)] +
+                           [wait_fixed(7) for i in range(2)] +
+                           [wait_fixed(9)]))
+    def wait_fixed_chained():
+        print "Wait 3s for 3 attempts, 7s for the next 2 attempts and 9s for all attempts thereafter"
+
+
+We have a few options for dealing with retries that raise specific or general
+exceptions, as in the cases here.
+
+.. code-block:: python
+
+    @retry(retry=retry_if_exception_type(IOError))
+    def might_io_error():
+        print "Retry forever with no wait if an IOError occurs, raise any other errors"
+
+    @retry(retry_on_exception=retry_if_io_error)
+    def only_raise_retry_error_when_not_io_error():
+        print "Retry forever with no wait if an IOError occurs, raise any other errors wrapped in RetryError"
+
+We can also use the result of the function to alter the behavior of retrying.
+
+.. code-block:: python
+
+    def is_none_p(value):
+        """Return True if value is None"""
+        return value is None
+
+    @retry(retry=retry_if_result(is_none_p))
+    def might_return_none():
+        print "Retry with no wait if return value is None"
+
+We can also combine several conditions:
+
+.. code-block:: python
+
+    def is_none_p(value):
+        """Return True if value is None"""
+        return value is None
+
+    @retry(retry=retry_any(retry_if_result(is_none_p), retry_if_exception_type()))
+    def might_return_none():
+        print "Retry forever ignoring Exceptions with no wait if return value is None"
+
+Any combination of stop, wait, etc. is also supported to give you the freedom
+to mix and match.
+
+It's also possible to retry explicitely at any time by raising the `TryAgain`
+exception:
+
+.. code-block:: python
+
+   @retry
+   def do_something():
+       result = something_else()
+       if result == 23:
+          raise TryAgain
+
+While callables that "timeout" retrying raise a `RetryError` by default,
+we can reraise the last attempt's exception if needed:
+
+.. code-block:: python
+
+    @retry(reraise=True, stop=stop_after_attempts(3))
+    def raise_my_exception():
+        raise MyException("Fail")
+
+    try:
+        raise_my_exception()
+    except MyException:
+        # timed out retrying
+
+Contribute
+----------
+
+#. Check for open issues or open a fresh issue to start a discussion around a
+   feature idea or a bug.
+#. Fork `the repository`_ on GitHub to start making your changes to the
+   **master** branch (or branch off of it).
+#. Write a test which shows that the bug was fixed or that the feature works as
+   expected.
+
+.. _`the repository`: https://github.com/jd/tenacity
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..b257d7b
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,3 @@
+six>=1.7.0
+futures>=3.0;python_version=='2.7'
+monotonic>=0.6 # Apache-2.0
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..3d71d1c
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,27 @@
+[metadata]
+name = tenacity
+url = https://github.com/jd/tenacity
+summary = Retry code until it succeeeds
+description-file =
+    README.rst
+author = Julien Danjou
+author-email = julien at danjou.info
+home-page = https://github.com/jd/tenacity
+classifier =
+    Intended Audience :: Developers
+    License :: OSI Approved :: Apache Software License
+    Programming Language :: Python
+    Programming Language :: Python :: 2.7
+    Programming Language :: Python :: 3.4
+    Programming Language :: Python :: 3.5
+    Topic :: Utilities
+
+[global]
+setup-hooks =
+    pbr.hooks.setup_hook
+
+[files]
+packages = tenacity
+
+[wheel]
+universal = 1
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..186fa00
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,20 @@
+#!/usr/bin/env python
+#
+# 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 setuptools
+
+setuptools.setup(
+    setup_requires=['pbr'],
+    pbr=True)
diff --git a/tenacity/__init__.py b/tenacity/__init__.py
new file mode 100644
index 0000000..a03102f
--- /dev/null
+++ b/tenacity/__init__.py
@@ -0,0 +1,226 @@
+# 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 concurrent import futures
+import sys
+import time
+
+from monotonic import monotonic as now
+import six
+
+# Import all built-in retry strategies for easier usage.
+from .retry import retry_always  # noqa
+from .retry import retry_any  # noqa
+from .retry import retry_if_exception  # noqa
+from .retry import retry_if_exception_type  # noqa
+from .retry import retry_if_result  # noqa
+from .retry import retry_never  # noqa
+
+# Import all built-in stop strategies for easier usage.
+from .stop import stop_after_attempt  # noqa
+from .stop import stop_after_delay  # noqa
+from .stop import stop_never  # noqa
+
+# Import all built-in wait strategies for easier usage.
+from .wait import wait_chain  # noqa
+from .wait import wait_combine  # noqa
+from .wait import wait_exponential  # noqa
+from .wait import wait_fixed  # noqa
+from .wait import wait_incrementing  # noqa
+from .wait import wait_jitter  # noqa
+from .wait import wait_none  # noqa
+from .wait import wait_random  # noqa
+
+# Import all built-in before strategies for easier usage.
+from .before import before_log  # noqa
+from .before import before_nothing  # noqa
+
+# Import all built-in after strategies for easier usage.
+from .after import after_log  # noqa
+from .after import after_nothing  # noqa
+
+from tenacity import _utils
+
+
+def retry(*dargs, **dkw):
+    """Decorator function that wraps + instantiates a ``Retrying`` object.
+
+    @param *dargs: positional arguments passed to Retrying object
+    @param **dkw: keyword arguments passed to the Retrying object
+    """
+    # support both @retry and @retry() as valid syntax
+    if len(dargs) == 1 and callable(dargs[0]):
+        def wrap_simple(f):
+
+            @six.wraps(f)
+            def wrapped_f(*args, **kw):
+                return Retrying().call(f, *args, **kw)
+
+            return wrapped_f
+
+        return wrap_simple(dargs[0])
+
+    else:
+        def wrap(f):
+
+            @six.wraps(f)
+            def wrapped_f(*args, **kw):
+                return Retrying(*dargs, **dkw).call(f, *args, **kw)
+
+            return wrapped_f
+
+        return wrap
+
+
+class TryAgain(Exception):
+    """Always retry the executed function when raised."""
+
+
+class Retrying(object):
+    """Retrying controller."""
+
+    def __init__(self,
+                 stop=stop_never, wait=wait_none(),
+                 sleep=time.sleep,
+                 retry=retry_if_exception_type(),
+                 before=before_nothing,
+                 after=after_nothing,
+                 reraise=False):
+        self.sleep = sleep
+        self.stop = stop
+        self.wait = wait
+        self.retry = retry
+        self.before = before
+        self.after = after
+        self._statistics = {}
+        self.reraise = reraise
+
+    def __repr__(self):
+        attrs = _utils.visible_attrs(self, attrs={'me': id(self)})
+        return ("<Retrying object at 0x%(me)x (stop=%(stop)s, "
+                "wait=%(wait)s, sleep=%(sleep)s, retry=%(retry)s, "
+                "before=%(before)s, after=%(after)s)>") % (attrs)
+
+    @property
+    def statistics(self):
+        """A dictionary of runtime statistics this controller has gathered.
+
+        This dictionary will be empty when the controller has never been
+        ran. When it is running or has ran previously it should have (but
+        may not) have useful and/or informational keys and values when
+        running is underway and/or completed.
+
+        .. warning:: The keys in this dictionary **should** be some what
+                     stable (not changing), but there existence **may**
+                     change between major releases as new statistics are
+                     gathered or removed so before accessing keys ensure that
+                     they actually exist and handle when they do not.
+        """
+        return self._statistics
+
+    def call(self, fn, *args, **kwargs):
+        self._statistics.clear()
+        start_time = now()
+        self._statistics['start_time'] = start_time
+        attempt_number = 1
+        self._statistics['attempt_number'] = attempt_number
+        self._statistics['idle_for'] = 0
+        while True:
+            trial_start_time = now()
+            if self.before is not None:
+                self.before(fn, attempt_number)
+
+            fut = Future(attempt_number)
+            try:
+                result = fn(*args, **kwargs)
+            except TryAgain:
+                trial_end_time = now()
+                retry = True
+            except Exception:
+                trial_end_time = now()
+                tb = sys.exc_info()
+                try:
+                    _utils.capture(fut, tb)
+                finally:
+                    del tb
+                retry = self.retry(fut)
+            else:
+                trial_end_time = now()
+                fut.set_result(result)
+                retry = self.retry(fut)
+
+            if not retry:
+                return fut.result()
+
+            if self.after is not None:
+                trial_time_taken = trial_end_time - trial_start_time
+                self.after(fn, attempt_number, trial_time_taken)
+
+            delay_since_first_attempt = now() - start_time
+            self._statistics['delay_since_first_attempt'] = \
+                delay_since_first_attempt
+            if self.stop(attempt_number, delay_since_first_attempt):
+                if self.reraise:
+                    raise RetryError(fut).reraise()
+                six.raise_from(RetryError(fut), fut.exception())
+
+            if self.wait:
+                sleep = self.wait(attempt_number, delay_since_first_attempt)
+            else:
+                sleep = 0
+            self._statistics['idle_for'] += sleep
+            self.sleep(sleep)
+
+            attempt_number += 1
+            self._statistics['attempt_number'] = attempt_number
+
+
+class Future(futures.Future):
+    """Encapsulates a (future or past) attempted call to a target function."""
+
+    def __init__(self, attempt_number):
+        super(Future, self).__init__()
+        self.attempt_number = attempt_number
+
+    @property
+    def failed(self):
+        """Return whether a exception is being held in this future."""
+        return self.exception() is not None
+
+    @classmethod
+    def construct(cls, attempt_number, value, has_exception):
+        """Helper (for testing) for making these objects easily."""
+        fut = cls(attempt_number)
+        if has_exception:
+            fut.set_exception(value)
+        else:
+            fut.set_result(value)
+        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)
diff --git a/tenacity/_utils.py b/tenacity/_utils.py
new file mode 100644
index 0000000..ef2476f
--- /dev/null
+++ b/tenacity/_utils.py
@@ -0,0 +1,99 @@
+# 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 inspect
+import sys
+
+import six
+
+# sys.maxint / 2, since Python 3.2 doesn't have a sys.maxint...
+try:
+    MAX_WAIT = sys.maxint / 2
+except AttributeError:
+    MAX_WAIT = 1073741823
+
+
+if six.PY2:
+    def capture(fut, tb):
+        # TODO(harlowja): delete this in future, since its
+        # has to repeatedly calculate this crap.
+        fut.set_exception_info(tb[1], tb[2])
+else:
+    def capture(fut, tb):
+        fut.set_exception(tb[1])
+
+
+def visible_attrs(obj, attrs=None):
+    if attrs is None:
+        attrs = {}
+    for attr_name, attr in inspect.getmembers(obj):
+        if attr_name.startswith("_"):
+            continue
+        attrs[attr_name] = attr
+    return attrs
+
+
+def find_ordinal(pos_num):
+    # See: https://en.wikipedia.org/wiki/English_numerals#Ordinal_numbers
+    if pos_num == 0:
+        return "th"
+    elif pos_num == 1:
+        return 'st'
+    elif pos_num == 2:
+        return 'nd'
+    elif pos_num == 3:
+        return 'rd'
+    elif pos_num >= 4 and pos_num <= 20:
+        return 'th'
+    else:
+        return find_ordinal(pos_num % 10)
+
+
+def to_ordinal(pos_num):
+    return "%i%s" % (pos_num, find_ordinal(pos_num))
+
+
+def get_callback_name(cb):
+    """Tries to get a callbacks fully-qualified name.
+
+    If no name can be produced ``repr(cb)`` is called and returned.
+    """
+    segments = []
+    try:
+        segments.append(cb.__qualname__)
+    except AttributeError:
+        try:
+            segments.append(cb.__name__)
+            if inspect.ismethod(cb):
+                try:
+                    # This attribute doesn't exist on py3.x or newer, so
+                    # we optionally ignore it... (on those versions of
+                    # python `__qualname__` should have been found anyway).
+                    segments.insert(0, cb.im_class.__name__)
+                except AttributeError:
+                    pass
+        except AttributeError:
+            pass
+    if not segments:
+        return repr(cb)
+    else:
+        try:
+            # When running under sphinx it appears this can be none?
+            if cb.__module__:
+                segments.insert(0, cb.__module__)
+        except AttributeError:
+            pass
+        return ".".join(segments)
diff --git a/tenacity/after.py b/tenacity/after.py
new file mode 100644
index 0000000..a310ea0
--- /dev/null
+++ b/tenacity/after.py
@@ -0,0 +1,34 @@
+# 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 after_nothing(func, trial_number, trial_time_taken):
+    """After call strategy that does nothing."""
+
+
+def after_log(logger, log_level):
+    """After call strategy that logs to some logger the finished attempt."""
+
+    def log_it(func, trial_number, trial_time_taken):
+        logger.log(log_level,
+                   "Finished call to '%s' after %i(ms), "
+                   "this was the %s time calling it.",
+                   _utils.get_callback_name(func), trial_time_taken,
+                   _utils.to_ordinal(trial_number))
+
+    return log_it
diff --git a/tenacity/before.py b/tenacity/before.py
new file mode 100644
index 0000000..c1856d5
--- /dev/null
+++ b/tenacity/before.py
@@ -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_nothing(func, trial_number):
... 800 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-tenacity.git



More information about the Python-modules-commits mailing list