diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index d06aeec..4bd620f 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -27,14 +27,12 @@ jobs: tox: py310 - python: "3.11" tox: py311 - - python: "3.11" + - python: "3.12" + tox: py312 + - python: "3.12" tox: pep8 - - python: "3.11" - tox: black-ci - python: "3.11" tox: mypy - - python: "3.12" - tox: py312 steps: - name: Checkout 🛎️ uses: actions/checkout@v4.0.0 diff --git a/.mergify.yml b/.mergify.yml index 003f217..a9d99e9 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -6,9 +6,7 @@ queue_rules: - "check-success=test (3.10, py310)" - "check-success=test (3.11, py311)" - "check-success=test (3.12, py312)" - - "check-success=test (3.11, black-ci)" - - "check-success=test (3.11, pep8)" - - "check-success=test (3.11, mypy)" + - "check-success=test (3.12, pep8)" pull_request_rules: - name: warn on no changelog diff --git a/pyproject.toml b/pyproject.toml index a293a6a..f4bda94 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,10 +8,10 @@ requires = [ ] build-backend="setuptools.build_meta" -[tool.black] -line-length = 120 -safe = true -target-version = ["py38", "py39", "py310", "py311", "py312"] +[tool.ruff] +line-length = 88 +indent-width = 4 +target-version = "py38" [tool.mypy] strict = true diff --git a/tenacity/__init__.py b/tenacity/__init__.py index bd60556..11e2faa 100644 --- a/tenacity/__init__.py +++ b/tenacity/__init__.py @@ -125,7 +125,9 @@ class BaseAction: NAME: t.Optional[str] = None def __repr__(self) -> str: - state_str = ", ".join(f"{field}={getattr(self, field)!r}" for field in self.REPR_FIELDS) + state_str = ", ".join( + f"{field}={getattr(self, field)!r}" for field in self.REPR_FIELDS + ) return f"{self.__class__.__name__}({state_str})" def __str__(self) -> str: @@ -221,10 +223,14 @@ def copy( retry: t.Union[retry_base, object] = _unset, before: t.Union[t.Callable[["RetryCallState"], None], object] = _unset, after: t.Union[t.Callable[["RetryCallState"], None], object] = _unset, - before_sleep: t.Union[t.Optional[t.Callable[["RetryCallState"], None]], object] = _unset, + before_sleep: t.Union[ + t.Optional[t.Callable[["RetryCallState"], None]], object + ] = _unset, reraise: t.Union[bool, object] = _unset, retry_error_cls: t.Union[t.Type[RetryError], object] = _unset, - retry_error_callback: t.Union[t.Optional[t.Callable[["RetryCallState"], t.Any]], object] = _unset, + retry_error_callback: t.Union[ + t.Optional[t.Callable[["RetryCallState"], t.Any]], object + ] = _unset, ) -> "BaseRetrying": """Copy this object with some parameters changed if needed.""" return self.__class__( @@ -237,7 +243,9 @@ def copy( before_sleep=_first_set(before_sleep, self.before_sleep), reraise=_first_set(reraise, self.reraise), retry_error_cls=_first_set(retry_error_cls, self.retry_error_cls), - retry_error_callback=_first_set(retry_error_callback, self.retry_error_callback), + retry_error_callback=_first_set( + retry_error_callback, self.retry_error_callback + ), ) def __repr__(self) -> str: @@ -285,7 +293,9 @@ def wraps(self, f: WrappedFn) -> WrappedFn: :param f: A function to wraps for retrying. """ - @functools.wraps(f, functools.WRAPPER_ASSIGNMENTS + ("__defaults__", "__kwdefaults__")) + @functools.wraps( + f, functools.WRAPPER_ASSIGNMENTS + ("__defaults__", "__kwdefaults__") + ) def wrapped_f(*args: t.Any, **kw: t.Any) -> t.Any: return self(f, *args, **kw) @@ -414,7 +424,9 @@ def failed(self) -> bool: return self.exception() is not None @classmethod - def construct(cls, attempt_number: int, value: t.Any, has_exception: bool) -> "Future": + def construct( + cls, attempt_number: int, value: t.Any, has_exception: bool + ) -> "Future": """Construct a new Future object.""" fut = cls(attempt_number) if has_exception: @@ -477,7 +489,10 @@ def set_result(self, val: t.Any) -> None: self.outcome, self.outcome_timestamp = fut, ts def set_exception( - self, exc_info: t.Tuple[t.Type[BaseException], BaseException, "types.TracebackType| None"] + self, + exc_info: t.Tuple[ + t.Type[BaseException], BaseException, "types.TracebackType| None" + ], ) -> None: ts = time.monotonic() fut = Future(self.attempt_number) @@ -539,7 +554,11 @@ def wrap(f: WrappedFn) -> WrappedFn: r: "BaseRetrying" if iscoroutinefunction(f): r = AsyncRetrying(*dargs, **dkw) - elif tornado and hasattr(tornado.gen, "is_coroutine_function") and tornado.gen.is_coroutine_function(f): + elif ( + tornado + and hasattr(tornado.gen, "is_coroutine_function") + and tornado.gen.is_coroutine_function(f) + ): r = TornadoRetrying(*dargs, **dkw) else: r = Retrying(*dargs, **dkw) diff --git a/tenacity/_asyncio.py b/tenacity/_asyncio.py index d901cbd..16aec62 100644 --- a/tenacity/_asyncio.py +++ b/tenacity/_asyncio.py @@ -33,7 +33,9 @@ class AsyncRetrying(BaseRetrying): sleep: t.Callable[[float], t.Awaitable[t.Any]] - def __init__(self, sleep: t.Callable[[float], t.Awaitable[t.Any]] = sleep, **kwargs: t.Any) -> None: + def __init__( + self, sleep: t.Callable[[float], t.Awaitable[t.Any]] = sleep, **kwargs: t.Any + ) -> None: super().__init__(**kwargs) self.sleep = sleep @@ -83,7 +85,9 @@ def wraps(self, fn: WrappedFn) -> WrappedFn: fn = super().wraps(fn) # Ensure wrapper is recognized as a coroutine function. - @functools.wraps(fn, functools.WRAPPER_ASSIGNMENTS + ("__defaults__", "__kwdefaults__")) + @functools.wraps( + fn, functools.WRAPPER_ASSIGNMENTS + ("__defaults__", "__kwdefaults__") + ) async def async_wrapped(*args: t.Any, **kwargs: t.Any) -> t.Any: return await fn(*args, **kwargs) diff --git a/tenacity/_utils.py b/tenacity/_utils.py index f14ff32..67ee0de 100644 --- a/tenacity/_utils.py +++ b/tenacity/_utils.py @@ -73,4 +73,6 @@ def get_callback_name(cb: typing.Callable[..., typing.Any]) -> str: def to_seconds(time_unit: time_unit_type) -> float: - return float(time_unit.total_seconds() if isinstance(time_unit, timedelta) else time_unit) + return float( + time_unit.total_seconds() if isinstance(time_unit, timedelta) else time_unit + ) diff --git a/tenacity/before.py b/tenacity/before.py index 9284f7a..366235a 100644 --- a/tenacity/before.py +++ b/tenacity/before.py @@ -28,7 +28,9 @@ def before_nothing(retry_state: "RetryCallState") -> None: """Before call strategy that does nothing.""" -def before_log(logger: "logging.Logger", log_level: int) -> typing.Callable[["RetryCallState"], None]: +def before_log( + logger: "logging.Logger", log_level: int +) -> typing.Callable[["RetryCallState"], None]: """Before call strategy that logs to some logger the attempt.""" def log_it(retry_state: "RetryCallState") -> None: diff --git a/tenacity/before_sleep.py b/tenacity/before_sleep.py index 279a21e..d04edcf 100644 --- a/tenacity/before_sleep.py +++ b/tenacity/before_sleep.py @@ -64,7 +64,8 @@ def log_it(retry_state: "RetryCallState") -> None: logger.log( log_level, - f"Retrying {fn_name} " f"in {retry_state.next_action.sleep} seconds as it {verb} {value}.", + f"Retrying {fn_name} " + f"in {retry_state.next_action.sleep} seconds as it {verb} {value}.", exc_info=local_exc_info, ) diff --git a/tenacity/retry.py b/tenacity/retry.py index 765b6fe..c5e55a6 100644 --- a/tenacity/retry.py +++ b/tenacity/retry.py @@ -204,7 +204,9 @@ def __init__( match: typing.Optional[str] = None, ) -> None: if message and match: - raise TypeError(f"{self.__class__.__name__}() takes either 'message' or 'match', not both") + raise TypeError( + f"{self.__class__.__name__}() takes either 'message' or 'match', not both" + ) # set predicate if message: @@ -221,7 +223,9 @@ def match_fnc(exception: BaseException) -> bool: predicate = match_fnc else: - raise TypeError(f"{self.__class__.__name__}() missing 1 required argument 'message' or 'match'") + raise TypeError( + f"{self.__class__.__name__}() missing 1 required argument 'message' or 'match'" + ) super().__init__(predicate) diff --git a/tenacity/stop.py b/tenacity/stop.py index d7eb6b9..5cda59a 100644 --- a/tenacity/stop.py +++ b/tenacity/stop.py @@ -124,4 +124,7 @@ def __init__(self, max_delay: _utils.time_unit_type) -> None: def __call__(self, retry_state: "RetryCallState") -> bool: if retry_state.seconds_since_start is None: raise RuntimeError("__call__() called but seconds_since_start is not set") - return retry_state.seconds_since_start + retry_state.upcoming_sleep >= self.max_delay + return ( + retry_state.seconds_since_start + retry_state.upcoming_sleep + >= self.max_delay + ) diff --git a/tenacity/tornadoweb.py b/tenacity/tornadoweb.py index fabf13a..44323e4 100644 --- a/tenacity/tornadoweb.py +++ b/tenacity/tornadoweb.py @@ -29,7 +29,11 @@ class TornadoRetrying(BaseRetrying): - def __init__(self, sleep: "typing.Callable[[float], Future[None]]" = gen.sleep, **kwargs: typing.Any) -> None: + def __init__( + self, + sleep: "typing.Callable[[float], Future[None]]" = gen.sleep, + **kwargs: typing.Any, + ) -> None: super().__init__(**kwargs) self.sleep = sleep diff --git a/tenacity/wait.py b/tenacity/wait.py index e1e2fe4..3addbb9 100644 --- a/tenacity/wait.py +++ b/tenacity/wait.py @@ -41,7 +41,9 @@ def __radd__(self, other: "wait_base") -> typing.Union["wait_combine", "wait_bas return self.__add__(other) -WaitBaseT = typing.Union[wait_base, typing.Callable[["RetryCallState"], typing.Union[float, int]]] +WaitBaseT = typing.Union[ + wait_base, typing.Callable[["RetryCallState"], typing.Union[float, int]] +] class wait_fixed(wait_base): @@ -64,12 +66,16 @@ def __init__(self) -> None: class wait_random(wait_base): """Wait strategy that waits a random amount of time between min/max.""" - def __init__(self, min: _utils.time_unit_type = 0, max: _utils.time_unit_type = 1) -> None: # noqa + def __init__( + self, min: _utils.time_unit_type = 0, max: _utils.time_unit_type = 1 + ) -> None: # noqa self.wait_random_min = _utils.to_seconds(min) self.wait_random_max = _utils.to_seconds(max) def __call__(self, retry_state: "RetryCallState") -> float: - return self.wait_random_min + (random.random() * (self.wait_random_max - self.wait_random_min)) + return self.wait_random_min + ( + random.random() * (self.wait_random_max - self.wait_random_min) + ) class wait_combine(wait_base): diff --git a/tests/test_after.py b/tests/test_after.py index 3211703..0cb4f71 100644 --- a/tests/test_after.py +++ b/tests/test_after.py @@ -11,7 +11,15 @@ class TestAfterLogFormat(unittest.TestCase): def setUp(self) -> None: - self.log_level = random.choice((logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, logging.CRITICAL)) + self.log_level = random.choice( + ( + logging.DEBUG, + logging.INFO, + logging.WARNING, + logging.ERROR, + logging.CRITICAL, + ) + ) self.previous_attempt_number = random.randint(1, 512) def test_01_default(self): @@ -22,10 +30,18 @@ def test_01_default(self): sec_format = "%0.3f" delay_since_first_attempt = 0.1 - retry_state = test_tenacity.make_retry_state(self.previous_attempt_number, delay_since_first_attempt) - fun = after_log(logger=logger, log_level=self.log_level) # use default sec_format + retry_state = test_tenacity.make_retry_state( + self.previous_attempt_number, delay_since_first_attempt + ) + fun = after_log( + logger=logger, log_level=self.log_level + ) # use default sec_format fun(retry_state) - fn_name = "" if retry_state.fn is None else _utils.get_callback_name(retry_state.fn) + fn_name = ( + "" + if retry_state.fn is None + else _utils.get_callback_name(retry_state.fn) + ) log.assert_called_once_with( self.log_level, f"Finished call to '{fn_name}' " @@ -41,10 +57,16 @@ def test_02_custom_sec_format(self): sec_format = "%.1f" delay_since_first_attempt = 0.1 - retry_state = test_tenacity.make_retry_state(self.previous_attempt_number, delay_since_first_attempt) + retry_state = test_tenacity.make_retry_state( + self.previous_attempt_number, delay_since_first_attempt + ) fun = after_log(logger=logger, log_level=self.log_level, sec_format=sec_format) fun(retry_state) - fn_name = "" if retry_state.fn is None else _utils.get_callback_name(retry_state.fn) + fn_name = ( + "" + if retry_state.fn is None + else _utils.get_callback_name(retry_state.fn) + ) log.assert_called_once_with( self.log_level, f"Finished call to '{fn_name}' " diff --git a/tests/test_asyncio.py b/tests/test_asyncio.py index 542f540..24cf6ed 100644 --- a/tests/test_asyncio.py +++ b/tests/test_asyncio.py @@ -96,12 +96,19 @@ async def function_with_defaults(a=1): async def function_with_kwdefaults(*, a=1): return a - retrying = AsyncRetrying(wait=tenacity.wait_fixed(0.01), stop=tenacity.stop_after_attempt(3)) + retrying = AsyncRetrying( + wait=tenacity.wait_fixed(0.01), stop=tenacity.stop_after_attempt(3) + ) wrapped_defaults_function = retrying.wraps(function_with_defaults) wrapped_kwdefaults_function = retrying.wraps(function_with_kwdefaults) - self.assertEqual(function_with_defaults.__defaults__, wrapped_defaults_function.__defaults__) - self.assertEqual(function_with_kwdefaults.__kwdefaults__, wrapped_kwdefaults_function.__kwdefaults__) + self.assertEqual( + function_with_defaults.__defaults__, wrapped_defaults_function.__defaults__ + ) + self.assertEqual( + function_with_kwdefaults.__kwdefaults__, + wrapped_kwdefaults_function.__kwdefaults__, + ) @asynctest async def test_attempt_number_is_correct_for_interleaved_coroutines(self): @@ -152,7 +159,9 @@ class CustomError(Exception): pass try: - async for attempt in tasyncio.AsyncRetrying(stop=stop_after_attempt(1), reraise=True): + async for attempt in tasyncio.AsyncRetrying( + stop=stop_after_attempt(1), reraise=True + ): with attempt: raise CustomError() except CustomError: @@ -164,7 +173,9 @@ class CustomError(Exception): async def test_sleeps(self): start = current_time_ms() try: - async for attempt in tasyncio.AsyncRetrying(stop=stop_after_attempt(1), wait=wait_fixed(1)): + async for attempt in tasyncio.AsyncRetrying( + stop=stop_after_attempt(1), wait=wait_fixed(1) + ): with attempt: raise Exception() except RetryError: diff --git a/tests/test_tenacity.py b/tests/test_tenacity.py index ac79201..e158fa6 100644 --- a/tests/test_tenacity.py +++ b/tests/test_tenacity.py @@ -51,12 +51,19 @@ def _set_delay_since_start(retry_state, delay): assert retry_state.seconds_since_start == delay -def make_retry_state(previous_attempt_number, delay_since_first_attempt, last_result=None, upcoming_sleep=0): +def make_retry_state( + previous_attempt_number, + delay_since_first_attempt, + last_result=None, + upcoming_sleep=0, +): """Construct RetryCallState for given attempt number & delay. Only used in testing and thus is extra careful about timestamp arithmetics. """ - required_parameter_unset = previous_attempt_number is _unset or delay_since_first_attempt is _unset + required_parameter_unset = ( + previous_attempt_number is _unset or delay_since_first_attempt is _unset + ) if required_parameter_unset: raise _make_unset_exception( "wait/stop", @@ -90,9 +97,15 @@ def test_callstate_repr(self): rs.idle_for = 1.1111111 assert repr(rs).endswith("attempt #1; slept for 1.11; last result: none yet>") rs = make_retry_state(2, 5) - assert repr(rs).endswith("attempt #2; slept for 0.0; last result: returned None>") - rs = make_retry_state(0, 0, last_result=tenacity.Future.construct(1, ValueError("aaa"), True)) - assert repr(rs).endswith("attempt #0; slept for 0.0; last result: failed (ValueError aaa)>") + assert repr(rs).endswith( + "attempt #2; slept for 0.0; last result: returned None>" + ) + rs = make_retry_state( + 0, 0, last_result=tenacity.Future.construct(1, ValueError("aaa"), True) + ) + assert repr(rs).endswith( + "attempt #0; slept for 0.0; last result: failed (ValueError aaa)>" + ) class TestStopConditions(unittest.TestCase): @@ -101,7 +114,9 @@ def test_never_stop(self): self.assertFalse(r.stop(make_retry_state(3, 6546))) def test_stop_any(self): - stop = tenacity.stop_any(tenacity.stop_after_delay(1), tenacity.stop_after_attempt(4)) + stop = tenacity.stop_any( + tenacity.stop_after_delay(1), tenacity.stop_after_attempt(4) + ) def s(*args): return stop(make_retry_state(*args)) @@ -114,7 +129,9 @@ def s(*args): self.assertTrue(s(4, 1.8)) def test_stop_all(self): - stop = tenacity.stop_all(tenacity.stop_after_delay(1), tenacity.stop_after_attempt(4)) + stop = tenacity.stop_all( + tenacity.stop_after_delay(1), tenacity.stop_after_attempt(4) + ) def s(*args): return stop(make_retry_state(*args)) @@ -170,7 +187,9 @@ def test_stop_before_delay(self): for delay in (1, datetime.timedelta(seconds=1)): with self.subTest(): r = Retrying(stop=tenacity.stop_before_delay(delay)) - self.assertFalse(r.stop(make_retry_state(2, 0.999, upcoming_sleep=0.0001))) + self.assertFalse( + r.stop(make_retry_state(2, 0.999, upcoming_sleep=0.0001)) + ) self.assertTrue(r.stop(make_retry_state(2, 1, upcoming_sleep=0.001))) self.assertTrue(r.stop(make_retry_state(2, 1, upcoming_sleep=1))) @@ -205,15 +224,23 @@ def test_fixed_sleep(self): self.assertEqual(1, r.wait(make_retry_state(12, 6546))) def test_incrementing_sleep(self): - for start, increment in ((500, 100), (datetime.timedelta(seconds=500), datetime.timedelta(seconds=100))): + for start, increment in ( + (500, 100), + (datetime.timedelta(seconds=500), datetime.timedelta(seconds=100)), + ): with self.subTest(): - r = Retrying(wait=tenacity.wait_incrementing(start=start, increment=increment)) + r = Retrying( + wait=tenacity.wait_incrementing(start=start, increment=increment) + ) self.assertEqual(500, r.wait(make_retry_state(1, 6546))) self.assertEqual(600, r.wait(make_retry_state(2, 6546))) self.assertEqual(700, r.wait(make_retry_state(3, 6546))) def test_random_sleep(self): - for min_, max_ in ((1, 20), (datetime.timedelta(seconds=1), datetime.timedelta(seconds=20))): + for min_, max_ in ( + (1, 20), + (datetime.timedelta(seconds=1), datetime.timedelta(seconds=20)), + ): with self.subTest(): r = Retrying(wait=tenacity.wait_random(min=min_, max=max_)) times = set() @@ -300,7 +327,10 @@ def test_exponential_with_min_wait_and_multiplier(self): self.assertEqual(r.wait(make_retry_state(20, 0)), 1048576) def test_exponential_with_min_wait_andmax__wait(self): - for min_, max_ in ((10, 100), (datetime.timedelta(seconds=10), datetime.timedelta(seconds=100))): + for min_, max_ in ( + (10, 100), + (datetime.timedelta(seconds=10), datetime.timedelta(seconds=100)), + ): with self.subTest(): r = Retrying(wait=tenacity.wait_exponential(min=min_, max=max_)) self.assertEqual(r.wait(make_retry_state(1, 0)), 10) @@ -327,7 +357,11 @@ def wait_func(retry_state): self.assertEqual(r.wait(make_retry_state(10, 100)), 1000) def test_wait_combine(self): - r = Retrying(wait=tenacity.wait_combine(tenacity.wait_random(0, 3), tenacity.wait_fixed(5))) + r = Retrying( + wait=tenacity.wait_combine( + tenacity.wait_random(0, 3), tenacity.wait_fixed(5) + ) + ) # Test it a few time since it's random for i in range(1000): w = r.wait(make_retry_state(1, 5)) @@ -343,7 +377,11 @@ def test_wait_double_sum(self): self.assertGreaterEqual(w, 5) def test_wait_triple_sum(self): - r = Retrying(wait=tenacity.wait_fixed(1) + tenacity.wait_random(0, 3) + tenacity.wait_fixed(5)) + r = Retrying( + wait=tenacity.wait_fixed(1) + + tenacity.wait_random(0, 3) + + tenacity.wait_fixed(5) + ) # Test it a few time since it's random for i in range(1000): w = r.wait(make_retry_state(1, 5)) @@ -495,7 +533,10 @@ def waitfunc(retry_state): retrying = Retrying( wait=waitfunc, - retry=(tenacity.retry_if_exception_type() | tenacity.retry_if_result(lambda result: result == 123)), + retry=( + tenacity.retry_if_exception_type() + | tenacity.retry_if_result(lambda result: result == 123) + ), ) def returnval(): @@ -579,7 +620,9 @@ def r(fut): self.assertFalse(r(tenacity.Future.construct(1, 1, True))) def test_retry_and(self): - retry = tenacity.retry_if_result(lambda x: x == 1) & tenacity.retry_if_result(lambda x: isinstance(x, int)) + retry = tenacity.retry_if_result(lambda x: x == 1) & tenacity.retry_if_result( + lambda x: isinstance(x, int) + ) def r(fut): retry_state = make_retry_state(1, 1.0, last_result=fut) @@ -591,7 +634,9 @@ def r(fut): self.assertFalse(r(tenacity.Future.construct(1, 1, True))) def test_retry_or(self): - retry = tenacity.retry_if_result(lambda x: x == "foo") | tenacity.retry_if_result(lambda x: isinstance(x, int)) + retry = tenacity.retry_if_result( + lambda x: x == "foo" + ) | tenacity.retry_if_result(lambda x: isinstance(x, int)) def r(fut): retry_state = make_retry_state(1, 1.0, last_result=fut) @@ -609,7 +654,9 @@ def _raise_try_again(self): def test_retry_try_again(self): self._attempts = 0 - Retrying(stop=tenacity.stop_after_attempt(5), retry=tenacity.retry_never)(self._raise_try_again) + Retrying(stop=tenacity.stop_after_attempt(5), retry=tenacity.retry_never)( + self._raise_try_again + ) self.assertEqual(3, self._attempts) def test_retry_try_again_forever(self): @@ -867,7 +914,9 @@ def _retryable_test_if_not_exception_type_io(thing): return thing.go() -@retry(stop=tenacity.stop_after_attempt(3), retry=tenacity.retry_if_exception_type(IOError)) +@retry( + stop=tenacity.stop_after_attempt(3), retry=tenacity.retry_if_exception_type(IOError) +) def _retryable_test_with_exception_type_io_attempt_limit(thing): return thing.go() @@ -892,28 +941,46 @@ def _retryable_test_with_unless_exception_type_no_input(thing): @retry( stop=tenacity.stop_after_attempt(5), - retry=tenacity.retry_if_exception_message(message=NoCustomErrorAfterCount.derived_message), + retry=tenacity.retry_if_exception_message( + message=NoCustomErrorAfterCount.derived_message + ), ) def _retryable_test_if_exception_message_message(thing): return thing.go() -@retry(retry=tenacity.retry_if_not_exception_message(message=NoCustomErrorAfterCount.derived_message)) +@retry( + retry=tenacity.retry_if_not_exception_message( + message=NoCustomErrorAfterCount.derived_message + ) +) def _retryable_test_if_not_exception_message_message(thing): return thing.go() -@retry(retry=tenacity.retry_if_exception_message(match=NoCustomErrorAfterCount.derived_message[:3] + ".*")) +@retry( + retry=tenacity.retry_if_exception_message( + match=NoCustomErrorAfterCount.derived_message[:3] + ".*" + ) +) def _retryable_test_if_exception_message_match(thing): return thing.go() -@retry(retry=tenacity.retry_if_not_exception_message(match=NoCustomErrorAfterCount.derived_message[:3] + ".*")) +@retry( + retry=tenacity.retry_if_not_exception_message( + match=NoCustomErrorAfterCount.derived_message[:3] + ".*" + ) +) def _retryable_test_if_not_exception_message_match(thing): return thing.go() -@retry(retry=tenacity.retry_if_not_exception_message(message=NameErrorUntilCount.derived_message)) +@retry( + retry=tenacity.retry_if_not_exception_message( + message=NameErrorUntilCount.derived_message + ) +) def _retryable_test_not_exception_message_delay(thing): return thing.go() @@ -977,7 +1044,9 @@ def test_retry_if_exception_of_type(self): self.assertTrue(isinstance(n, NameError)) print(n) - self.assertTrue(_retryable_test_with_exception_type_custom(NoCustomErrorAfterCount(5))) + self.assertTrue( + _retryable_test_with_exception_type_custom(NoCustomErrorAfterCount(5)) + ) try: _retryable_test_with_exception_type_custom(NoNameErrorAfterCount(5)) @@ -987,7 +1056,9 @@ def test_retry_if_exception_of_type(self): print(n) def test_retry_except_exception_of_type(self): - self.assertTrue(_retryable_test_if_not_exception_type_io(NoNameErrorAfterCount(5))) + self.assertTrue( + _retryable_test_if_not_exception_type_io(NoNameErrorAfterCount(5)) + ) try: _retryable_test_if_not_exception_type_io(NoIOErrorAfterCount(5)) @@ -998,7 +1069,9 @@ def test_retry_except_exception_of_type(self): def test_retry_until_exception_of_type_attempt_number(self): try: - self.assertTrue(_retryable_test_with_unless_exception_type_name(NameErrorUntilCount(5))) + self.assertTrue( + _retryable_test_with_unless_exception_type_name(NameErrorUntilCount(5)) + ) except NameError as e: s = _retryable_test_with_unless_exception_type_name.retry.statistics self.assertTrue(s["attempt_number"] == 6) @@ -1009,7 +1082,11 @@ def test_retry_until_exception_of_type_attempt_number(self): def test_retry_until_exception_of_type_no_type(self): try: # no input should catch all subclasses of Exception - self.assertTrue(_retryable_test_with_unless_exception_type_no_input(NameErrorUntilCount(5))) + self.assertTrue( + _retryable_test_with_unless_exception_type_no_input( + NameErrorUntilCount(5) + ) + ) except NameError as e: s = _retryable_test_with_unless_exception_type_no_input.retry.statistics self.assertTrue(s["attempt_number"] == 6) @@ -1020,7 +1097,9 @@ def test_retry_until_exception_of_type_no_type(self): def test_retry_until_exception_of_type_wrong_exception(self): try: # two iterations with IOError, one that returns True - _retryable_test_with_unless_exception_type_name_attempt_limit(IOErrorUntilCount(2)) + _retryable_test_with_unless_exception_type_name_attempt_limit( + IOErrorUntilCount(2) + ) self.fail("Expected RetryError") except RetryError as e: self.assertTrue(isinstance(e, RetryError)) @@ -1028,21 +1107,29 @@ def test_retry_until_exception_of_type_wrong_exception(self): def test_retry_if_exception_message(self): try: - self.assertTrue(_retryable_test_if_exception_message_message(NoCustomErrorAfterCount(3))) + self.assertTrue( + _retryable_test_if_exception_message_message(NoCustomErrorAfterCount(3)) + ) except CustomError: print(_retryable_test_if_exception_message_message.retry.statistics) self.fail("CustomError should've been retried from errormessage") def test_retry_if_not_exception_message(self): try: - self.assertTrue(_retryable_test_if_not_exception_message_message(NoCustomErrorAfterCount(2))) + self.assertTrue( + _retryable_test_if_not_exception_message_message( + NoCustomErrorAfterCount(2) + ) + ) except CustomError: s = _retryable_test_if_not_exception_message_message.retry.statistics self.assertTrue(s["attempt_number"] == 1) def test_retry_if_not_exception_message_delay(self): try: - self.assertTrue(_retryable_test_not_exception_message_delay(NameErrorUntilCount(3))) + self.assertTrue( + _retryable_test_not_exception_message_delay(NameErrorUntilCount(3)) + ) except NameError: s = _retryable_test_not_exception_message_delay.retry.statistics print(s["attempt_number"]) @@ -1050,19 +1137,27 @@ def test_retry_if_not_exception_message_delay(self): def test_retry_if_exception_message_match(self): try: - self.assertTrue(_retryable_test_if_exception_message_match(NoCustomErrorAfterCount(3))) + self.assertTrue( + _retryable_test_if_exception_message_match(NoCustomErrorAfterCount(3)) + ) except CustomError: self.fail("CustomError should've been retried from errormessage") def test_retry_if_not_exception_message_match(self): try: - self.assertTrue(_retryable_test_if_not_exception_message_message(NoCustomErrorAfterCount(2))) + self.assertTrue( + _retryable_test_if_not_exception_message_message( + NoCustomErrorAfterCount(2) + ) + ) except CustomError: s = _retryable_test_if_not_exception_message_message.retry.statistics self.assertTrue(s["attempt_number"] == 1) def test_retry_if_exception_cause_type(self): - self.assertTrue(_retryable_test_with_exception_cause_type(NoNameErrorCauseAfterCount(5))) + self.assertTrue( + _retryable_test_with_exception_cause_type(NoNameErrorCauseAfterCount(5)) + ) try: _retryable_test_with_exception_cause_type(NoIOErrorCauseAfterCount(5)) @@ -1077,12 +1172,19 @@ def function_with_defaults(a=1): def function_with_kwdefaults(*, a=1): return a - retrying = Retrying(wait=tenacity.wait_fixed(0.01), stop=tenacity.stop_after_attempt(3)) + retrying = Retrying( + wait=tenacity.wait_fixed(0.01), stop=tenacity.stop_after_attempt(3) + ) wrapped_defaults_function = retrying.wraps(function_with_defaults) wrapped_kwdefaults_function = retrying.wraps(function_with_kwdefaults) - self.assertEqual(function_with_defaults.__defaults__, wrapped_defaults_function.__defaults__) - self.assertEqual(function_with_kwdefaults.__kwdefaults__, wrapped_kwdefaults_function.__kwdefaults__) + self.assertEqual( + function_with_defaults.__defaults__, wrapped_defaults_function.__defaults__ + ) + self.assertEqual( + function_with_kwdefaults.__kwdefaults__, + wrapped_kwdefaults_function.__kwdefaults__, + ) def test_defaults(self): self.assertTrue(_retryable_default(NoNameErrorAfterCount(5))) @@ -1101,7 +1203,9 @@ class Hello: def __call__(self): return "Hello" - retrying = Retrying(wait=tenacity.wait_fixed(0.01), stop=tenacity.stop_after_attempt(3)) + retrying = Retrying( + wait=tenacity.wait_fixed(0.01), stop=tenacity.stop_after_attempt(3) + ) h = retrying.wraps(Hello()) self.assertEqual(h(), "Hello") @@ -1109,13 +1213,17 @@ def __call__(self): class TestRetryWith: def test_redefine_wait(self): start = current_time_ms() - result = _retryable_test_with_wait.retry_with(wait=tenacity.wait_fixed(0.1))(NoneReturnUntilAfterCount(5)) + result = _retryable_test_with_wait.retry_with(wait=tenacity.wait_fixed(0.1))( + NoneReturnUntilAfterCount(5) + ) t = current_time_ms() - start assert t >= 500 assert result is True def test_redefine_stop(self): - result = _retryable_test_with_stop.retry_with(stop=tenacity.stop_after_attempt(5))(NoneReturnUntilAfterCount(4)) + result = _retryable_test_with_stop.retry_with( + stop=tenacity.stop_after_attempt(5) + )(NoneReturnUntilAfterCount(4)) assert result is True def test_retry_error_cls_should_be_preserved(self): @@ -1220,7 +1328,10 @@ def _before_sleep_log_raises(self, get_call_fn): finally: logger.removeHandler(handler) - etalon_re = r"^Retrying .* in 0\.01 seconds as it raised " r"(IO|OS)Error: Hi there, I'm an IOError\.$" + etalon_re = ( + r"^Retrying .* in 0\.01 seconds as it raised " + r"(IO|OS)Error: Hi there, I'm an IOError\.$" + ) self.assertEqual(len(handler.records), 2) fmt = logging.Formatter().format self.assertRegex(fmt(handler.records[0]), etalon_re) @@ -1237,7 +1348,9 @@ def test_before_sleep_log_raises_with_exc_info(self): handler = CapturingHandler() logger.addHandler(handler) try: - _before_sleep = tenacity.before_sleep_log(logger, logging.INFO, exc_info=True) + _before_sleep = tenacity.before_sleep_log( + logger, logging.INFO, exc_info=True + ) retrying = Retrying( wait=tenacity.wait_fixed(0.01), stop=tenacity.stop_after_attempt(3), @@ -1267,7 +1380,9 @@ def test_before_sleep_log_returns(self, exc_info=False): handler = CapturingHandler() logger.addHandler(handler) try: - _before_sleep = tenacity.before_sleep_log(logger, logging.INFO, exc_info=exc_info) + _before_sleep = tenacity.before_sleep_log( + logger, logging.INFO, exc_info=exc_info + ) _retry = tenacity.retry_if_result(lambda result: result is None) retrying = Retrying( wait=tenacity.wait_fixed(0.01), @@ -1550,7 +1665,9 @@ def test_retry_error_is_pickleable(self): class TestRetryTyping(unittest.TestCase): - @pytest.mark.skipif(sys.version_info < (3, 0), reason="typeguard not supported for python 2") + @pytest.mark.skipif( + sys.version_info < (3, 0), reason="typeguard not supported for python 2" + ) def test_retry_type_annotations(self): """The decorator should maintain types of decorated functions.""" # Just in case this is run with unit-test, return early for py2 diff --git a/tox.ini b/tox.ini index 108c6e2..13e5a1d 100644 --- a/tox.ini +++ b/tox.ini @@ -15,20 +15,10 @@ commands = [testenv:pep8] basepython = python3 -deps = flake8 - flake8-import-order - flake8-blind-except - flake8-builtins - flake8-docstrings - flake8-rst-docstrings - flake8-logging-format -commands = flake8 {posargs} - -[testenv:black] -deps = - black +deps = ruff commands = - black . + ruff check . {posargs} + ruff format --check . {posargs} [testenv:mypy] deps = @@ -37,21 +27,7 @@ deps = commands = mypy {posargs} -[testenv:black-ci] -deps = - black - {[testenv:black]deps} -commands = - black --check --diff . - [testenv:reno] basepython = python3 deps = reno -commands = reno {posargs} - -[flake8] -exclude = .tox,.eggs -show-source = true -ignore = D100,D101,D102,D103,D104,D105,D107,G200,G201,W503,W504,E501 -enable-extensions=G -max-line-length = 120 +commands = reno {posargs} \ No newline at end of file