Name | recur-command JSON |
Version |
0.5.0
JSON |
| download |
home_page | None |
Summary | Retry a command with exponential backoff and jitter. |
upload_time | 2024-10-17 15:16:13 |
maintainer | None |
docs_url | None |
author | None |
requires_python | >=3.9 |
license | None |
keywords |
retry
|
VCS |
|
bugtrack_url |
|
requirements |
No requirements were recorded.
|
Travis-CI |
No Travis.
|
coveralls test coverage |
No coveralls.
|
# recur
**recur** is a command-line tool that runs a single command repeatedly until it succeeds or allowed attempts run out.
It implements optional [exponential backoff](https://en.wikipedia.org/wiki/Exponential_backoff) with configurable [jitter](https://en.wikipedia.org/wiki/Thundering_herd_problem#Mitigation).
It lets you define the success condition.
## Requirements
Python 3.9 or later,
PyPI package `simpleeval` (installed automatically with `recur-command`).
## Installation
The recommended way to install recur is [from PyPI](https://pypi.org/project/recur-command/) with [pipx](https://github.com/pypa/pipx).
```shell
pipx install recur-command
# or
pip install --user recur-command
```
recur is also available for download as a single-file Python [ZIP application](https://peps.python.org/pep-0441/) or "zipapp" with its dependencies included.
A regular Python interpreter can run zipapps.
Zipapps are attached to
[GitLab releases](https://gitlab.com/dbohdan/recur/-/releases)
as `.pyz` files.
They are
[automatically built](https://gitlab.com/dbohdan/recur/-/artifacts)
for commits.
## Usage
```none
usage: recur [-h] [-V] [-b BACKOFF] [-c COND] [-d DELAY] [-j JITTER] [-m MAX]
[-t TRIES] [-v]
command ...
Retry a command with exponential backoff and jitter.
positional arguments:
command command to run
args arguments
options:
-h, --help show this help message and exit
-V, --version show program's version number and exit
-b BACKOFF, --backoff BACKOFF
multiplier applied to delay on every attempt (default:
1, no backoff)
-c COND, --condition COND
success condition (simpleeval expression, default:
"code == 0")
-d DELAY, --delay DELAY
constant or initial exponential delay (seconds,
default: 0)
-j JITTER, --jitter JITTER
additional random delay (maximum seconds or "min,max"
seconds, default: "0,0")
-m MAX, --max-delay MAX
maximum delay (seconds, default: 3600)
-t TRIES, --tries TRIES
maximum number of attempts (negative for infinite,
default: 5)
-v, --verbose announce exit code and attempt number; adds debug
information for errors if used twice
```
recur exits with the last command's exit code, unless the user overrides this in the condition.
When the command is not found during the last attempt,
recur exits with the code 255.
The CLI options are modeled after the parameters of the [`retry`](https://github.com/invl/retry) decorator, which Python programmers may recognize.
However, recur does not use `retry`.
The jitter (random delay) behavior is different.
Jitter is applied starting with the first retry, not the second.
I think this is what the user expects.
A single-number jitter argument results in random, not constant, jitter.
## Conditions
recur supports a limited form of scripting.
It allows you to set the success condition using the simpleeval [expression language](https://github.com/danthedeckie/simpleeval#operators), which is a subset of Python.
The default condition is `code == 0`.
It means recur will stop retrying when the exit code of the command is zero.
You can use the following variables in the condition expression:
* `attempt`: `int` — the number of the current attempt, starting with one.
Combine with `--tries -1` to use the condition instead of the built-in attempt counter.
* `code`: `int | None` — the exit code of the last command.
`code` is `None` when the command was not found.
* `command_found`: `bool` — whether the last command was found.
* `time`: `float` — the time the most recent attempt took, in seconds.
* `total_time`: `float` — the time between the start of the first attempt and the end of the most recent, again in seconds.
* `max_tries`: `int` — the value of the option `--tries`.
recur defines one custom function:
* `exit(code: int | None) -> None` — exit with the exit code.
If `code` is `None`, exit with the exit code for a missing command (255).
This function allows you to override the default behavior of returning the last command's exit code.
For example, you can make recur exit with success when the command fails.
```shell
recur --condition 'code != 0 and exit(0)' sh -c 'exit 1'
# or
recur --condition 'exit(0) if code != 0 else False' sh -c 'exit 1'
```
In the following example we stop early and do not retry when the command's exit code indicates incorrect usage or a problem with the installation.
```shell
recur --condition 'code == 0 or (code in {1, 2, 3, 4} and exit(code))' curl "$url"
```
## License
MIT.
## Alternatives
recur was inspired by [retry-cli](https://github.com/tirsen/retry-cli).
I wanted something like retry-cli, but without the Node.js dependency.
There are other tools like this.
* [retry (joshdk)](https://github.com/joshdk/retry).
Written in Go.
`go install github.com/joshdk/retry@master`.
* [retry (kadwanev)](https://github.com/kadwanev/retry).
Written in Bash.
* [retry (minfrin)](https://github.com/minfrin/retry).
Written in C.
Packaged in Debian and Ubuntu.
`sudo apt install retry`.
* [retry (timofurrer)](https://github.com/timofurrer/retry-cmd).
Written in Rust.
`cargo install retry-cmd`.
* [retry-cli](https://github.com/tirsen/retry-cli).
Written in JavaScript for Node.js.
`npx retry-cli`.
Raw data
{
"_id": null,
"home_page": null,
"name": "recur-command",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.9",
"maintainer_email": null,
"keywords": "retry",
"author": null,
"author_email": "\"D. Bohdan\" <dbohdan@dbohdan.com>",
"download_url": "https://files.pythonhosted.org/packages/c6/59/7c04e141d5afd85b860f4b304e47ccf09022b634690cbd5b001c4fa847e8/recur_command-0.5.0.tar.gz",
"platform": null,
"description": "# recur\n\n**recur** is a command-line tool that runs a single command repeatedly until it succeeds or allowed attempts run out.\nIt implements optional [exponential backoff](https://en.wikipedia.org/wiki/Exponential_backoff) with configurable [jitter](https://en.wikipedia.org/wiki/Thundering_herd_problem#Mitigation).\nIt lets you define the success condition.\n\n\n## Requirements\n\nPython 3.9 or later,\nPyPI package `simpleeval` (installed automatically with `recur-command`).\n\n\n## Installation\n\nThe recommended way to install recur is [from PyPI](https://pypi.org/project/recur-command/) with [pipx](https://github.com/pypa/pipx).\n\n```shell\npipx install recur-command\n# or\npip install --user recur-command\n```\n\nrecur is also available for download as a single-file Python [ZIP application](https://peps.python.org/pep-0441/) or \"zipapp\" with its dependencies included.\nA regular Python interpreter can run zipapps.\nZipapps are attached to\n[GitLab releases](https://gitlab.com/dbohdan/recur/-/releases)\nas `.pyz` files.\nThey are\n[automatically built](https://gitlab.com/dbohdan/recur/-/artifacts)\nfor commits.\n\n\n## Usage\n\n```none\nusage: recur [-h] [-V] [-b BACKOFF] [-c COND] [-d DELAY] [-j JITTER] [-m MAX]\n [-t TRIES] [-v]\n command ...\n\nRetry a command with exponential backoff and jitter.\n\npositional arguments:\n command command to run\n args arguments\n\noptions:\n -h, --help show this help message and exit\n -V, --version show program's version number and exit\n -b BACKOFF, --backoff BACKOFF\n multiplier applied to delay on every attempt (default:\n 1, no backoff)\n -c COND, --condition COND\n success condition (simpleeval expression, default:\n \"code == 0\")\n -d DELAY, --delay DELAY\n constant or initial exponential delay (seconds,\n default: 0)\n -j JITTER, --jitter JITTER\n additional random delay (maximum seconds or \"min,max\"\n seconds, default: \"0,0\")\n -m MAX, --max-delay MAX\n maximum delay (seconds, default: 3600)\n -t TRIES, --tries TRIES\n maximum number of attempts (negative for infinite,\n default: 5)\n -v, --verbose announce exit code and attempt number; adds debug\n information for errors if used twice\n```\n\nrecur exits with the last command's exit code, unless the user overrides this in the condition.\nWhen the command is not found during the last attempt,\nrecur exits with the code 255.\n\nThe CLI options are modeled after the parameters of the [`retry`](https://github.com/invl/retry) decorator, which Python programmers may recognize.\nHowever, recur does not use `retry`.\nThe jitter (random delay) behavior is different.\nJitter is applied starting with the first retry, not the second.\nI think this is what the user expects.\nA single-number jitter argument results in random, not constant, jitter.\n\n\n## Conditions\n\nrecur supports a limited form of scripting.\nIt allows you to set the success condition using the simpleeval [expression language](https://github.com/danthedeckie/simpleeval#operators), which is a subset of Python.\nThe default condition is `code == 0`.\nIt means recur will stop retrying when the exit code of the command is zero.\n\nYou can use the following variables in the condition expression:\n\n* `attempt`: `int` \u2014 the number of the current attempt, starting with one.\nCombine with `--tries -1` to use the condition instead of the built-in attempt counter. \n* `code`: `int | None` \u2014 the exit code of the last command.\n`code` is `None` when the command was not found.\n* `command_found`: `bool` \u2014 whether the last command was found.\n* `time`: `float` \u2014 the time the most recent attempt took, in seconds.\n* `total_time`: `float` \u2014 the time between the start of the first attempt and the end of the most recent, again in seconds.\n* `max_tries`: `int` \u2014 the value of the option `--tries`.\n\nrecur defines one custom function:\n\n* `exit(code: int | None) -> None` \u2014 exit with the exit code.\nIf `code` is `None`, exit with the exit code for a missing command (255).\n\nThis function allows you to override the default behavior of returning the last command's exit code.\nFor example, you can make recur exit with success when the command fails.\n\n```shell\nrecur --condition 'code != 0 and exit(0)' sh -c 'exit 1'\n# or\nrecur --condition 'exit(0) if code != 0 else False' sh -c 'exit 1'\n```\n\nIn the following example we stop early and do not retry when the command's exit code indicates incorrect usage or a problem with the installation.\n\n```shell\nrecur --condition 'code == 0 or (code in {1, 2, 3, 4} and exit(code))' curl \"$url\"\n```\n\n## License\n\nMIT.\n\n\n## Alternatives\n\nrecur was inspired by [retry-cli](https://github.com/tirsen/retry-cli).\nI wanted something like retry-cli, but without the Node.js dependency.\nThere are other tools like this.\n\n* [retry (joshdk)](https://github.com/joshdk/retry).\nWritten in Go.\n`go install github.com/joshdk/retry@master`.\n* [retry (kadwanev)](https://github.com/kadwanev/retry).\nWritten in Bash.\n* [retry (minfrin)](https://github.com/minfrin/retry).\nWritten in C.\nPackaged in Debian and Ubuntu.\n`sudo apt install retry`.\n* [retry (timofurrer)](https://github.com/timofurrer/retry-cmd).\nWritten in Rust.\n`cargo install retry-cmd`.\n* [retry-cli](https://github.com/tirsen/retry-cli).\nWritten in JavaScript for Node.js.\n`npx retry-cli`.\n",
"bugtrack_url": null,
"license": null,
"summary": "Retry a command with exponential backoff and jitter.",
"version": "0.5.0",
"project_urls": null,
"split_keywords": [
"retry"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "ce1c2e996724492697a1a3beb05f3da2b3b268b3242c9d3f94344ceb4a984efc",
"md5": "e94ac0962a4211d1889a374790c21fd7",
"sha256": "3144954f5dc35275a12773e4536690709a095d1d4efa2f43b9470031bb7a91f7"
},
"downloads": -1,
"filename": "recur_command-0.5.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "e94ac0962a4211d1889a374790c21fd7",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.9",
"size": 8657,
"upload_time": "2024-10-17T15:16:11",
"upload_time_iso_8601": "2024-10-17T15:16:11.318360Z",
"url": "https://files.pythonhosted.org/packages/ce/1c/2e996724492697a1a3beb05f3da2b3b268b3242c9d3f94344ceb4a984efc/recur_command-0.5.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "c6597c04e141d5afd85b860f4b304e47ccf09022b634690cbd5b001c4fa847e8",
"md5": "a153df3f8a9da4e9704d8272ae91442c",
"sha256": "c389a5202261292e5127b2acb3c66e2698c9fcd99f4ebf08f2c13cc5314f1c46"
},
"downloads": -1,
"filename": "recur_command-0.5.0.tar.gz",
"has_sig": false,
"md5_digest": "a153df3f8a9da4e9704d8272ae91442c",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.9",
"size": 23735,
"upload_time": "2024-10-17T15:16:13",
"upload_time_iso_8601": "2024-10-17T15:16:13.173806Z",
"url": "https://files.pythonhosted.org/packages/c6/59/7c04e141d5afd85b860f4b304e47ccf09022b634690cbd5b001c4fa847e8/recur_command-0.5.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-10-17 15:16:13",
"github": false,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"lcname": "recur-command"
}