From aecc78ad3dfe029ef19003622510b063b3fa6d87 Mon Sep 17 00:00:00 2001 From: Robsdedude Date: Mon, 6 May 2024 09:42:15 +0200 Subject: [PATCH] Keep `numbers` for runtime type checks, use `float` for type hints --- freezegun/api.py | 18 +++++++++++------- tests/test_datetimes.py | 12 ++++++++++++ tests/test_operations.py | 17 ++++++++++++++--- 3 files changed, 37 insertions(+), 10 deletions(-) diff --git a/freezegun/api.py b/freezegun/api.py index 4ac68d5..d235292 100644 --- a/freezegun/api.py +++ b/freezegun/api.py @@ -510,9 +510,10 @@ def __call__(self) -> datetime.datetime: return self.time_to_freeze + (real_datetime.now() - self.start) def tick(self, delta: Union[datetime.timedelta, float]=datetime.timedelta(seconds=1)) -> datetime.datetime: - if isinstance(delta, numbers.Real): - # noinspection PyTypeChecker - self.move_to(self.time_to_freeze + datetime.timedelta(seconds=delta)) + if isinstance(delta, numbers.Integral): + self.move_to(self.time_to_freeze + datetime.timedelta(seconds=int(delta))) + elif isinstance(delta, numbers.Real): + self.move_to(self.time_to_freeze + datetime.timedelta(seconds=float(delta))) else: self.move_to(self.time_to_freeze + delta) # type: ignore return self.time_to_freeze @@ -532,9 +533,10 @@ def __call__(self) -> datetime.datetime: return self.time_to_freeze def tick(self, delta: Union[datetime.timedelta, float]=datetime.timedelta(seconds=1)) -> datetime.datetime: - if isinstance(delta, numbers.Real): - # noinspection PyTypeChecker - self.time_to_freeze += datetime.timedelta(seconds=delta) + if isinstance(delta, numbers.Integral): + self.move_to(self.time_to_freeze + datetime.timedelta(seconds=int(delta))) + elif isinstance(delta, numbers.Real): + self.move_to(self.time_to_freeze + datetime.timedelta(seconds=float(delta))) else: self.time_to_freeze += delta # type: ignore return self.time_to_freeze @@ -560,8 +562,10 @@ def __call__(self) -> datetime.datetime: def tick(self, delta: Union[datetime.timedelta, float, None]=None) -> datetime.datetime: if not delta: delta = datetime.timedelta(seconds=self.step_width) + elif isinstance(delta, numbers.Integral): + delta = datetime.timedelta(seconds=int(delta)) elif isinstance(delta, numbers.Real): - delta = datetime.timedelta(seconds=delta) + delta = datetime.timedelta(seconds=float(delta)) self.time_to_freeze += delta # type: ignore return self.time_to_freeze diff --git a/tests/test_datetimes.py b/tests/test_datetimes.py index 12e5949..a6f1989 100644 --- a/tests/test_datetimes.py +++ b/tests/test_datetimes.py @@ -1,6 +1,7 @@ import time import calendar import datetime +import fractions import unittest import locale import sys @@ -180,6 +181,17 @@ def test_manual_increment() -> None: assert frozen_datetime.tick(delta=datetime.timedelta(seconds=10)) == expected assert frozen_datetime() == expected + expected = initial_datetime + datetime.timedelta(seconds=22.5) + ticked_time = frozen_datetime.tick( + delta=fractions.Fraction(3, 2) # type: ignore + # type hints follow the recommendation of + # https://peps.python.org/pep-0484/#the-numeric-tower + # which means for instance `Fraction`s work at runtime, but not + # during static type analysis + ) + assert ticked_time == expected + assert frozen_datetime() == expected + def test_move_to() -> None: initial_datetime = datetime.datetime(year=1, month=7, day=12, diff --git a/tests/test_operations.py b/tests/test_operations.py index 71b94e5..c0b4f37 100644 --- a/tests/test_operations.py +++ b/tests/test_operations.py @@ -1,10 +1,11 @@ import datetime +import fractions import pytest from freezegun import freeze_time from dateutil.relativedelta import relativedelta from datetime import timedelta, tzinfo from tests import utils -from typing import Any +from typing import Any, Union @freeze_time("2012-01-14") @@ -109,10 +110,20 @@ def test_auto_tick() -> None: ( (datetime.timedelta(milliseconds=1500), 1.5), (1, 1), - (1.5, 1.5) + (1.5, 1.5), + (fractions.Fraction(3, 2), 1.5), ) ) -def test_auto_and_manual_tick(tick, expected_diff) -> None: +def test_auto_and_manual_tick( + tick: Union[ + datetime.timedelta, + float, + # fractions.Fraction, + # Fraction works at runtime, but not at type-checking time + # cf. https://peps.python.org/pep-0484/#the-numeric-tower + ], + expected_diff: float +) -> None: first_time = datetime.datetime(2020, 1, 14, 0, 0, 0, 1) with freeze_time(first_time, auto_tick_seconds=2) as frozen_time: