[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