├── tests ├── __init__.py └── test_refry.py ├── .python-version ├── src └── refry │ ├── __init__.py │ └── main.py ├── .gitignore ├── requirements.lock ├── requirements-dev.lock ├── .github └── workflows │ └── latest-changes.yml ├── pyproject.toml ├── LICENSE ├── changelog.md ├── CONTRIBUTING.md ├── CODE_OF_CONDUCT.md └── README.md /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.python-version: -------------------------------------------------------------------------------- 1 | 3.12.3 2 | -------------------------------------------------------------------------------- /src/refry/__init__.py: -------------------------------------------------------------------------------- 1 | from .main import retry 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # python generated files 2 | __pycache__/ 3 | *.py[oc] 4 | build/ 5 | dist/ 6 | wheels/ 7 | *.egg-info 8 | 9 | # venv 10 | .venv 11 | 12 | # pytest 13 | .pytest_cache/ -------------------------------------------------------------------------------- /requirements.lock: -------------------------------------------------------------------------------- 1 | # generated by rye 2 | # use `rye lock` or `rye sync` to update this lockfile 3 | # 4 | # last locked with the following flags: 5 | # pre: false 6 | # features: [] 7 | # all-features: false 8 | # with-sources: false 9 | # generate-hashes: false 10 | 11 | -e file:. 12 | -------------------------------------------------------------------------------- /requirements-dev.lock: -------------------------------------------------------------------------------- 1 | # generated by rye 2 | # use `rye lock` or `rye sync` to update this lockfile 3 | # 4 | # last locked with the following flags: 5 | # pre: false 6 | # features: [] 7 | # all-features: false 8 | # with-sources: false 9 | # generate-hashes: false 10 | 11 | -e file:. 12 | iniconfig==2.0.0 13 | # via pytest 14 | mypy==1.10.0 15 | mypy-extensions==1.0.0 16 | # via mypy 17 | packaging==24.0 18 | # via pytest 19 | pluggy==1.5.0 20 | # via pytest 21 | pytest==8.2.2 22 | ruff==0.4.8 23 | typing-extensions==4.12.2 24 | # via mypy 25 | -------------------------------------------------------------------------------- /.github/workflows/latest-changes.yml: -------------------------------------------------------------------------------- 1 | name: Latest Changes 2 | 3 | on: 4 | pull_request_target: 5 | branches: 6 | - main 7 | types: 8 | - closed 9 | # For manually triggering it 10 | workflow_dispatch: 11 | inputs: 12 | number: 13 | description: PR number 14 | required: true 15 | 16 | jobs: 17 | latest-changes: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/checkout@v4 21 | with: 22 | token: ${{ secrets.REPO_TOKEN }} 23 | - uses: tiangolo/latest-changes@0.2.1 24 | with: 25 | token: ${{ secrets.REPO_TOKEN }} 26 | latest_changes_file: changelog.md 27 | latest_changes_header: '# Changelog' -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "refry" 3 | version = "0.2.0" 4 | description = "Refry is a modern, maintained, typed easy-to-use retry decorator." 5 | authors = [ 6 | { name = "Daniel Roy Greenfeld", email = "daniel@feldroy.com" } 7 | ] 8 | dependencies = [] 9 | readme = "README.md" 10 | requires-python = ">= 3.8" 11 | 12 | [project.urls] 13 | homepage = "https://github.com/pydanny/refry" 14 | bugs = "https://github.com/pydanny/refry/issues" 15 | 16 | 17 | [build-system] 18 | requires = ["hatchling"] 19 | build-backend = "hatchling.build" 20 | 21 | [tool.rye] 22 | managed = true 23 | dev-dependencies = [ 24 | "ruff>=0.4.8", 25 | "pytest>=8.2.2", 26 | "mypy>=1.10.0", 27 | ] 28 | 29 | [tool.rye.scripts] 30 | format = "ruff format . --fix && ruff check ." 31 | test = "pytest ." 32 | 33 | [tool.hatch.metadata] 34 | allow-direct-references = true 35 | 36 | [tool.hatch.build.targets.wheel] 37 | packages = ["src/refry"] 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2024 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | * feat(logging): Enhance logging in retry decorator. PR [#40](https://github.com/pydanny/refry/pull/40) by [@rjvitorino](https://github.com/rjvitorino). 4 | * feat(docs): Add illustrative image to README. PR [#39](https://github.com/pydanny/refry/pull/39) by [@rjvitorino](https://github.com/rjvitorino). 5 | * feat(retry): Add support for backoff strategies and jitter. PR [#38](https://github.com/pydanny/refry/pull/38) by [@rjvitorino](https://github.com/rjvitorino). 6 | * 📝 Update `CONTRIBUTING.md` with detailed contribution guidelines. PR [#37](https://github.com/pydanny/refry/pull/37) by [@rjvitorino](https://github.com/rjvitorino). 7 | * Add CODE_OF_CONDUCT.md. PR [#34](https://github.com/pydanny/refry/pull/34) by [@rjvitorino](https://github.com/rjvitorino). 8 | * docs(readme): add implementation comparison. PR [#33](https://github.com/pydanny/refry/pull/33) by [@rjvitorino](https://github.com/rjvitorino). 9 | * docs(readme): include arguments usage. PR [#32](https://github.com/pydanny/refry/pull/32) by [@rjvitorino](https://github.com/rjvitorino). 10 | * Remove contributors GH action. PR [#31](https://github.com/pydanny/refry/pull/31) by [@pydanny](https://github.com/pydanny). 11 | * Add missing changelog module. PR [#16](https://github.com/pydanny/refry/pull/16) by [@pydanny](https://github.com/pydanny). 12 | * Adds unit tests for retry decorator. PR [#8](https://github.com/pydanny/refry/pull/8) by [@rjvitorino](https://github.com/rjvitorino). 13 | * Add latest-changes GH action. PR [#13](https://github.com/pydanny/refry/pull/13) by [@pydanny](https://github.com/pydanny). 14 | * Update README.md with bash syntax highlighting. PR [#9](https://github.com/pydanny/refry/pull/9) by [@rjvitorino](https://github.com/rjvitorino). 15 | * Add contributing workflow. PR [#10](https://github.com/pydanny/refry/pull/10) by [@pydanny](https://github.com/pydanny). 16 | -------------------------------------------------------------------------------- /src/refry/main.py: -------------------------------------------------------------------------------- 1 | import functools 2 | import logging 3 | import math 4 | import random 5 | import time 6 | from typing import Any 7 | from typing import Callable 8 | from typing import Type 9 | 10 | # Logger's log level is configurable with `log_level` 11 | logger = logging.getLogger(__name__) 12 | 13 | 14 | def retry( 15 | backoff_increment: int = 5, 16 | backoff_type: str = "sequential", 17 | jitter: bool = False, 18 | rate_limit_exception: Type[Exception] = Exception, 19 | retries: int = 5, 20 | log_level: int = logging.INFO, 21 | ) -> Callable: 22 | """ 23 | Decorator to retry a function if it raises a custom exception. 24 | """ 25 | 26 | def calculate_backoff(attempt: int) -> float: 27 | backoff_strategies = { 28 | "sequential": backoff_increment * (attempt + 1), 29 | "logarithmic": math.log(attempt + 2) * backoff_increment, 30 | "exponential": (2**attempt) * backoff_increment, 31 | } 32 | backoff = backoff_strategies.get( 33 | backoff_type, backoff_increment * (attempt + 1) 34 | ) 35 | return backoff + random.uniform(0, backoff_increment) if jitter else backoff 36 | 37 | def _outer_wrapper(func: Callable) -> Callable: 38 | @functools.wraps(func) 39 | def _wrapper(*args: list[Any], **kwargs: list[Any]) -> Any: 40 | logger.setLevel(log_level) 41 | logger.log( 42 | log_level, 43 | f"Applying retry decorator with backoff_type='{backoff_type}', " 44 | f"backoff_increment={backoff_increment}, jitter={jitter}, retries={retries}", 45 | ) 46 | for attempt in range(retries): 47 | try: 48 | return func(*args, **kwargs) 49 | except rate_limit_exception as exception: 50 | current_backoff = calculate_backoff(attempt) 51 | logger.log( 52 | log_level, 53 | f"Attempt {attempt + 1} failed with exception: {exception}. " 54 | f"Retrying in {current_backoff:.2f} seconds.", 55 | ) 56 | time.sleep(current_backoff) 57 | # If all retries fail, log the final failure and raise the exception without additional message 58 | logger.error( 59 | f"All {retries} retries failed for function {func.__name__}.", 60 | exc_info=True, 61 | ) 62 | raise rate_limit_exception 63 | 64 | return _wrapper 65 | 66 | return _outer_wrapper 67 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to refry 2 | 3 | First off, thanks for taking the time to contribute! 🎉 4 | 5 | The following is a set of guidelines for contributing to refry. These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request. 6 | 7 | ## Table of Contents 8 | 9 | - [Code of Conduct](#code-of-conduct) 10 | - [How can I contribute?](#how-can-i-contribute) 11 | - [Reporting bugs](#reporting-bugs) 12 | - [Suggesting enhancements](#suggesting-enhancements) 13 | - [Pull Requests](#pull-requests) 14 | - [Style Guides](#style-guides) 15 | - [Git Commit Messages](#git-commit-messages) 16 | - [Python Style Guide](#python-style-guide) 17 | 18 | ## Code of Conduct 19 | 20 | This project and everyone participating in it is governed by refry's [Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report unacceptable behavior to [@pydanny](https://github.com/pydanny). 21 | 22 | ## How can I contribute? 23 | 24 | ### Reporting bugs 25 | 26 | This section guides you through submitting a bug report for refry. Following these guidelines helps maintainers and the community understand your report, reproduce the behavior, and find related reports. 27 | 28 | Before creating a bug report, please check if an issue already exists and has been addressed. 29 | 30 | #### How do I submit a (good) bug report? 31 | 32 | - **Use a clear and descriptive title** for the issue to identify the problem. 33 | - **Describe the exact steps which reproduce the problem** in as many details as possible. 34 | - **Provide specific examples** to demonstrate the steps. 35 | - **Describe the behavior you observed** after following the steps and point out what exactly is the problem with that behavior. 36 | - **Explain which behavior you expected to see** instead and why. 37 | - **Include screenshots** which might help to illustrate the problem. 38 | - **If the problem is related to performance or memory**, include a [profile log](https://docs.python.org/3/library/profile.html) with your report. 39 | 40 | ### Suggesting enhancements 41 | 42 | This section guides you through submitting an enhancement suggestion for refry, including completely new features and minor improvements to existing functionalities. 43 | 44 | #### How do I submit a (good) enhancement suggestion? 45 | 46 | - **Use a clear and descriptive title** for the issue to identify the suggestion. 47 | - **Provide a step-by-step description of the suggested enhancement** in as many details as possible. 48 | - **Provide specific examples to demonstrate** the steps. 49 | - **Describe the current behavior and explain which behavior you expected to see** instead and why. 50 | - **Include screenshots or code snippets** which might help to illustrate the suggestion. 51 | 52 | ### Pull Requests 53 | 54 | The process described here will help you to get your contributions accepted more easily. 55 | 56 | #### Before Submitting a Pull Request 57 | 58 | - Ensure any install or build dependencies are removed before the end of the layer when doing a build. 59 | - Update the [README.md](README.md) with details of changes to the interface, this includes new environment variables, exposed ports, useful file locations and container parameters. 60 | - Ensure that your code conforms to our existing code conventions and test coverage. 61 | - Ensure your feature or fix passes all tests. 62 | - Make sure your commit messages are clear and appropriate. 63 | 64 | #### Submitting a Pull Request 65 | 66 | 1. **Fork the repository.** 67 | 2. **Create a new branch** from the `main` branch. 68 | 3. **Commit your changes** to your branch. 69 | 4. **Push your changes** to your fork. 70 | 5. **Submit a pull request** from your fork to the `main` branch of the original repository. 71 | 72 | ## Style Guides 73 | 74 | ### Git Commit Messages 75 | 76 | - Follow [Gitflow](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow) best practices: 77 | - Use `feature/`, `bugfix/`, `docs/`, `release/`, `hotfix/`, and `support/` prefixes for branch names. 78 | - Regularly rebase your feature branch with the `main` branch to keep up to date. 79 | - Merge your feature branch into `main` once it is complete. 80 | - Use the present tense ("Add feature" not "Added feature"). 81 | - Use the imperative mood ("Move cursor to..." not "Moves cursor to..."). 82 | - Limit the first line to 72 characters or less. 83 | - Reference issues and pull requests liberally after the first line. 84 | - Optionally consider starting the commit message with an applicable emoji: 85 | - :bug: `:bug:` for a bug fix. 86 | - :sparkles: `:sparkles:` for a new feature. 87 | - :memo: `:memo:` for documentation. 88 | - :rocket: `:rocket:` for performance improvements. 89 | - :lipstick: `:lipstick:` for UI improvements. 90 | 91 | ### Python Style Guide 92 | 93 | - Follow [PEP 8](https://www.python.org/dev/peps/pep-0008/). 94 | - Use [Black](https://github.com/psf/black) for code formatting. 95 | - Write [docstrings](https://www.python.org/dev/peps/pep-0257/). 96 | - Use type hints where possible. 97 | - Follow [Ruff](https://github.com/charliermarsh/ruff) rules for linting: 98 | - Install Ruff with `pip install ruff` in your development environment or with `rye` as [documented](README.md#development-setup). 99 | - Run Ruff with `ruff check .` to check for linting issues. 100 | - Use `ruff --fix` to automatically fix issues where possible. 101 | 102 | --- 103 | 104 | Thank you for considering contributing to refry! 105 | 106 | We look forward to your contributions. 107 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | Github. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # refry 2 | 3 | > You know what's better than fries? Fries cooked twice. 4 | > 5 | > What's even better than twice cooked fries is triple cooked fries! 6 | 7 |  8 | 9 | Refry is a modern, maintained, typed easy-to-use retry decorator. 10 | 11 | --- 12 | 13 | ## Installation 14 | 15 | ```bash 16 | pip install refry 17 | ``` 18 | 19 | --- 20 | 21 | ## Usage 22 | 23 | ### Basic usage 24 | 25 | ```python 26 | import refry 27 | 28 | @refry.retry 29 | def function_with_problem(): 30 | raise Exception('Something bad happens here') 31 | 32 | 33 | # This will be attempted 5 times, each time with a delay increasing 34 | # by 5 seconds. 35 | function_with_problem() 36 | ``` 37 | 38 | ### Retry with custom Exception 39 | 40 | ```python 41 | @refry.retry(rate_limit_exception=ZeroDivisionError) 42 | def function_with_bad_division(): 43 | 1 / 0 44 | 45 | function_with_bad_division() 46 | ``` 47 | 48 | ### Advanced usage: custom backoff and retry count 49 | 50 | ```python 51 | @refry.retry(backoff_increment=10, retries=3) 52 | def function_with_custom_backoff(): 53 | raise Exception('Something bad happens here') 54 | 55 | # This will be attempted 3 times, with a delay increasing by 10 seconds each time. 56 | function_with_custom_backoff() 57 | ``` 58 | 59 | ### Asynchronous function support 60 | 61 | ```python 62 | import asyncio 63 | import refry 64 | 65 | @refry.retry 66 | async def async_function_with_problem(): 67 | raise Exception('Something bad happens here') 68 | 69 | # This will be attempted 5 times, each time with a delay increasing by 5 seconds. 70 | await async_function_with_problem() 71 | ``` 72 | 73 | ### Retry with custom logic 74 | 75 | ```python 76 | import refry 77 | 78 | def custom_logic(exception): 79 | # Custom logic to determine if a retry should occur 80 | return isinstance(exception, ZeroDivisionError) 81 | 82 | @refry.retry(rate_limit_exception=ZeroDivisionError, retries=3) 83 | def function_with_custom_logic(): 84 | 1 / 0 85 | 86 | function_with_custom_logic() 87 | ``` 88 | 89 | ### Retry Decorator with Backoff and Jitter 90 | 91 | The `@retry` decorator allows you to automatically retry a function if it raises specific exceptions. 92 | It supports **different backoff strategies** (sequential, logarithmic, exponential) and **optional jitter** to handle the [thundering herd problem](http://www.catb.org/jargon/html/T/thundering-herd-problem.html). 93 | 94 | 95 | ```python 96 | import logging 97 | 98 | import refry 99 | 100 | @refry.retry(rate_limit_exception=Exception, backoff_increment=2, retries=5, backoff_type="sequential", jitter=True, log_level=logging.DEBUG) 101 | def function_with_problem(): 102 | raise Exception('Something bad happens here') 103 | 104 | # Call the function to see the retry mechanism in action 105 | function_with_problem() 106 | ``` 107 | 108 | 109 | ### Arguments for `refry.retry()` 110 | 111 | * `rate_limit_exception`: Types of exception to trigger a retry. Default value is [`Exception`]. 112 | * `backoff_increment`: The delay increment in seconds between retries. Default value is `5` seconds. 113 | * `retries`: The number of retry attempts. Default value is `5`. 114 | * `backoff_type`: The backoff strategy to use. Can be one of `"sequential"`, `"logarithmic"`, or `"exponential"`. Defaults to `"sequential"`. 115 | * `jitter`: If `True`, adds a random amount of time to each backoff interval to prevent thundering herd problem. Defaults to `False`. 116 | * `log_level`: The logging level to be used for messages generated by the decorator, allowing control over the logs' verbosity. Can be set to standard Python logging levels, such as `logging.DEBUG`, `logging.INFO`, `logging.WARNING`, etc. Defaults to `logging.INFO`. 117 | 118 | --- 119 | 120 | ## Comparison with Previous Implementations 121 | 122 | Refry is designed to be a modern, maintained, and typed easy-to-use retry decorator. 123 | Here are some comparisons with other popular retry libraries: 124 | 125 | * **Tenacity**: [Tenacity](https://pypi.org/project/tenacity/) is a highly configurable retry library for Python. It provides extensive options for backoff strategies, custom retry conditions, and logging. However, Tenacity's flexibility can make it more complex to use for simple retry needs. 126 | * **Retrying**: [Retrying](https://pypi.org/project/retrying/) is another popular retry library that offers simplicity and ease of use. However, it is no longer actively maintained, which can be a concern for projects requiring long-term support. 127 | * **Built-in Solutions**: Frameworks like Django and Flask provide built-in retry mechanisms for certain operations (e.g., database retries). These are often tightly coupled with the framework and may not be suitable for general-purpose retry needs outside of the framework's context. 128 | 129 | --- 130 | 131 | ## Development Setup 132 | 133 | For development, this project uses [rye](https://rye.astral.sh/) to manage dependencies, environment, and publishing to pypi. 134 | 135 | Once you've [installed the tool](https://rye.astral.sh/guide/installation/), clone the project and follow these instructions. 136 | 137 | ```bash 138 | cd refry/ 139 | rye sync 140 | ``` 141 | 142 | The `rye sync` will set up the local `.venv` folder. 143 | 144 | Running tests: 145 | 146 | ```bash 147 | rye run tests 148 | ``` 149 | 150 | Formatting the code: 151 | 152 | ```bash 153 | rye run format 154 | ``` 155 | 156 | --- 157 | 158 | ## Contribution Guidelines 159 | 160 | ### Code of Conduct 161 | 162 | This project and everyone participating in it is governed by refry's [Code of Conduct](CODE_OF_CONDUCT.md). 163 | 164 | By participating, you are expected to uphold this code. Please report unacceptable behavior to [@pydanny](https://github.com/pydanny). 165 | 166 | ### How to contribute 167 | 168 | Thank you for considering contributing to refry! We welcome contributions from everyone. 169 | 170 | Please read our [CONTRIBUTING.md](CONTRIBUTING.md) for detailed guidelines on how to contribute to this project, including how to report bugs, suggest enhancements, and submit pull requests. 171 | 172 | 1. **Fork the repository.** 173 | 2. **Create a new branch** from the `main` branch. 174 | 3. **Commit your changes** to your branch. 175 | 4. **Push your changes** to your fork. 176 | 5. **Submit a pull request** from your fork to the `main` branch of the original repository. 177 | 178 | --- 179 | 180 | ## Contributors 181 | 182 |
|
185 |
186 | 188 | Daniel Roy Greenfeld 189 | 190 | |
191 |
192 |
193 | 195 | Ricardo Vitorino 196 | 197 | |