diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8762d03..e0b88b2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -30,6 +30,14 @@ repos: rev: v2025.12.8.5 hooks: - id: doccmd + name: Ruff format docs + alias: ruff-format-docs args: ["--language", "python", "--no-pad-file", "--no-pad-groups", "--command", "ruff format", "docs/"] additional_dependencies: - ruff==0.14.8 + - id: doccmd + name: Ruff check fix docs + alias: ruff-check-fix-docs + args: ["--language", "python", "--no-pad-file", "--no-pad-groups", "--command", "ruff check --fix", "docs/"] + additional_dependencies: + - ruff==0.14.8 diff --git a/CHANGELOG.md b/CHANGELOG.md index 503409e..cdd13b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ ### Internal - Use Ruff to give the codebase a consistent format [#66](https://github.com/python-backoff/backoff/pull/66) (from [@edgarrmondragon](https://github.com/edgarrmondragon)) +- Enable the `UP` (pyupgrade) Ruff rules and use Python 3.8+ code, including in docs [#73](https://github.com/python-backoff/backoff/pull/73) (from [@edgarrmondragon](https://github.com/edgarrmondragon)) ## [v2.3.0] - 2025-11-28 diff --git a/README.rst b/README.rst index 984b819..3cc54cf 100644 --- a/README.rst +++ b/README.rst @@ -374,7 +374,7 @@ asynchronous HTTP client/server library. max_time=60, ) async def get_url(url): - async with aiohttp.ClientSession(raise_for_status=True) as session: + async with aiohttp.ClientSession(raise_for_status=True) as session: # noqa: SIM117 async with session.get(url) as response: return await response.text() diff --git a/backoff/__init__.py b/backoff/__init__.py index 4d2bb4f..96bc010 100644 --- a/backoff/__init__.py +++ b/backoff/__init__.py @@ -1,4 +1,3 @@ -# coding:utf-8 """ Function decoration for backoff and retry diff --git a/backoff/_async.py b/backoff/_async.py index 89148d0..322c097 100644 --- a/backoff/_async.py +++ b/backoff/_async.py @@ -1,4 +1,3 @@ -# coding:utf-8 import asyncio import functools import inspect diff --git a/backoff/_common.py b/backoff/_common.py index 69fb847..30476e4 100644 --- a/backoff/_common.py +++ b/backoff/_common.py @@ -1,5 +1,3 @@ -# coding:utf-8 - import functools import logging import sys diff --git a/backoff/_decorator.py b/backoff/_decorator.py index e068a58..5c3d69a 100644 --- a/backoff/_decorator.py +++ b/backoff/_decorator.py @@ -1,4 +1,3 @@ -# coding:utf-8 import inspect import logging import operator diff --git a/backoff/_jitter.py b/backoff/_jitter.py index be7e389..758d2a9 100644 --- a/backoff/_jitter.py +++ b/backoff/_jitter.py @@ -1,5 +1,3 @@ -# coding:utf-8 - import random diff --git a/backoff/_sync.py b/backoff/_sync.py index f7058da..7cafae2 100644 --- a/backoff/_sync.py +++ b/backoff/_sync.py @@ -1,4 +1,3 @@ -# coding:utf-8 import functools import time diff --git a/backoff/_typing.py b/backoff/_typing.py index 0e30e37..c2c553b 100644 --- a/backoff/_typing.py +++ b/backoff/_typing.py @@ -1,6 +1,4 @@ -# coding:utf-8 import logging -import sys from types import FunctionType from typing import ( Any, @@ -10,22 +8,11 @@ Generator, Sequence, Tuple, + TypedDict, TypeVar, Union, ) -if sys.version_info >= (3, 8): # pragma: no cover - from typing import TypedDict -else: # pragma: no cover - # use typing_extensions if installed but don't require it - try: - from typing_extensions import TypedDict - except ImportError: - - class TypedDict(dict): - def __init_subclass__(cls, **kwargs: Any) -> None: - return super().__init_subclass__() - class _Details(TypedDict): target: FunctionType diff --git a/backoff/_wait_gen.py b/backoff/_wait_gen.py index 14fbae3..3d028c1 100644 --- a/backoff/_wait_gen.py +++ b/backoff/_wait_gen.py @@ -1,5 +1,3 @@ -# coding:utf-8 - import itertools import math from typing import Any, Callable, Generator, Iterable, Optional, Union diff --git a/backoff/types.py b/backoff/types.py index 7ca670f..4fb197d 100644 --- a/backoff/types.py +++ b/backoff/types.py @@ -1,4 +1,3 @@ -# coding:utf-8 from ._typing import Details __all__ = [ diff --git a/docs/advanced/custom-strategies.md b/docs/advanced/custom-strategies.md index 9863a3f..0deb2b9 100644 --- a/docs/advanced/custom-strategies.md +++ b/docs/advanced/custom-strategies.md @@ -9,6 +9,7 @@ A wait generator is a function that yields wait times in seconds: ```python def my_wait_gen(): """Yields: 1, 2, 3, 4, 5, 5, 5, ...""" + # Note: Using `yield from` can be unsafe in backoff wait generators. for i in range(1, 6): yield i while True: diff --git a/docs/examples.md b/docs/examples.md index 97ecaf6..72ba5fb 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -121,9 +121,8 @@ import backoff max_time=60, ) async def fetch_async(url): - async with aiohttp.ClientSession() as session: - async with session.get(url) as response: - return await response.json() + async with aiohttp.ClientSession() as session, session.get(url) as response: + return await response.json() ``` ### Async Database Operations diff --git a/docs/faq.md b/docs/faq.md index d0d6bb3..48ebf43 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -188,9 +188,8 @@ Yes! Just decorate async functions: ```python @backoff.on_exception(backoff.expo, aiohttp.ClientError) async def fetch_data(url): - async with aiohttp.ClientSession() as session: - async with session.get(url) as response: - return await response.json() + async with aiohttp.ClientSession() as session, session.get(url) as response: + return await response.json() ``` ### Can event handlers be async? diff --git a/docs/getting-started.md b/docs/getting-started.md index 5203fe6..62a5722 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -188,9 +188,8 @@ import aiohttp max_time=60, ) async def get_url(url): - async with aiohttp.ClientSession() as session: - async with session.get(url) as response: - return await response.text() + async with aiohttp.ClientSession() as session, session.get(url) as response: + return await response.text() ``` ## Next Steps diff --git a/docs/user-guide/async.md b/docs/user-guide/async.md index bf931bd..db1f564 100644 --- a/docs/user-guide/async.md +++ b/docs/user-guide/async.md @@ -13,9 +13,8 @@ import aiohttp @backoff.on_exception(backoff.expo, aiohttp.ClientError) async def fetch_data(url): - async with aiohttp.ClientSession() as session: - async with session.get(url) as response: - return await response.json() + async with aiohttp.ClientSession() as session, session.get(url) as response: + return await response.json() ``` ## Async Event Handlers @@ -47,7 +46,7 @@ async def async_operation(): max_time=60, ) async def get_url(url): - async with aiohttp.ClientSession(raise_for_status=True) as session: + async with aiohttp.ClientSession(raise_for_status=True) as session: # noqa: SIM117 async with session.get(url) as response: return await response.text() ``` @@ -100,7 +99,7 @@ async def fetch_all(urls): max_time=300, ) async def poll_job_status(job_id): - async with aiohttp.ClientSession() as session: + async with aiohttp.ClientSession() as session: # noqa: SIM117 async with session.get(f"/api/jobs/{job_id}") as response: return await response.json() ``` @@ -169,7 +168,7 @@ async def log_async_retry(details): on_backoff=log_async_retry, ) async def robust_fetch(url, timeout=10): - async with aiohttp.ClientSession() as session: + async with aiohttp.ClientSession() as session: # noqa: SIM117 async with session.get(url, timeout=timeout) as response: response.raise_for_status() return await response.json() diff --git a/docs/user-guide/configuration.md b/docs/user-guide/configuration.md index 8bb23b0..234bd1a 100644 --- a/docs/user-guide/configuration.md +++ b/docs/user-guide/configuration.md @@ -323,7 +323,7 @@ def should_retry(result): return True if not result.get("ready"): return True - if result.get("status") == "processing": + if result.get("status") == "processing": # noqa: SIM103 return True return False diff --git a/docs/user-guide/event-handlers.md b/docs/user-guide/event-handlers.md index 425698e..24a2b8b 100644 --- a/docs/user-guide/event-handlers.md +++ b/docs/user-guide/event-handlers.md @@ -328,9 +328,8 @@ def conditional_alert(details): send_alert(f"High retry count: {details['tries']}") # Only log errors, not warnings - if details.get("exception"): - if isinstance(details["exception"], CriticalError): - logger.error("Critical error during retry") + if details.get("exception") and isinstance(details["exception"], CriticalError): + logger.error("Critical error during retry") @backoff.on_exception( diff --git a/pyproject.toml b/pyproject.toml index ec6cedc..0b8817b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -174,6 +174,18 @@ docstring-code-line-length = 20 [tool.ruff.lint] extend-select = [ "SIM", # flake8-simplify + "UP", # pyupgrade +] +ignore = [ + # converting to a `yield from` expression is not safe because this library sends values via `send` + # https://docs.astral.sh/ruff/rules/yield-in-for-loop/#fix-safety + "UP028", +] + +[tool.ruff.lint.per-file-ignores] +"**/doccmd_*.py" = [ + "F811", # redefinition of unused + "F821", # undefined name ] [tool.coverage.report] diff --git a/tests/common.py b/tests/common.py index d9a1a77..542663f 100644 --- a/tests/common.py +++ b/tests/common.py @@ -1,4 +1,3 @@ -# coding:utf-8 import collections import functools diff --git a/tests/test_backoff.py b/tests/test_backoff.py index 6d0aaaa..2db3d05 100644 --- a/tests/test_backoff.py +++ b/tests/test_backoff.py @@ -1,4 +1,3 @@ -# coding:utf-8 import itertools import logging import random diff --git a/tests/test_backoff_async.py b/tests/test_backoff_async.py index 2199e97..f2aed99 100644 --- a/tests/test_backoff_async.py +++ b/tests/test_backoff_async.py @@ -1,5 +1,3 @@ -# coding:utf-8 - import asyncio # Python 3.5 code and syntax is allowed in this file import random diff --git a/tests/test_jitter.py b/tests/test_jitter.py index 041b9fb..8193d0d 100644 --- a/tests/test_jitter.py +++ b/tests/test_jitter.py @@ -1,4 +1,3 @@ -# coding:utf-8 import backoff diff --git a/tests/test_types.py b/tests/test_types.py index 1e75e85..5e40da0 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -1,5 +1,3 @@ -# coding:utf-8 - from backoff.types import Details diff --git a/tests/test_wait_gen.py b/tests/test_wait_gen.py index 94f4b38..2a7c4f4 100644 --- a/tests/test_wait_gen.py +++ b/tests/test_wait_gen.py @@ -1,4 +1,3 @@ -# coding:utf-8 import backoff import math