{ "info": { "author": "Aric Coady", "author_email": "aric.coady@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Operating System :: OS Independent", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Topic :: Software Development :: Libraries :: Python Modules" ], "description": "[![image](https://img.shields.io/pypi/v/waiter.svg)](https://pypi.org/project/waiter/)\n[![image](https://img.shields.io/pypi/pyversions/waiter.svg)](https://python3statement.org)\n[![image](https://pepy.tech/badge/waiter)](https://pepy.tech/project/waiter)\n![image](https://img.shields.io/pypi/status/waiter.svg)\n[![image](https://img.shields.io/travis/coady/waiter.svg)](https://travis-ci.org/coady/waiter)\n[![image](https://img.shields.io/codecov/c/github/coady/waiter.svg)](https://codecov.io/github/coady/waiter)\n[![image](https://readthedocs.org/projects/waiter/badge)](https://waiter.readthedocs.io)\n[![image](https://requires.io/github/coady/waiter/requirements.svg)](https://requires.io/github/coady/waiter/requirements/)\n[![image](https://api.codeclimate.com/v1/badges/21c4db3602347a7e794a/maintainability)](https://codeclimate.com/github/coady/waiter/maintainability)\n[![image](https://img.shields.io/badge/code%20style-black-000000.svg)](https://pypi.org/project/black/)\n\nDoes Python need yet another retry / poll library?\nIt needs at least one that isn't coupled to decorators and functions.\nDecorators prevent the caller from customizing delay options,\nand organizing the code around functions hinders any custom handling of failures.\n\nWaiter is built around iteration instead,\nbecause the foundation of retrying / polling is a slowly executing loop.\nThe resulting interface is both easier to use and more flexible,\ndecoupling the delay algorithms from the application logic.\n\n# Usage\n## creation\nSupply a number of seconds to repeat endlessly, or any iterable of seconds.\n\n```python\nfrom waiter import wait\n\nwait(1) # 1, 1, 1, 1, ...\nwait([1] * 3) # 1, 1, 1\nwait([0.5, 0.5, 60]) # circuit breaker\n```\n\nIterable delays can express any waiting strategy, and constructors for common algorithms are also provided.\n\n```python\nwait.count(1) # incremental backoff 1, 2, 3, 4, 5, ...\nwait(1) + 1 # alternate syntax 1, 2, 3, 4, 5, ...\nwait.fibonacci(1) # 1, 1, 2, 3, 5, ...\nwait.polynomial(2) # 0, 1, 4, 9, 16, ...\n\nwait.exponential(2) # exponential backoff 1, 2, 4, 8, ...\nbackoff = wait(1) * 2 # alternate syntax 1, 2, 4, 8, ...\nbackoff[:3] # limit attempt count 1, 2, 4\nbackoff <= 5 # set maximum delay 1, 2, 4, 5, 5, 5, ...\nbackoff.random(-1, 1) # add random jitter\n```\n\n## iteration\nThen simply use the `wait` object like any iterable, yielding the amount of elapsed time.\nTimeouts also supported of course.\n\n```python\nfrom waiter import wait, suppress, first\n\nfor elapsed in wait(delays): # first iteration is immediate\n with suppress(exception): # then each subsequent iteration sleeps as necessary\n ...\n break\n\nfor _ in wait(delays, timeout): # standard convention for ignoring a loop variable\n ... # won't sleep past the timeout\n if ...:\n break\n\nresults = (... for _ in wait(delays)) # expressions are even easier\nfirst(predicate, results[, default]) # filter for first true item\nassert any(results) # perfect for tests too\n```\n\n## functions\nYes, functional versions are provided, as well as being trivial to implement.\n\n```python\nwait(...).throttle(iterable) # generate items from iterable\nwait(...).repeat(func, *args, **kwargs) # generate successive results\nwait(...).retry(exception, func, *args, **kwargs) # return first success or re-raise exception\nwait(...).poll(predicate, func, *args, **kwargs) # return first success or raise StopIteration\n```\n\nThe decorator variants are simply partial applications of the corresponding methods.\nNote decorator syntax doesn't support arbitrary expressions.\n\n```python\nbackoff = wait(0.1) * 2\n@backoff.repeating\n@backoff.retrying(exception)\n@backoff.polling(predicate)\n```\n\nBut in the real world:\n* the function may not exist or be succinctly written as a lambda\n* the predicate may not exist or be succinctly written as a lambda\n* logging may be required\n* there may be complex handling of different exceptions or results\n\nSo consider the block form, just as decorators don't render `with` blocks superfluous.\nAlso note `wait` objects are re-iterable provided their original delays were.\n\n## async\nIn Python >=3.6, waiters also support async iteration.\n`throttle` optionally accepts an async iterable.\n`repeat`, `retry`, and `poll` optionally accept coroutine functions.\n\n## statistics\nWaiter objects have a `stats` attribute for aggregating statistics about the calls made.\nThe base implementation provides `total` and `failure` counts.\nThe interface of the `stats` object itself is considered provisional for now,\nbut can be extended by overriding the `Stats` class attribute.\nThis also allows customization of the iterable values; elapsed time is the default.\n\n# Installation\n\n $ pip install waiter\n\n# Dependencies\n* multimethod (if Python >=3.6)\n\n# Tests\n100% branch coverage.\n\n $ pytest [--cov]\n\n# Changes\n1.0\n* Map a function across an iterable in batches\n\n0.6\n* Extensible iterable values and statistics\n* Additional constructors: fibonacci, polynomial, accumulate\n\n0.5\n* Asynchronous iteration\n\n0.4\n* Decorators support methods\n* Iterables can be throttled\n\n0.3\n* Waiters behave as iterables instead of iterators\n* Support for function decorators\n\n0.2\n* `suppress` context manager for exception handling\n* `repeat` method for decoupled iteration\n* `first` function for convenient filtering\n\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/coady/waiter", "keywords": "wait retry poll delay sleep timeout incremental exponential backoff async", "license": "Apache Software License", "maintainer": "", "maintainer_email": "", "name": "waiter", "package_url": "https://pypi.org/project/waiter/", "platform": "", "project_url": "https://pypi.org/project/waiter/", "project_urls": { "Documentation": "https://waiter.readthedocs.io", "Homepage": "https://github.com/coady/waiter" }, "release_url": "https://pypi.org/project/waiter/1.0/", "requires_dist": [ "multimethod (>=0.7) ; python_version>=\"3.6\"", "nbsphinx ; extra == 'docs'", "m2r ; extra == 'docs'", "jupyter ; extra == 'docs'", "aiohttp ; extra == 'docs'" ], "requires_python": ">=2.7", "summary": "Delayed iteration for polling and retries.", "version": "1.0" }, "last_serial": 5594193, "releases": { "0.0": [], "0.1": [ { "comment_text": "", "digests": { "md5": "5da99722398ed4cfcb8d73773cee7418", "sha256": "af5209449caebe319e9e22f1ed098f6e7f048f9f2a54487de9fca08c1a77670f" }, "downloads": -1, "filename": "waiter-0.1.tar.gz", "has_sig": false, "md5_digest": "5da99722398ed4cfcb8d73773cee7418", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 4379, "upload_time": "2016-03-25T01:14:21", "url": "https://files.pythonhosted.org/packages/4e/17/1f61cbd244fc9d925be2346cb94df65707200305b4340fe3a7082963cdcc/waiter-0.1.tar.gz" } ], "0.2": [ { "comment_text": "", "digests": { "md5": "4d4266609e2d3ab6a476b7be22c75aeb", "sha256": "0e36dfe3c7cf8cab379403c112d17c1fcf634a5d81d5db224316765cf9c58089" }, "downloads": -1, "filename": "waiter-0.2.tar.gz", "has_sig": false, "md5_digest": "4d4266609e2d3ab6a476b7be22c75aeb", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 4685, "upload_time": "2016-04-03T21:51:51", "url": "https://files.pythonhosted.org/packages/8e/58/e3ef5d61c12819b116db7fa9e7f27b2c383e65a0863600ab7911a8be95d1/waiter-0.2.tar.gz" } ], "0.3": [ { "comment_text": "", "digests": { "md5": "41a51abacbb70c55ad532bb095dfd409", "sha256": "44a859d85bc770087ea5ae03e89dd829208ddedf84e3339e00b7267752334a2a" }, "downloads": -1, "filename": "waiter-0.3.tar.gz", "has_sig": false, "md5_digest": "41a51abacbb70c55ad532bb095dfd409", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 5126, "upload_time": "2016-04-29T18:54:44", "url": "https://files.pythonhosted.org/packages/94/b5/50f56f3546a3e2dcd5dcf12f24bb2871e642d3cc22f434f3ad01bc69114a/waiter-0.3.tar.gz" } ], "0.4": [ { "comment_text": "", "digests": { "md5": "56061a9709251b040281d6aef7bbe28f", "sha256": "296de5eae8b1027ea7bd3b4208414d094bf447e43ed7c7508bb62e2fec15c082" }, "downloads": -1, "filename": "waiter-0.4-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "56061a9709251b040281d6aef7bbe28f", "packagetype": "bdist_wheel", "python_version": "2.7", "requires_python": null, "size": 6824, "upload_time": "2017-01-02T23:16:30", "url": "https://files.pythonhosted.org/packages/1a/33/b100bd61a722c3ae1d467840e5fe79219255ccaca0bda9f9c2e261c8b551/waiter-0.4-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "d6d9d102bdab4e1b7b0b7d7256db2219", "sha256": "6852ae0770903108d5671220cf6336074085e5577b26d15e9f7eaead711f1df0" }, "downloads": -1, "filename": "waiter-0.4.tar.gz", "has_sig": false, "md5_digest": "d6d9d102bdab4e1b7b0b7d7256db2219", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 5410, "upload_time": "2017-01-02T23:16:28", "url": "https://files.pythonhosted.org/packages/f1/25/e9f4b82920569a5926869f1b01a2761be1759cb4a8540fda6919b08bcf56/waiter-0.4.tar.gz" } ], "0.5": [ { "comment_text": "", "digests": { "md5": "bae8d9d0c5a364964fd4bdf0e9281c34", "sha256": "1e8a374baacb6522e7647172687adca21239d751e3b06b4e6ba82c76293663c0" }, "downloads": -1, "filename": "waiter-0.5-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "bae8d9d0c5a364964fd4bdf0e9281c34", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 8121, "upload_time": "2017-09-17T23:58:05", "url": "https://files.pythonhosted.org/packages/26/01/51febec99e7fdf81fb3ae3fc66ed6af1ffcb4b319b0ab9fa73bd0ec51a64/waiter-0.5-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "729dd37cc1c19411e7cb49bf438f2760", "sha256": "37bea330bb1e4ac8804769337c0164e3a48592fbd5548a765c23d27a09987884" }, "downloads": -1, "filename": "waiter-0.5.tar.gz", "has_sig": false, "md5_digest": "729dd37cc1c19411e7cb49bf438f2760", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 6278, "upload_time": "2017-09-17T23:58:06", "url": "https://files.pythonhosted.org/packages/dc/4e/445c40008bae3476c4c0c65876e4911c6fafaf79be2061229c58d6afcdc3/waiter-0.5.tar.gz" } ], "0.6": [ { "comment_text": "", "digests": { "md5": "1414a58a380eb631ae19ac6f163cbf8b", "sha256": "feb92477f41b5484bce6108bdc7d91cc078991e6fd947040b992a48fcd0a8ad7" }, "downloads": -1, "filename": "waiter-0.6-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "1414a58a380eb631ae19ac6f163cbf8b", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": ">=2.7", "size": 6811, "upload_time": "2018-11-25T20:20:32", "url": "https://files.pythonhosted.org/packages/51/fe/9edbafbc03457143bdb65c0f220e00c94f5a7bf3c7aac739ff2371405521/waiter-0.6-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "89e33d961ae5ba94a11cced95b276b6e", "sha256": "87c50c24908bdbe372bcbcf0f5a0df1dcf4f97c7ab17bf1900ecda6f4ea79b3b" }, "downloads": -1, "filename": "waiter-0.6.tar.gz", "has_sig": false, "md5_digest": "89e33d961ae5ba94a11cced95b276b6e", "packagetype": "sdist", "python_version": "source", "requires_python": ">=2.7", "size": 11527, "upload_time": "2018-11-25T20:20:33", "url": "https://files.pythonhosted.org/packages/58/86/8a6fcf7c70f34510d9e022f00e55689ee30cd57e05b4bb54ef695a14bcf2/waiter-0.6.tar.gz" } ], "1.0": [ { "comment_text": "", "digests": { "md5": "099e95fb66ba199384f31ff7678c0162", "sha256": "2dfb6c8117e817434a0c9c7110aeaf836542ffafa892a2f0e154e6f1c58ba913" }, "downloads": -1, "filename": "waiter-1.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "099e95fb66ba199384f31ff7678c0162", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": ">=2.7", "size": 7111, "upload_time": "2019-07-27T22:08:35", "url": "https://files.pythonhosted.org/packages/80/3e/3d36ce3eb1910434eef9c63e82436bc7de1e5e7ce90a6e9d6325318122d3/waiter-1.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "6a04b2eea4889f1d85733d6b8da75547", "sha256": "84b3696eaaa4439e4a5d747126af0331c93850e6f3ff6094e069ece1583a6452" }, "downloads": -1, "filename": "waiter-1.0.tar.gz", "has_sig": false, "md5_digest": "6a04b2eea4889f1d85733d6b8da75547", "packagetype": "sdist", "python_version": "source", "requires_python": ">=2.7", "size": 11887, "upload_time": "2019-07-27T22:08:36", "url": "https://files.pythonhosted.org/packages/3e/6b/4352f46048810238e6f09ae9de02580291b755fe3f22ee1079b20fa83a43/waiter-1.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "099e95fb66ba199384f31ff7678c0162", "sha256": "2dfb6c8117e817434a0c9c7110aeaf836542ffafa892a2f0e154e6f1c58ba913" }, "downloads": -1, "filename": "waiter-1.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "099e95fb66ba199384f31ff7678c0162", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": ">=2.7", "size": 7111, "upload_time": "2019-07-27T22:08:35", "url": "https://files.pythonhosted.org/packages/80/3e/3d36ce3eb1910434eef9c63e82436bc7de1e5e7ce90a6e9d6325318122d3/waiter-1.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "6a04b2eea4889f1d85733d6b8da75547", "sha256": "84b3696eaaa4439e4a5d747126af0331c93850e6f3ff6094e069ece1583a6452" }, "downloads": -1, "filename": "waiter-1.0.tar.gz", "has_sig": false, "md5_digest": "6a04b2eea4889f1d85733d6b8da75547", "packagetype": "sdist", "python_version": "source", "requires_python": ">=2.7", "size": 11887, "upload_time": "2019-07-27T22:08:36", "url": "https://files.pythonhosted.org/packages/3e/6b/4352f46048810238e6f09ae9de02580291b755fe3f22ee1079b20fa83a43/waiter-1.0.tar.gz" } ] }