{ "info": { "author": "Jared Gillespie", "author_email": "jaredlgillespie@hotmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: Implementation :: CPython", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Utilities" ], "description": "Rerun Me\n========\n\n.. image:: https://img.shields.io/travis/JaredLGillespie/rerun.me.svg\n :alt: Travis\n :target: https://travis-ci.org/JaredLGillespie/rerun.me\n.. image:: https://img.shields.io/coveralls/github/JaredLGillespie/rerun.me.svg\n :alt: Coveralls github\n :target: https://coveralls.io/github/JaredLGillespie/rerun.me\n.. image:: https://img.shields.io/pypi/v/rerun.me.svg\n :alt: PyPI\n :target: https://pypi.org/project/rerun.me/\n.. image:: https://img.shields.io/pypi/wheel/rerun.me.svg\n :alt: PyPI - Wheel\n :target: https://pypi.org/project/rerun.me/\n.. image:: https://img.shields.io/pypi/pyversions/rerun.me.svg\n :alt: PyPI - Python Version\n :target: https://pypi.org/project/rerun.me/\n.. image:: https://img.shields.io/pypi/l/rerun.me.svg\n :alt: PyPI - License\n :target: https://pypi.org/project/rerun.me/\n\nA library for rerunning functions in the case of raised exceptions and specific return values with configurable delays.\n\n.. code-block:: python\n\n @rerun(on_delay=fibonacci(1000, 3),\n on_error=[ConnectionTimeoutError, DeadlockVictimError],\n on_return=[None]\n on_retry=lambda d, r: log.info('Retrying connection again (#%s) in %s seconds' % (r, d)))\n def connection(conn_str, params):\n conn = db(conn_str, params)\n return db.open()\n\nInstallation\n------------\n\nThe latest version of rerun.me is available via ``pip``:\n\n.. code-block:: python\n\n pip install rerun.me\n\nAlternatively, you can download and install from source:\n\n.. code-block:: python\n\n python setup.py install\n\nGetting Started\n---------------\n\nThe ``rerun`` function contains the following signature:\n\n.. code-block:: python\n\n def rerun(on_delay=None, on_error=None, on_return=None, on_retry=None, retry_after_delay=False):\n ...\n\nIt serves as both a function decorator, and a runnable wrapper and is configurable through it's dynamic parameters. Most\nof which are function callbacks which allow the user to highly configure the retrying behavior.\n\nThis configurable nature is what sets this library apart from others with similar functionality. Many of which allow\nbasic configuration using defined retry limits and constant delays between requests, which may be OK for the most\nsimplistic of use cases. But most applications need more complex functionality which can delay with various common\nalgorithms such as exponential or fibonacci delays. This library provides a subset of the most common delay generators,\nbut is easily expandable to fit the application-specific needs.\n\nDelay Generators\n^^^^^^^^^^^^^^^^\n\nDifferent ``on_delay`` generators can be used for increasing the delays between successive retries. Note that the values\nfor the delays are given in milliseconds.\n\n.. code-block:: python\n\n @rerun(on_delay=[1000, 2000], on_error=KeyError)\n def func():\n ...\n\nGenerators and iterable items can be used to generate delays too.\n\n.. code-block:: python\n\n def fancy_generator():\n # yield delays\n ...\n\n @rerun(on_delay=fancy_generator)\n def func():\n ...\n\nIf a single delay is desired, an ``integer`` or ``float`` value can be given, like so.\n\n.. code-block:: python\n\n @rerun(on_delay=1000, on_error=KeyError)\n def func():\n ...\n\nA couple of generator functions are provided in the library. These are the typical algorithms used in most systems, and\ncan serve as a baseline example for more complex delay systems.\n\n- ``constant(delay, limit)``: yields a constant delay at each iteration\n- ``linear(start, increment, limit)``: yields a linearly increasing delay at each iteration\n- ``exponential(base, multiplier, limit)``: yields an exponentially increasing delay at each iteration\n- ``fibonacci(multiplier, limit)``: yields a delay following the fibonacci pattern at each iteration\n\nIf the function fails to yield a response that isn't handled before running out of generated items by the ``on_delay``\ngenerator, a ``MaxRetryException`` is thrown.\n\n.. code-block:: python\n\n @rerun(on_delay=None, on_error=KeyError) # No retries\n def func():\n raise KeyError\n\n # MaxRetryException is raised\n\nError Handling\n^^^^^^^^^^^^^^\n\nThe ``on_error`` can be used to determine if a raised exception should be handled and the function retried. A single\nexception can be specified to be handled. If an exception is raised that isn't handled, it will bubble up to the outer\nscope without retrying the function.\n\n.. code-block:: python\n\n @rerun(on_delay=[1000], on_error=TypeError)\n def func():\n raise KeyError\n\n # KeyError isn't handled, and is thus raised\n\nMultiple errors can be given as a sequence to handle more than one.\n\n.. code-block:: python\n\n @rerun(on_delay=[1000], on_error=[ValueError, TimeoutError])\n def func():\n ...\n\nA callable object (such as a function), can be used for more complex handling of errors. These should accept a single\nvalue, the error raised, and return a boolean indicating ``True`` to handle, or ``False`` to not.\n\n.. code-block:: python\n\n @rerun(on_delay=[1000], on_error=lambda x: not isinstance(ValueError, TimeoutError))\n def func():\n ...\n\nReturn Value Handling\n^^^^^^^^^^^^^^^^^^^^^\n\nLike raised exception, return values can also be handled in a similar manner. Return values that are handled cause the\nfunction to be retried, and those that aren't are simply return. A common use case for this is when interacting with\nfunctions that yield a return value that indicates a failed state (like ``-1`` or ``None``), while other values indicate\na successful state (like ``0`` or an ``object``).\n\n.. code-block:: python\n\n @rerun(on_delay=[1000], on_return=-1)\n def func()\n return -1\n\n # Function is retried because -1 is handled\n\nOne note to make is that if a sequence is given, any value that is matched in the sequence is handled. If, however, the\nreturn value is a sequence, either a function should be used to check for equality or ``on_return`` should be a sequence\nof sequences, like so.\n\n.. code-block:: python\n\n # WRONG: checks if [-1, -1] is in the sequence [-1, -1]\n @rerun(on_delay=[1000], on_return=[-1, -1])\n def func():\n return [-1, -1] # Not handled\n\n # CORRECT: checks if [-1, -1] is the return value\n @rerun(on_delay=[1000], on_return=lambda x: x == [-1, -1])\n def func():\n return [-1, -1] # Is handled\n\n # CORRECT: checks if [-1, -1] is in the sequence [[-1, -1]]\n @rerun(on_delay=[1000], on_return=[[-1, -1]])\n def func():\n return [-1, -1] # Is handled\n\nEach time a retry takes place the ``on_retry`` callback is called, if given, passing in the current delay and the number\nof retries thus far. Logging is a common use-case for this, as shown below.\n\n.. code-block:: python\n\n def log(delay, retry):\n logging.info('Retrying function again (#%s) in %s seconds' % (delay, retry))\n\n @rerun(on_delay=[1000, 2000, 3000], on_return=-1, on_retry=log)\n def func():\n ...\n\nThe ``on_retry`` callback is called prior to waiting for the delay in-between successive retries. If calling the\nit after the delay, the ``retry_after_delay`` parameter can be specified.\n\n.. code-block:: python\n\n @rerun(on_delay=[1000],\n on_return=-1,\n on_retry=lambda d, r: print('Waited %s seconds for retry #%s' % (d, r)))\n def func():\n ...\n\n\nAdvanced Usage\n--------------\n\nInstead of using as a decorator, ``rerun`` can be used as an instead for wrapping an arbitrary number of function\ncalls. This can be achieved via the ``run`` method.\n\n.. code-block:: python\n\n def func_a():\n ...\n\n def func_b():\n ...\n\n rerunner = rerun(on_delay=..., on_error=..., on_return=..., on_retry=...)\n\n # Using same configured rerun instance\n rerun.run(func_a, args, kwargs)\n rerun.run(func_b, args, kwargs)\n\nBesides using the provided ``run`` method, like any decorator functions can be locally wrapped, passed around, and\nexecuted.\n\n.. code-block:: python\n\n def func():\n ...\n\n rerunner = rerun(on_delay=..., on_error=..., on_return=..., on_retry=...)\n rerun_func = rerunner(func)\n rerun_func(args, kwargs)\n\n # Or as a one-off like so\n rerun(...)(func)(args, kwargs)\n\nEach of the function parameters that can be passed into ``rerun``, can actually be configured to accepts different\nnumber of parameters depending on the function. They can each either accept 0 parameters, the parameters that would be\ntypically passed in, or the wrapped function's args and kwargs in addition to the parameters typically given.\n\nOptionally passing in the args and kwargs allows for building more complex callback functions. Each of the possible\nfunction variations are shown below.\n\n.. code-block:: python\n\n def on_delay(): ...\n def on_delay(*args, **kwargs): ...\n\n def on_error(): ...\n def on_error(error): ...\n def on_error(error, *args, **kwargs): ...\n\n def on_return(): ...\n def on_return(value): ...\n def on_return(value, *args, **kwargs): ...\n\n def on_retry(): ...\n def on_retry(delay, retries): ...\n def on_retry(delay, retries, *args, **kwargs): ...\n\n\nContribution\n------------\n\nContributions or suggestions are welcome! Feel free to `open an issue`_ if a bug is found or an enhancement is desired,\nor even a `pull request`_.\n\n.. _open an issue: https://github.com/jaredlgillespie/rerun.me/issues\n.. _pull request: https://github.com/jaredlgillespie/rerun.me/compare\n\nChangelog\n---------\n\nAll changes and versioning information can be found in the `CHANGELOG`_.\n\n.. _CHANGELOG: https://github.com/JaredLGillespie/rerun.me/blob/master/CHANGELOG.rst\n\nLicense\n-------\n\nCopyright (c) 2018 Jared Gillespie. See `LICENSE`_ for details.\n\n.. _LICENSE: https://github.com/JaredLGillespie/rerun.me/blob/master/LICENSE.txt\n\n\n", "description_content_type": "", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/jaredlgillespie/rerun.me", "keywords": "rerun.me rerun retry decorator", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "rerun.me", "package_url": "https://pypi.org/project/rerun.me/", "platform": "", "project_url": "https://pypi.org/project/rerun.me/", "project_urls": { "Homepage": "https://github.com/jaredlgillespie/rerun.me" }, "release_url": "https://pypi.org/project/rerun.me/1.0.0/", "requires_dist": null, "requires_python": "", "summary": "A library for rerunning erred functions with delays.", "version": "1.0.0" }, "last_serial": 3980193, "releases": { "0.1.1": [ { "comment_text": "", "digests": { "md5": "5d41994d4011d51a751fe8f4e4166df4", "sha256": "eace18f95104e29e5e82bbaf71fa3caf1561e5971bf19545b179a3567e2d8a2f" }, "downloads": -1, "filename": "rerun.me-0.1.1-py3-none-any.whl", "has_sig": false, "md5_digest": "5d41994d4011d51a751fe8f4e4166df4", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 9117, "upload_time": "2018-06-17T05:54:03", "url": "https://files.pythonhosted.org/packages/6f/84/d5f75a3d7930e6a7e24071808cd3cccb439e874b88f26a99a18ebe6c262c/rerun.me-0.1.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "2bb176e6ceb7b00c1a7b091448547a0c", "sha256": "a0e9b097e6388c47395d3c2a1adae549c7981dd2832fe2ed22ceb01cc5c35dc1" }, "downloads": -1, "filename": "rerun.me-0.1.1.tar.gz", "has_sig": false, "md5_digest": "2bb176e6ceb7b00c1a7b091448547a0c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 9642, "upload_time": "2018-06-17T05:54:04", "url": "https://files.pythonhosted.org/packages/29/dd/ff3ba6653a002ec3f30ce3ce93a77216b2a1b7e713a49ebc0dbb670e8735/rerun.me-0.1.1.tar.gz" } ], "0.1.2": [ { "comment_text": "", "digests": { "md5": "1d1ce0fb99de7041297051a50e5bd9a2", "sha256": "126b38d2ce48affa88f408bca38c8cac56f4d570aa09b12a0d8538063472f9b5" }, "downloads": -1, "filename": "rerun.me-0.1.2-py3-none-any.whl", "has_sig": false, "md5_digest": "1d1ce0fb99de7041297051a50e5bd9a2", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 9120, "upload_time": "2018-06-17T19:10:07", "url": "https://files.pythonhosted.org/packages/1f/c8/31ad6a73d9604d61953c85c797763cb7dc22777119809e7030050b99b638/rerun.me-0.1.2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "1d209d9164335316109bacf7e8c729fa", "sha256": "3a9ffebad24e1cfa6a5e6a27057d5bba85974bda3faee1d86725fc8fdb21a614" }, "downloads": -1, "filename": "rerun.me-0.1.2.tar.gz", "has_sig": false, "md5_digest": "1d209d9164335316109bacf7e8c729fa", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 9718, "upload_time": "2018-06-17T19:10:08", "url": "https://files.pythonhosted.org/packages/97/bb/60b852d54cbf29e31567ca3fd6edcbd6f06de69dcbbf7ef327eb9302c02a/rerun.me-0.1.2.tar.gz" } ], "1.0.0": [ { "comment_text": "", "digests": { "md5": "261a2a6396d77506c788e8f0be6f09aa", "sha256": "c4303bf28e2cf4119cd4c1172ee6da9b274dd44904e414dff4e552c276bb2160" }, "downloads": -1, "filename": "rerun.me-1.0.0-py3-none-any.whl", "has_sig": false, "md5_digest": "261a2a6396d77506c788e8f0be6f09aa", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 9121, "upload_time": "2018-06-20T05:47:48", "url": "https://files.pythonhosted.org/packages/a3/e9/9cd8895d7f03df2207b670d79fc008161827be6e024f80418330300df7ea/rerun.me-1.0.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "675a3bee66707dbf15714823d9d0a4b5", "sha256": "a58b8bb2121b440a1e7e5e85bdac57c76f9b50d7bef2b94ae00f955651cb056a" }, "downloads": -1, "filename": "rerun.me-1.0.0.tar.gz", "has_sig": false, "md5_digest": "675a3bee66707dbf15714823d9d0a4b5", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 9780, "upload_time": "2018-06-20T05:47:49", "url": "https://files.pythonhosted.org/packages/6c/a7/bd6b5df1bff1056ac70754dcc6d9a585ce924a0241a40fdbc6d23748af59/rerun.me-1.0.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "261a2a6396d77506c788e8f0be6f09aa", "sha256": "c4303bf28e2cf4119cd4c1172ee6da9b274dd44904e414dff4e552c276bb2160" }, "downloads": -1, "filename": "rerun.me-1.0.0-py3-none-any.whl", "has_sig": false, "md5_digest": "261a2a6396d77506c788e8f0be6f09aa", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 9121, "upload_time": "2018-06-20T05:47:48", "url": "https://files.pythonhosted.org/packages/a3/e9/9cd8895d7f03df2207b670d79fc008161827be6e024f80418330300df7ea/rerun.me-1.0.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "675a3bee66707dbf15714823d9d0a4b5", "sha256": "a58b8bb2121b440a1e7e5e85bdac57c76f9b50d7bef2b94ae00f955651cb056a" }, "downloads": -1, "filename": "rerun.me-1.0.0.tar.gz", "has_sig": false, "md5_digest": "675a3bee66707dbf15714823d9d0a4b5", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 9780, "upload_time": "2018-06-20T05:47:49", "url": "https://files.pythonhosted.org/packages/6c/a7/bd6b5df1bff1056ac70754dcc6d9a585ce924a0241a40fdbc6d23748af59/rerun.me-1.0.0.tar.gz" } ] }