From c1d508c37efaa6bd0ee1cf29907d03868196e979 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Ram=C3=ADrez=20Mondrag=C3=B3n?= Date: Tue, 14 Jul 2026 21:22:18 -0600 Subject: [PATCH] chore: Check types with `ty` and update some type annotations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Edgar Ramírez Mondragón --- CHANGELOG.md | 2 ++ backoff/_common.py | 2 +- backoff/_typing.py | 4 ++-- backoff/_wait_gen.py | 14 +++++++------- pyproject.toml | 10 ++++++++++ tests/test_backoff.py | 15 ++++++++------- tests/test_backoff_async.py | 17 +++++++++-------- tests/test_wait_gen.py | 2 +- 8 files changed, 40 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd06ddc..1e0c1bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,8 @@ - Apply the Ruff `FA` rules [#104](https://github.com/python-backoff/backoff/pull/104) (from [@edgarrmondragon](https://github.com/edgarrmondragon)) +- Check types with [`ty`](https://docs.astral.sh/ty/) and update some type annotations [#179](https://github.com/python-backoff/backoff/pull/179) + ## [v2.3.1] - 2025-12-18 ### Fixed diff --git a/backoff/_common.py b/backoff/_common.py index fd6316b..809d0fd 100644 --- a/backoff/_common.py +++ b/backoff/_common.py @@ -70,7 +70,7 @@ def _config_handlers( assert log_level is not None, "Log level is not specified" # bind the specified logger to the default log handler log_handler = functools.partial( - default_handler, + default_handler, # ty:ignore[invalid-argument-type] logger=logger, log_level=log_level, ) diff --git a/backoff/_typing.py b/backoff/_typing.py index 5485604..50fd39f 100644 --- a/backoff/_typing.py +++ b/backoff/_typing.py @@ -40,5 +40,5 @@ class Details(_Details, total=False): _MaybeCallable = Union[T, Callable[[], T]] _MaybeLogger = Union[str, logging.Logger, logging.LoggerAdapter, None] _MaybeSequence = Union[T, Sequence[T]] -_Predicate = Callable[[T], bool] -_WaitGenerator = Callable[..., Generator[float, None, None]] +_Predicate = Union[Callable[[T], bool], Callable[[T], Coroutine[T, None, bool]]] +_WaitGenerator = Callable[..., Generator[Union[float, None], None, None]] diff --git a/backoff/_wait_gen.py b/backoff/_wait_gen.py index ec904dd..7a88470 100644 --- a/backoff/_wait_gen.py +++ b/backoff/_wait_gen.py @@ -12,7 +12,7 @@ def expo( base: float = 2, factor: float = 1, max_value: float | None = None, -) -> Generator[float, Any, None]: +) -> Generator[float | None, Any, None]: """Generator for exponential decay. Args: @@ -38,7 +38,7 @@ def decay( initial_value: float = 1, decay_factor: float = 1, min_value: float | None = None, -) -> Generator[float, Any, None]: +) -> Generator[float | None, Any, None]: """Generator for exponential decay[1]: Args: @@ -62,7 +62,7 @@ def decay( yield min_value -def fibo(max_value: int | None = None) -> Generator[int, None, None]: +def fibo(max_value: int | None = None) -> Generator[int | None, None, None]: """Generator for fibonaccial decay. Args: @@ -85,7 +85,7 @@ def fibo(max_value: int | None = None) -> Generator[int, None, None]: def constant( interval: int | Iterable[float] = 1, -) -> Generator[float, None, None]: +) -> Generator[int | float | None, None, None]: """Generator for constant intervals. Args: @@ -97,16 +97,16 @@ def constant( try: itr = iter(interval) # type: ignore except TypeError: - itr = itertools.repeat(interval) # type: ignore + itr = itertools.repeat(interval) # type: ignore[arg-type] for val in itr: - yield val + yield val # ty:ignore[invalid-yield] def runtime( *, value: Callable[[Any], float], -) -> Generator[float, None, None]: +) -> Generator[float | None, None, None]: """Generator that is based on parsing the return value or thrown exception of the decorated method diff --git a/pyproject.toml b/pyproject.toml index 8a5ad83..3b75104 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -62,6 +62,7 @@ test = [ typing = [ { include-group = "test" }, "mypy>=0.942", + "ty>=0.0.59", "types-requests>=2.27.20", ] @@ -134,6 +135,15 @@ commands = [ "tests", ], extend = true }, ], + [ + "ty", + "check", + { replace = "if", condition = "env.GITHUB_ACTIONS == 'true'", then = [ "--output-format=github" ], else = [], extend = true }, + { replace = "posargs", default = [ + "backoff", + "tests", + ], extend = true }, + ], ] [tool.tox.env.docs] diff --git a/tests/test_backoff.py b/tests/test_backoff.py index dba5900..ff2c35d 100644 --- a/tests/test_backoff.py +++ b/tests/test_backoff.py @@ -1,5 +1,4 @@ import logging -import random import re import sys import threading @@ -531,7 +530,6 @@ def emptiness(*args, **kwargs): # on_predicate should support 0-argument jitter function. def test_on_exception_success_0_arg_jitter(monkeypatch): monkeypatch.setattr("time.sleep", lambda x: None) - monkeypatch.setattr("random.random", lambda: 0) backoffs, giveups, successes = [], [], [] @@ -541,7 +539,7 @@ def test_on_exception_success_0_arg_jitter(monkeypatch): on_success=successes.append, on_backoff=backoffs.append, on_giveup=giveups.append, - jitter=random.random, + jitter=lambda: 0.0, # ty:ignore[invalid-argument-type] interval=0, ) @_save_target @@ -550,7 +548,9 @@ def succeeder(*args, **kwargs): if len(backoffs) < 2: raise ValueError("catch me") - with pytest.deprecated_call(): + with pytest.deprecated_call( + match="Nullary jitter function signature is deprecated", + ): succeeder(1, 2, 3, foo=1, bar=2) # we try 3 times, backing off twice before succeeding @@ -587,7 +587,6 @@ def succeeder(*args, **kwargs): # on_predicate should support 0-argument jitter function. def test_on_predicate_success_0_arg_jitter(monkeypatch): monkeypatch.setattr("time.sleep", lambda x: None) - monkeypatch.setattr("random.random", lambda: 0) backoffs, giveups, successes = [], [], [] @@ -596,7 +595,7 @@ def test_on_predicate_success_0_arg_jitter(monkeypatch): on_success=successes.append, on_backoff=backoffs.append, on_giveup=giveups.append, - jitter=random.random, + jitter=lambda: 0.0, # ty:ignore[invalid-argument-type] interval=0, ) @_save_target @@ -604,7 +603,9 @@ def success(*args, **kwargs): # succeed after we've backed off twice return len(backoffs) == 2 - with pytest.deprecated_call(): + with pytest.deprecated_call( + match="Nullary jitter function signature is deprecated", + ): success(1, 2, 3, foo=1, bar=2) # we try 3 times, backing off twice before succeeding diff --git a/tests/test_backoff_async.py b/tests/test_backoff_async.py index 77504b3..fa56734 100644 --- a/tests/test_backoff_async.py +++ b/tests/test_backoff_async.py @@ -1,5 +1,4 @@ import asyncio # Python 3.5 code and syntax is allowed in this file -import random import pytest @@ -347,7 +346,7 @@ async def foo_bar_baz(): async def test_on_exception_giveup_coro(monkeypatch): monkeypatch.setattr("asyncio.sleep", _await_none) - async def on_baz(e): + async def on_baz(e: Exception) -> bool: return str(e) == "baz" vals = ["baz", "bar", "foo"] @@ -518,7 +517,6 @@ async def falsey(): @pytest.mark.asyncio async def test_on_exception_success_0_arg_jitter(monkeypatch): monkeypatch.setattr("asyncio.sleep", _await_none) - monkeypatch.setattr("random.random", lambda: 0) log, log_success, log_backoff, log_giveup = _log_hdlrs() @@ -528,7 +526,7 @@ async def test_on_exception_success_0_arg_jitter(monkeypatch): on_success=log_success, on_backoff=log_backoff, on_giveup=log_giveup, - jitter=random.random, + jitter=lambda: 0.0, # ty:ignore[invalid-argument-type] interval=0, ) @_save_target @@ -537,7 +535,9 @@ async def succeeder(*args, **kwargs): if len(log["backoff"]) < 2: raise ValueError("catch me") - with pytest.deprecated_call(): + with pytest.deprecated_call( + match="Nullary jitter function signature is deprecated", + ): await succeeder(1, 2, 3, foo=1, bar=2) # we try 3 times, backing off twice before succeeding @@ -575,7 +575,6 @@ async def succeeder(*args, **kwargs): @pytest.mark.asyncio async def test_on_predicate_success_0_arg_jitter(monkeypatch): monkeypatch.setattr("asyncio.sleep", _await_none) - monkeypatch.setattr("random.random", lambda: 0) log, log_success, log_backoff, log_giveup = _log_hdlrs() @@ -584,7 +583,7 @@ async def test_on_predicate_success_0_arg_jitter(monkeypatch): on_success=log_success, on_backoff=log_backoff, on_giveup=log_giveup, - jitter=random.random, + jitter=lambda: 0.0, # ty:ignore[invalid-argument-type] interval=0, ) @_save_target @@ -592,7 +591,9 @@ async def success(*args, **kwargs): # succeed after we've backed off twice return len(log["backoff"]) == 2 - with pytest.deprecated_call(): + with pytest.deprecated_call( + match="Nullary jitter function signature is deprecated", + ): await success(1, 2, 3, foo=1, bar=2) # we try 3 times, backing off twice before succeeding diff --git a/tests/test_wait_gen.py b/tests/test_wait_gen.py index a18fe62..3e8d1b8 100644 --- a/tests/test_wait_gen.py +++ b/tests/test_wait_gen.py @@ -102,4 +102,4 @@ def test_runtime(): gen = backoff.runtime(value=lambda x: x) gen.send(None) for i in range(20): - assert i == gen.send(i) + assert i == gen.send(i) # ty:ignore[invalid-argument-type]