From 4c32d0ce3ef2102133184fb40d346bae9b5f17f5 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Mon, 29 Apr 2024 13:58:15 +0200 Subject: [PATCH 01/23] Make datetime generic over tzinfo --- stdlib/datetime.pyi | 131 +++++++++++++++++++++++++--- test_cases/stdlib/check_datetime.py | 49 +++++++++++ 2 files changed, 167 insertions(+), 13 deletions(-) create mode 100644 test_cases/stdlib/check_datetime.py diff --git a/stdlib/datetime.pyi b/stdlib/datetime.pyi index 7b890ca010dc..45131876d000 100644 --- a/stdlib/datetime.pyi +++ b/stdlib/datetime.pyi @@ -1,7 +1,7 @@ import sys from abc import abstractmethod from time import struct_time -from typing import ClassVar, Literal, NamedTuple, NoReturn, SupportsIndex, final, overload +from typing import ClassVar, Generic, Literal, NamedTuple, NoReturn, SupportsIndex, TypeVar, final, overload from typing_extensions import Self, TypeAlias, deprecated if sys.version_info >= (3, 11): @@ -210,9 +210,12 @@ class timedelta: def __bool__(self) -> bool: ... def __hash__(self) -> int: ... -class datetime(date): +_TzInfoT = TypeVar("_TzInfoT", bound=tzinfo | None, default=tzinfo | None) + +class datetime(date, Generic[_TzInfoT]): min: ClassVar[datetime] max: ClassVar[datetime] + @overload def __new__( cls, year: SupportsIndex, @@ -222,10 +225,40 @@ class datetime(date): minute: SupportsIndex = ..., second: SupportsIndex = ..., microsecond: SupportsIndex = ..., - tzinfo: _TzInfo | None = ..., + tzinfo: None = None, *, fold: int = ..., - ) -> Self: ... + ) -> datetime[None]: ... + @overload + def __new__( + cls, + year: SupportsIndex, + month: SupportsIndex, + day: SupportsIndex, + hour: SupportsIndex = ..., + minute: SupportsIndex = ..., + second: SupportsIndex = ..., + microsecond: SupportsIndex = ..., + *, + tzinfo: _TzInfo, + fold: int = ..., + ) -> datetime[_TzInfo]: ... + @overload + def __new__( + cls, + year: SupportsIndex, + month: SupportsIndex, + day: SupportsIndex, + hour: SupportsIndex, + minute: SupportsIndex, + second: SupportsIndex, + microsecond: SupportsIndex, + tzinfo: _TzInfo, + *, + fold: int = ..., + ) -> datetime[_TzInfo]: ... + @classmethod + def fromisoformat(cls, date_string: str, /) -> datetime[_TzInfo | None]: ... @property def hour(self) -> int: ... @property @@ -235,34 +268,51 @@ class datetime(date): @property def microsecond(self) -> int: ... @property - def tzinfo(self) -> _TzInfo | None: ... + def tzinfo(self) -> _TzInfoT: ... @property def fold(self) -> int: ... # On <3.12, the name of the first parameter in the pure-Python implementation # didn't match the name in the C implementation, # meaning it is only *safe* to pass it as a keyword argument on 3.12+ if sys.version_info >= (3, 12): + @overload + @classmethod + def fromtimestamp(cls, timestamp: float, tz: None = None) -> datetime[None]: ... + @overload @classmethod - def fromtimestamp(cls, timestamp: float, tz: _TzInfo | None = ...) -> Self: ... + def fromtimestamp(cls, timestamp: float, tz: _TzInfo) -> datetime[_TzInfo]: ... else: + @overload @classmethod - def fromtimestamp(cls, timestamp: float, /, tz: _TzInfo | None = ...) -> Self: ... + def fromtimestamp(cls, timestamp: float, /, tz: None = None) -> datetime[None]: ... + @overload + @classmethod + def fromtimestamp(cls, timestamp: float, /, tz: _TzInfo) -> datetime[_TzInfo]: ... @classmethod @deprecated("Use timezone-aware objects to represent datetimes in UTC; e.g. by calling .fromtimestamp(datetime.UTC)") - def utcfromtimestamp(cls, t: float, /) -> Self: ... + def utcfromtimestamp(cls, t: float, /) -> datetime[None]: ... + @overload @classmethod - def now(cls, tz: _TzInfo | None = None) -> Self: ... + def now(cls, tz: None = None) -> datetime[None]: ... + @overload + @classmethod + def now(cls, tz: _TzInfo) -> datetime[_TzInfo]: ... @classmethod @deprecated("Use timezone-aware objects to represent datetimes in UTC; e.g. by calling .now(datetime.UTC)") - def utcnow(cls) -> Self: ... + def utcnow(cls) -> datetime[None]: ... + @overload + @classmethod + def combine(cls, date: _Date, time: _Time, tzinfo: None = None) -> datetime[None]: ... + @overload @classmethod - def combine(cls, date: _Date, time: _Time, tzinfo: _TzInfo | None = ...) -> Self: ... + def combine(cls, date: _Date, time: _Time, tzinfo: _TzInfo) -> datetime[_TzInfo]: ... def timestamp(self) -> float: ... def utctimetuple(self) -> struct_time: ... def date(self) -> _Date: ... def time(self) -> _Time: ... def timetz(self) -> _Time: ... + @overload def replace( self, year: SupportsIndex = ..., @@ -272,11 +322,66 @@ class datetime(date): minute: SupportsIndex = ..., second: SupportsIndex = ..., microsecond: SupportsIndex = ..., - tzinfo: _TzInfo | None = ..., *, fold: int = ..., ) -> Self: ... - def astimezone(self, tz: _TzInfo | None = ...) -> Self: ... + @overload + def replace( + self, + year: SupportsIndex, + month: SupportsIndex, + day: SupportsIndex, + hour: SupportsIndex, + minute: SupportsIndex, + second: SupportsIndex, + microsecond: SupportsIndex, + tzinfo: _TzInfo, + *, + fold: int = ..., + ) -> datetime[_TzInfo]: ... + @overload + def replace( + self, + year: SupportsIndex = ..., + month: SupportsIndex = ..., + day: SupportsIndex = ..., + hour: SupportsIndex = ..., + minute: SupportsIndex = ..., + second: SupportsIndex = ..., + microsecond: SupportsIndex = ..., + *, + tzinfo: _TzInfo, + fold: int = ..., + ) -> datetime[_TzInfo]: ... + @overload + def replace( + self, + year: SupportsIndex, + month: SupportsIndex, + day: SupportsIndex, + hour: SupportsIndex, + minute: SupportsIndex, + second: SupportsIndex, + microsecond: SupportsIndex, + tzinfo: None, + *, + fold: int = ..., + ) -> datetime[None]: ... + @overload + def replace( + self, + year: SupportsIndex = ..., + month: SupportsIndex = ..., + day: SupportsIndex = ..., + hour: SupportsIndex = ..., + minute: SupportsIndex = ..., + second: SupportsIndex = ..., + microsecond: SupportsIndex = ..., + *, + tzinfo: None, + fold: int = ..., + ) -> datetime[None]: ... + def astimezone(self, tz: _TzInfo | None = None) -> datetime[_TzInfo]: ... def isoformat(self, sep: str = ..., timespec: str = ...) -> str: ... @classmethod def strptime(cls, date_string: str, format: str, /) -> Self: ... diff --git a/test_cases/stdlib/check_datetime.py b/test_cases/stdlib/check_datetime.py new file mode 100644 index 000000000000..a17baba152f6 --- /dev/null +++ b/test_cases/stdlib/check_datetime.py @@ -0,0 +1,49 @@ +from datetime import date, datetime, time, tzinfo, UTC +from typing import assert_type + +dt_wo: datetime[None] +dt_tz: datetime[tzinfo] +dt_any: datetime[tzinfo | None] + + +# Constructors + +assert_type(datetime(2000, 1, 1), datetime[None]) +assert_type(datetime(2000, 1, 1, tzinfo=None), datetime[None]) +assert_type(datetime(2000, 1, 1, tzinfo=UTC), datetime[tzinfo]) + +assert_type(datetime.fromtimestamp(0), datetime[None]) +assert_type(datetime.fromtimestamp(0, None), datetime[None]) +assert_type(datetime.fromtimestamp(0, UTC), datetime[tzinfo]) +assert_type(datetime.utcfromtimestamp(0), datetime[None]) + +assert_type(datetime.now(), datetime[None]) +assert_type(datetime.now(None), datetime[None]) +assert_type(datetime.now(UTC), datetime[tzinfo]) +assert_type(datetime.utcnow(), datetime[None]) + +assert_type(datetime.fromisoformat("2000-01-01"), datetime[tzinfo | None]) + +# Combine + +assert_type(datetime.combine(date(2000, 1, 1), time(12, 0)), datetime[None]) +assert_type(datetime.combine(date(2000, 1, 1), time(12, 0), tzinfo=None), datetime[None]) +assert_type(datetime.combine(date(2000, 1, 1), time(12, 0), tzinfo=UTC), datetime[tzinfo]) + +# Replace + +assert_type(dt_wo.replace(year=2001), datetime[None]) +assert_type(dt_wo.replace(year=2001, tzinfo=None), datetime[None]) +assert_type(dt_wo.replace(year=2001, tzinfo=UTC), datetime[tzinfo]) +assert_type(dt_tz.replace(year=2001), datetime[tzinfo]) +assert_type(dt_tz.replace(year=2001, tzinfo=None), datetime[None]) +assert_type(dt_tz.replace(year=2001, tzinfo=UTC), datetime[tzinfo]) +assert_type(dt_any.replace(year=2001), datetime[tzinfo | None]) +assert_type(dt_any.replace(year=2001, tzinfo=None), datetime[None]) +assert_type(dt_any.replace(year=2001, tzinfo=UTC), datetime[tzinfo]) + +# Attributes + +assert_type(dt_wo.tzinfo, None) +assert_type(dt_tz.tzinfo, tzinfo) +assert_type(dt_any.tzinfo, tzinfo | None) From e2a38004d7b66d62431fb4a477b906c48c0dc884 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 11:59:57 +0000 Subject: [PATCH 02/23] [pre-commit.ci] auto fixes from pre-commit.com hooks --- test_cases/stdlib/check_datetime.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test_cases/stdlib/check_datetime.py b/test_cases/stdlib/check_datetime.py index a17baba152f6..1f67522279ba 100644 --- a/test_cases/stdlib/check_datetime.py +++ b/test_cases/stdlib/check_datetime.py @@ -1,4 +1,6 @@ -from datetime import date, datetime, time, tzinfo, UTC +from __future__ import annotations + +from datetime import UTC, date, datetime, time, tzinfo from typing import assert_type dt_wo: datetime[None] From 814df90707708a0229e56b2ac22d9683b0615613 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Mon, 29 Apr 2024 14:01:34 +0200 Subject: [PATCH 03/23] Ignore stubtest error --- stdlib/datetime.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/datetime.pyi b/stdlib/datetime.pyi index 45131876d000..723dac458d83 100644 --- a/stdlib/datetime.pyi +++ b/stdlib/datetime.pyi @@ -258,7 +258,7 @@ class datetime(date, Generic[_TzInfoT]): fold: int = ..., ) -> datetime[_TzInfo]: ... @classmethod - def fromisoformat(cls, date_string: str, /) -> datetime[_TzInfo | None]: ... + def fromisoformat(cls, date_string: str, /) -> datetime[_TzInfo | None]: ... # type: ignore[override] @property def hour(self) -> int: ... @property From a5492ba0a1842d64e9552b23a7da823332ed38c0 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Mon, 29 Apr 2024 14:20:01 +0200 Subject: [PATCH 04/23] Overload operators --- stdlib/datetime.pyi | 36 +++++++++++++++++---- test_cases/stdlib/check_datetime.py | 49 ++++++++++++++++++++++------- test_cases/stdlib/check_unittest.py | 4 +-- 3 files changed, 69 insertions(+), 20 deletions(-) diff --git a/stdlib/datetime.pyi b/stdlib/datetime.pyi index 723dac458d83..d2d1f809df95 100644 --- a/stdlib/datetime.pyi +++ b/stdlib/datetime.pyi @@ -1,7 +1,7 @@ import sys from abc import abstractmethod from time import struct_time -from typing import ClassVar, Generic, Literal, NamedTuple, NoReturn, SupportsIndex, TypeVar, final, overload +from typing import Any, ClassVar, Generic, Literal, NamedTuple, NoReturn, SupportsIndex, TypeVar, final, overload from typing_extensions import Self, TypeAlias, deprecated if sys.version_info >= (3, 11): @@ -388,13 +388,37 @@ class datetime(date, Generic[_TzInfoT]): def utcoffset(self) -> timedelta | None: ... def tzname(self) -> str | None: ... def dst(self) -> timedelta | None: ... - def __le__(self, value: datetime, /) -> bool: ... # type: ignore[override] - def __lt__(self, value: datetime, /) -> bool: ... # type: ignore[override] - def __ge__(self, value: datetime, /) -> bool: ... # type: ignore[override] - def __gt__(self, value: datetime, /) -> bool: ... # type: ignore[override] + @overload + def __le__(self: datetime[_TzInfo], value: datetime[None], /) -> NoReturn: ... # type: ignore[override] + @overload + def __le__(self: datetime[None], value: datetime[_TzInfo], /) -> NoReturn: ... # type: ignore[override] + @overload + def __le__(self: datetime[Any], value: datetime[Any], /) -> bool: ... # type: ignore[override] + @overload + def __lt__(self: datetime[_TzInfo], value: datetime[None], /) -> NoReturn: ... # type: ignore[override] + @overload + def __lt__(self: datetime[None], value: datetime[_TzInfo], /) -> NoReturn: ... # type: ignore[override] + @overload + def __lt__(self: datetime[Any], value: datetime[Any], /) -> bool: ... # type: ignore[override] + @overload + def __ge__(self: datetime[_TzInfo], value: datetime[None], /) -> NoReturn: ... # type: ignore[override] + @overload + def __ge__(self: datetime[None], value: datetime[_TzInfo], /) -> NoReturn: ... # type: ignore[override] + @overload + def __ge__(self: datetime[Any], value: datetime[Any], /) -> bool: ... # type: ignore[override] + @overload + def __gt__(self: datetime[_TzInfo], value: datetime[None], /) -> NoReturn: ... # type: ignore[override] + @overload + def __gt__(self: datetime[None], value: datetime[_TzInfo], /) -> NoReturn: ... # type: ignore[override] + @overload + def __gt__(self: datetime[Any], value: datetime[Any], /) -> bool: ... # type: ignore[override] def __eq__(self, value: object, /) -> bool: ... def __hash__(self) -> int: ... @overload # type: ignore[override] - def __sub__(self, value: Self, /) -> timedelta: ... + def __sub__(self: datetime[_TzInfo], value: datetime[None], /) -> NoReturn: ... + @overload # type: ignore[override] + def __sub__(self: datetime[None], value: datetime[_TzInfo], /) -> NoReturn: ... + @overload # type: ignore[override] + def __sub__(self: datetime[Any], value: datetime[Any], /) -> timedelta: ... @overload def __sub__(self, value: timedelta, /) -> Self: ... diff --git a/test_cases/stdlib/check_datetime.py b/test_cases/stdlib/check_datetime.py index 1f67522279ba..17005e2a897c 100644 --- a/test_cases/stdlib/check_datetime.py +++ b/test_cases/stdlib/check_datetime.py @@ -1,11 +1,11 @@ from __future__ import annotations -from datetime import UTC, date, datetime, time, tzinfo -from typing import assert_type +from datetime import UTC, date, datetime, time, timedelta, tzinfo +from typing import Never, assert_type -dt_wo: datetime[None] +dt_none: datetime[None] dt_tz: datetime[tzinfo] -dt_any: datetime[tzinfo | None] +dt_both: datetime[tzinfo | None] # Constructors @@ -26,6 +26,31 @@ assert_type(datetime.fromisoformat("2000-01-01"), datetime[tzinfo | None]) +# Comparisons + +assert_type(dt_none < dt_none, bool) +assert_type(dt_tz < dt_tz, bool) +assert_type(dt_both < dt_both, bool) + +assert_type(dt_none < dt_tz, Never) +assert_type(dt_tz < dt_none, Never) +assert_type(dt_both < dt_none, bool) +assert_type(dt_both < dt_tz, bool) +assert_type(dt_none < dt_both, bool) + +# Sub + +assert_type(dt_none - dt_none, timedelta) +assert_type(dt_tz - dt_tz, timedelta) +assert_type(dt_both - dt_both, timedelta) + +assert_type(dt_none - dt_tz, Never) +assert_type(dt_tz - dt_none, Never) +assert_type(dt_both - dt_none, timedelta) +assert_type(dt_both - dt_tz, timedelta) +assert_type(dt_none - dt_both, timedelta) +assert_type(dt_tz - dt_both, timedelta) + # Combine assert_type(datetime.combine(date(2000, 1, 1), time(12, 0)), datetime[None]) @@ -34,18 +59,18 @@ # Replace -assert_type(dt_wo.replace(year=2001), datetime[None]) -assert_type(dt_wo.replace(year=2001, tzinfo=None), datetime[None]) -assert_type(dt_wo.replace(year=2001, tzinfo=UTC), datetime[tzinfo]) +assert_type(dt_none.replace(year=2001), datetime[None]) +assert_type(dt_none.replace(year=2001, tzinfo=None), datetime[None]) +assert_type(dt_none.replace(year=2001, tzinfo=UTC), datetime[tzinfo]) assert_type(dt_tz.replace(year=2001), datetime[tzinfo]) assert_type(dt_tz.replace(year=2001, tzinfo=None), datetime[None]) assert_type(dt_tz.replace(year=2001, tzinfo=UTC), datetime[tzinfo]) -assert_type(dt_any.replace(year=2001), datetime[tzinfo | None]) -assert_type(dt_any.replace(year=2001, tzinfo=None), datetime[None]) -assert_type(dt_any.replace(year=2001, tzinfo=UTC), datetime[tzinfo]) +assert_type(dt_both.replace(year=2001), datetime[tzinfo | None]) +assert_type(dt_both.replace(year=2001, tzinfo=None), datetime[None]) +assert_type(dt_both.replace(year=2001, tzinfo=UTC), datetime[tzinfo]) # Attributes -assert_type(dt_wo.tzinfo, None) +assert_type(dt_none.tzinfo, None) assert_type(dt_tz.tzinfo, tzinfo) -assert_type(dt_any.tzinfo, tzinfo | None) +assert_type(dt_both.tzinfo, tzinfo | None) diff --git a/test_cases/stdlib/check_unittest.py b/test_cases/stdlib/check_unittest.py index 40c6efaa8ca0..da3e146117d6 100644 --- a/test_cases/stdlib/check_unittest.py +++ b/test_cases/stdlib/check_unittest.py @@ -28,7 +28,7 @@ case.assertAlmostEqual(2.4, 2.41, places=9, delta=0.02) # type: ignore case.assertAlmostEqual("foo", "bar") # type: ignore -case.assertAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1)) # type: ignore +case.assertAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1)) case.assertAlmostEqual(Decimal("0.4"), Fraction(1, 2)) # type: ignore case.assertAlmostEqual(complex(2, 3), Decimal("0.9")) # type: ignore @@ -44,7 +44,7 @@ case.assertNotAlmostEqual(2.4, 2.41, places=9, delta=0.02) # type: ignore case.assertNotAlmostEqual("foo", "bar") # type: ignore -case.assertNotAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1)) # type: ignore +case.assertNotAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1)) case.assertNotAlmostEqual(Decimal("0.4"), Fraction(1, 2)) # type: ignore case.assertNotAlmostEqual(complex(2, 3), Decimal("0.9")) # type: ignore From d176a1d7023f1918d6ca749550290e1ef221601d Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Mon, 29 Apr 2024 14:24:23 +0200 Subject: [PATCH 05/23] Update some type ignores --- stdlib/datetime.pyi | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/stdlib/datetime.pyi b/stdlib/datetime.pyi index d2d1f809df95..dcf39893273e 100644 --- a/stdlib/datetime.pyi +++ b/stdlib/datetime.pyi @@ -282,7 +282,7 @@ class datetime(date, Generic[_TzInfoT]): @classmethod def fromtimestamp(cls, timestamp: float, tz: _TzInfo) -> datetime[_TzInfo]: ... else: - @overload + @overload # type: ignore[override] @classmethod def fromtimestamp(cls, timestamp: float, /, tz: None = None) -> datetime[None]: ... @overload @@ -388,37 +388,37 @@ class datetime(date, Generic[_TzInfoT]): def utcoffset(self) -> timedelta | None: ... def tzname(self) -> str | None: ... def dst(self) -> timedelta | None: ... + @overload # type: ignore[override] + def __le__(self: datetime[_TzInfo], value: datetime[None], /) -> NoReturn: ... @overload - def __le__(self: datetime[_TzInfo], value: datetime[None], /) -> NoReturn: ... # type: ignore[override] - @overload - def __le__(self: datetime[None], value: datetime[_TzInfo], /) -> NoReturn: ... # type: ignore[override] - @overload - def __le__(self: datetime[Any], value: datetime[Any], /) -> bool: ... # type: ignore[override] - @overload - def __lt__(self: datetime[_TzInfo], value: datetime[None], /) -> NoReturn: ... # type: ignore[override] - @overload - def __lt__(self: datetime[None], value: datetime[_TzInfo], /) -> NoReturn: ... # type: ignore[override] + def __le__(self: datetime[None], value: datetime[_TzInfo], /) -> NoReturn: ... @overload - def __lt__(self: datetime[Any], value: datetime[Any], /) -> bool: ... # type: ignore[override] + def __le__(self: datetime[Any], value: datetime[Any], /) -> bool: ... + @overload # type: ignore[override] + def __lt__(self: datetime[_TzInfo], value: datetime[None], /) -> NoReturn: ... @overload - def __ge__(self: datetime[_TzInfo], value: datetime[None], /) -> NoReturn: ... # type: ignore[override] + def __lt__(self: datetime[None], value: datetime[_TzInfo], /) -> NoReturn: ... @overload - def __ge__(self: datetime[None], value: datetime[_TzInfo], /) -> NoReturn: ... # type: ignore[override] + def __lt__(self: datetime[Any], value: datetime[Any], /) -> bool: ... + @overload # type: ignore[override] + def __ge__(self: datetime[_TzInfo], value: datetime[None], /) -> NoReturn: ... @overload - def __ge__(self: datetime[Any], value: datetime[Any], /) -> bool: ... # type: ignore[override] + def __ge__(self: datetime[None], value: datetime[_TzInfo], /) -> NoReturn: ... @overload - def __gt__(self: datetime[_TzInfo], value: datetime[None], /) -> NoReturn: ... # type: ignore[override] + def __ge__(self: datetime[Any], value: datetime[Any], /) -> bool: ... + @overload # type: ignore[override] + def __gt__(self: datetime[_TzInfo], value: datetime[None], /) -> NoReturn: ... @overload - def __gt__(self: datetime[None], value: datetime[_TzInfo], /) -> NoReturn: ... # type: ignore[override] + def __gt__(self: datetime[None], value: datetime[_TzInfo], /) -> NoReturn: ... @overload - def __gt__(self: datetime[Any], value: datetime[Any], /) -> bool: ... # type: ignore[override] + def __gt__(self: datetime[Any], value: datetime[Any], /) -> bool: ... def __eq__(self, value: object, /) -> bool: ... def __hash__(self) -> int: ... @overload # type: ignore[override] def __sub__(self: datetime[_TzInfo], value: datetime[None], /) -> NoReturn: ... - @overload # type: ignore[override] + @overload def __sub__(self: datetime[None], value: datetime[_TzInfo], /) -> NoReturn: ... - @overload # type: ignore[override] + @overload def __sub__(self: datetime[Any], value: datetime[Any], /) -> timedelta: ... @overload def __sub__(self, value: timedelta, /) -> Self: ... From 040dbcb3d76ae160e917551de5500543542a6955 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Mon, 29 Apr 2024 14:29:58 +0200 Subject: [PATCH 06/23] Ignore some mypy errors --- stdlib/datetime.pyi | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/stdlib/datetime.pyi b/stdlib/datetime.pyi index dcf39893273e..7219c39ad583 100644 --- a/stdlib/datetime.pyi +++ b/stdlib/datetime.pyi @@ -389,27 +389,27 @@ class datetime(date, Generic[_TzInfoT]): def tzname(self) -> str | None: ... def dst(self) -> timedelta | None: ... @overload # type: ignore[override] - def __le__(self: datetime[_TzInfo], value: datetime[None], /) -> NoReturn: ... + def __le__(self: datetime[_TzInfo], value: datetime[None], /) -> NoReturn: ... # type: ignore[misc] @overload - def __le__(self: datetime[None], value: datetime[_TzInfo], /) -> NoReturn: ... + def __le__(self: datetime[None], value: datetime[_TzInfo], /) -> NoReturn: ... # type: ignore[misc] @overload def __le__(self: datetime[Any], value: datetime[Any], /) -> bool: ... @overload # type: ignore[override] - def __lt__(self: datetime[_TzInfo], value: datetime[None], /) -> NoReturn: ... + def __lt__(self: datetime[_TzInfo], value: datetime[None], /) -> NoReturn: ... # type: ignore[misc] @overload - def __lt__(self: datetime[None], value: datetime[_TzInfo], /) -> NoReturn: ... + def __lt__(self: datetime[None], value: datetime[_TzInfo], /) -> NoReturn: ... # type: ignore[misc] @overload def __lt__(self: datetime[Any], value: datetime[Any], /) -> bool: ... @overload # type: ignore[override] - def __ge__(self: datetime[_TzInfo], value: datetime[None], /) -> NoReturn: ... + def __ge__(self: datetime[_TzInfo], value: datetime[None], /) -> NoReturn: ... # type: ignore[misc] @overload - def __ge__(self: datetime[None], value: datetime[_TzInfo], /) -> NoReturn: ... + def __ge__(self: datetime[None], value: datetime[_TzInfo], /) -> NoReturn: ... # type: ignore[misc] @overload def __ge__(self: datetime[Any], value: datetime[Any], /) -> bool: ... @overload # type: ignore[override] - def __gt__(self: datetime[_TzInfo], value: datetime[None], /) -> NoReturn: ... + def __gt__(self: datetime[_TzInfo], value: datetime[None], /) -> NoReturn: ... # type: ignore[misc] @overload - def __gt__(self: datetime[None], value: datetime[_TzInfo], /) -> NoReturn: ... + def __gt__(self: datetime[None], value: datetime[_TzInfo], /) -> NoReturn: ... # type: ignore[misc] @overload def __gt__(self: datetime[Any], value: datetime[Any], /) -> bool: ... def __eq__(self, value: object, /) -> bool: ... From 7ce96ea0fb3360085ea49f7a4f8dfc0e17affffb Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Mon, 29 Apr 2024 14:34:45 +0200 Subject: [PATCH 07/23] Make pyright a bit happier --- test_cases/stdlib/check_datetime.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test_cases/stdlib/check_datetime.py b/test_cases/stdlib/check_datetime.py index 17005e2a897c..6f9f7ce52f91 100644 --- a/test_cases/stdlib/check_datetime.py +++ b/test_cases/stdlib/check_datetime.py @@ -1,7 +1,9 @@ from __future__ import annotations -from datetime import UTC, date, datetime, time, timedelta, tzinfo -from typing import Never, assert_type +from datetime import date, datetime, time, timedelta, timezone, tzinfo +from typing_extensions import Never, assert_type + +UTC: timezone dt_none: datetime[None] dt_tz: datetime[tzinfo] @@ -17,12 +19,12 @@ assert_type(datetime.fromtimestamp(0), datetime[None]) assert_type(datetime.fromtimestamp(0, None), datetime[None]) assert_type(datetime.fromtimestamp(0, UTC), datetime[tzinfo]) -assert_type(datetime.utcfromtimestamp(0), datetime[None]) +assert_type(datetime.utcfromtimestamp(0), datetime[None]) # pyright: ignore[reportDeprecated] assert_type(datetime.now(), datetime[None]) assert_type(datetime.now(None), datetime[None]) assert_type(datetime.now(UTC), datetime[tzinfo]) -assert_type(datetime.utcnow(), datetime[None]) +assert_type(datetime.utcnow(), datetime[None]) # pyright: ignore[reportDeprecated] assert_type(datetime.fromisoformat("2000-01-01"), datetime[tzinfo | None]) From ce3ee770f2ecc9c87878349de8c5b1b4573533ce Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Mon, 29 Apr 2024 14:39:19 +0200 Subject: [PATCH 08/23] Add another type ignore --- stdlib/datetime.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/datetime.pyi b/stdlib/datetime.pyi index 7219c39ad583..9943d552072e 100644 --- a/stdlib/datetime.pyi +++ b/stdlib/datetime.pyi @@ -275,7 +275,7 @@ class datetime(date, Generic[_TzInfoT]): # didn't match the name in the C implementation, # meaning it is only *safe* to pass it as a keyword argument on 3.12+ if sys.version_info >= (3, 12): - @overload + @overload # type: ignore[override] @classmethod def fromtimestamp(cls, timestamp: float, tz: None = None) -> datetime[None]: ... @overload From c59db10372216ff74149659ae5a7a8c773b4bd95 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Mon, 29 Apr 2024 14:47:19 +0200 Subject: [PATCH 09/23] Make pyright mostly happy --- test_cases/stdlib/check_datetime.py | 16 ++++++++-------- test_cases/stdlib/check_unittest.py | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/test_cases/stdlib/check_datetime.py b/test_cases/stdlib/check_datetime.py index 6f9f7ce52f91..124be460a4ac 100644 --- a/test_cases/stdlib/check_datetime.py +++ b/test_cases/stdlib/check_datetime.py @@ -1,14 +1,14 @@ from __future__ import annotations from datetime import date, datetime, time, timedelta, timezone, tzinfo +from typing import Union, cast from typing_extensions import Never, assert_type -UTC: timezone - -dt_none: datetime[None] -dt_tz: datetime[tzinfo] -dt_both: datetime[tzinfo | None] +UTC: timezone = timezone.utc +dt_none = cast(datetime[None], None) +dt_tz = cast(datetime[tzinfo], None) +dt_both = cast(datetime[Union[tzinfo, None]], None) # Constructors @@ -26,7 +26,7 @@ assert_type(datetime.now(UTC), datetime[tzinfo]) assert_type(datetime.utcnow(), datetime[None]) # pyright: ignore[reportDeprecated] -assert_type(datetime.fromisoformat("2000-01-01"), datetime[tzinfo | None]) +assert_type(datetime.fromisoformat("2000-01-01"), datetime[Union[tzinfo, None]]) # Comparisons @@ -67,7 +67,7 @@ assert_type(dt_tz.replace(year=2001), datetime[tzinfo]) assert_type(dt_tz.replace(year=2001, tzinfo=None), datetime[None]) assert_type(dt_tz.replace(year=2001, tzinfo=UTC), datetime[tzinfo]) -assert_type(dt_both.replace(year=2001), datetime[tzinfo | None]) +assert_type(dt_both.replace(year=2001), datetime[Union[tzinfo, None]]) assert_type(dt_both.replace(year=2001, tzinfo=None), datetime[None]) assert_type(dt_both.replace(year=2001, tzinfo=UTC), datetime[tzinfo]) @@ -75,4 +75,4 @@ assert_type(dt_none.tzinfo, None) assert_type(dt_tz.tzinfo, tzinfo) -assert_type(dt_both.tzinfo, tzinfo | None) +assert_type(dt_both.tzinfo, Union[tzinfo, None]) diff --git a/test_cases/stdlib/check_unittest.py b/test_cases/stdlib/check_unittest.py index da3e146117d6..40c6efaa8ca0 100644 --- a/test_cases/stdlib/check_unittest.py +++ b/test_cases/stdlib/check_unittest.py @@ -28,7 +28,7 @@ case.assertAlmostEqual(2.4, 2.41, places=9, delta=0.02) # type: ignore case.assertAlmostEqual("foo", "bar") # type: ignore -case.assertAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1)) +case.assertAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1)) # type: ignore case.assertAlmostEqual(Decimal("0.4"), Fraction(1, 2)) # type: ignore case.assertAlmostEqual(complex(2, 3), Decimal("0.9")) # type: ignore @@ -44,7 +44,7 @@ case.assertNotAlmostEqual(2.4, 2.41, places=9, delta=0.02) # type: ignore case.assertNotAlmostEqual("foo", "bar") # type: ignore -case.assertNotAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1)) +case.assertNotAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1)) # type: ignore case.assertNotAlmostEqual(Decimal("0.4"), Fraction(1, 2)) # type: ignore case.assertNotAlmostEqual(complex(2, 3), Decimal("0.9")) # type: ignore From c0d5cc1d58452a21a1a546033ccc48267ca17c0b Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Mon, 29 Apr 2024 14:48:00 +0200 Subject: [PATCH 10/23] Set default to `Any` --- stdlib/datetime.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/datetime.pyi b/stdlib/datetime.pyi index 9943d552072e..6765c0936bce 100644 --- a/stdlib/datetime.pyi +++ b/stdlib/datetime.pyi @@ -210,7 +210,7 @@ class timedelta: def __bool__(self) -> bool: ... def __hash__(self) -> int: ... -_TzInfoT = TypeVar("_TzInfoT", bound=tzinfo | None, default=tzinfo | None) +_TzInfoT = TypeVar("_TzInfoT", bound=tzinfo | None, default=Any) class datetime(date, Generic[_TzInfoT]): min: ClassVar[datetime] From eb4f7d81ee728c058f5e22e2986f9b449d9a42dd Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Mon, 29 Apr 2024 14:53:49 +0200 Subject: [PATCH 11/23] Make both, pyright and mypy happy --- test_cases/stdlib/check_unittest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test_cases/stdlib/check_unittest.py b/test_cases/stdlib/check_unittest.py index 40c6efaa8ca0..a50e41dedde8 100644 --- a/test_cases/stdlib/check_unittest.py +++ b/test_cases/stdlib/check_unittest.py @@ -28,7 +28,7 @@ case.assertAlmostEqual(2.4, 2.41, places=9, delta=0.02) # type: ignore case.assertAlmostEqual("foo", "bar") # type: ignore -case.assertAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1)) # type: ignore +case.assertAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1)) # pyright: ignore case.assertAlmostEqual(Decimal("0.4"), Fraction(1, 2)) # type: ignore case.assertAlmostEqual(complex(2, 3), Decimal("0.9")) # type: ignore @@ -44,7 +44,7 @@ case.assertNotAlmostEqual(2.4, 2.41, places=9, delta=0.02) # type: ignore case.assertNotAlmostEqual("foo", "bar") # type: ignore -case.assertNotAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1)) # type: ignore +case.assertNotAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1)) # pyright: ignore case.assertNotAlmostEqual(Decimal("0.4"), Fraction(1, 2)) # type: ignore case.assertNotAlmostEqual(complex(2, 3), Decimal("0.9")) # type: ignore From 14aa5d2d57d878eb913957926dc0aca34e9f60e3 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Mon, 29 Apr 2024 14:58:17 +0200 Subject: [PATCH 12/23] Annotate datetime.today() --- stdlib/datetime.pyi | 2 ++ test_cases/stdlib/check_datetime.py | 1 + 2 files changed, 3 insertions(+) diff --git a/stdlib/datetime.pyi b/stdlib/datetime.pyi index 6765c0936bce..9892f796bed9 100644 --- a/stdlib/datetime.pyi +++ b/stdlib/datetime.pyi @@ -299,6 +299,8 @@ class datetime(date, Generic[_TzInfoT]): @classmethod def now(cls, tz: _TzInfo) -> datetime[_TzInfo]: ... @classmethod + def today(cls) -> datetime[None]: ... # type: ignore[override] + @classmethod @deprecated("Use timezone-aware objects to represent datetimes in UTC; e.g. by calling .now(datetime.UTC)") def utcnow(cls) -> datetime[None]: ... @overload diff --git a/test_cases/stdlib/check_datetime.py b/test_cases/stdlib/check_datetime.py index 124be460a4ac..055776ba0b9f 100644 --- a/test_cases/stdlib/check_datetime.py +++ b/test_cases/stdlib/check_datetime.py @@ -24,6 +24,7 @@ assert_type(datetime.now(), datetime[None]) assert_type(datetime.now(None), datetime[None]) assert_type(datetime.now(UTC), datetime[tzinfo]) +assert_type(datetime.today(), datetime[None]) assert_type(datetime.utcnow(), datetime[None]) # pyright: ignore[reportDeprecated] assert_type(datetime.fromisoformat("2000-01-01"), datetime[Union[tzinfo, None]]) From d8d9d416abeb3ef26e490b05679dbc43a4f0175d Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Mon, 29 Apr 2024 14:58:33 +0200 Subject: [PATCH 13/23] Use explicit datetime annotation in test --- stubs/python-dateutil/@tests/test_cases/check_inheritance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stubs/python-dateutil/@tests/test_cases/check_inheritance.py b/stubs/python-dateutil/@tests/test_cases/check_inheritance.py index 716881c6f657..faebbf3269b2 100644 --- a/stubs/python-dateutil/@tests/test_cases/check_inheritance.py +++ b/stubs/python-dateutil/@tests/test_cases/check_inheritance.py @@ -15,7 +15,7 @@ class MyDateTime(datetime): d3 = datetime.today() x3 = d3 - relativedelta(days=1) -assert_type(x3, datetime) +assert_type(x3, datetime[None]) d2 = date.today() x2 = d2 - relativedelta(days=1) From f656dbb828ba07f36ba3272ae241768e89ac1fbc Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Mon, 29 Apr 2024 15:25:20 +0200 Subject: [PATCH 14/23] Revert bound, use covariance --- stdlib/datetime.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/datetime.pyi b/stdlib/datetime.pyi index 9892f796bed9..56ab2175b0b6 100644 --- a/stdlib/datetime.pyi +++ b/stdlib/datetime.pyi @@ -210,7 +210,7 @@ class timedelta: def __bool__(self) -> bool: ... def __hash__(self) -> int: ... -_TzInfoT = TypeVar("_TzInfoT", bound=tzinfo | None, default=Any) +_TzInfoT = TypeVar("_TzInfoT", bound=tzinfo | None, default=tzinfo | None, covariant=True) class datetime(date, Generic[_TzInfoT]): min: ClassVar[datetime] From 25a3e485117ddbc2691bda7e833d3bb7f21ef1b4 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Mon, 29 Apr 2024 15:37:14 +0200 Subject: [PATCH 15/23] Annotate datetime.strptime() --- stdlib/datetime.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/datetime.pyi b/stdlib/datetime.pyi index 56ab2175b0b6..4282adbcf8cd 100644 --- a/stdlib/datetime.pyi +++ b/stdlib/datetime.pyi @@ -386,7 +386,7 @@ class datetime(date, Generic[_TzInfoT]): def astimezone(self, tz: _TzInfo | None = None) -> datetime[_TzInfo]: ... def isoformat(self, sep: str = ..., timespec: str = ...) -> str: ... @classmethod - def strptime(cls, date_string: str, format: str, /) -> Self: ... + def strptime(cls, date_string: str, format: str, /) -> datetime[_TzInfo | None]: ... def utcoffset(self) -> timedelta | None: ... def tzname(self) -> str | None: ... def dst(self) -> timedelta | None: ... From ce3c470be9699d8665e9b46bf826cceb2e0de171 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Mon, 29 Apr 2024 15:53:59 +0200 Subject: [PATCH 16/23] Reshuffle annotations; use Any as default again --- stdlib/datetime.pyi | 44 ++++++++++++++++++----------- test_cases/stdlib/check_unittest.py | 6 ++-- 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/stdlib/datetime.pyi b/stdlib/datetime.pyi index 4282adbcf8cd..2c0f2489d08a 100644 --- a/stdlib/datetime.pyi +++ b/stdlib/datetime.pyi @@ -210,7 +210,7 @@ class timedelta: def __bool__(self) -> bool: ... def __hash__(self) -> int: ... -_TzInfoT = TypeVar("_TzInfoT", bound=tzinfo | None, default=tzinfo | None, covariant=True) +_TzInfoT = TypeVar("_TzInfoT", bound=tzinfo | None, default=Any) class datetime(date, Generic[_TzInfoT]): min: ClassVar[datetime] @@ -391,36 +391,48 @@ class datetime(date, Generic[_TzInfoT]): def tzname(self) -> str | None: ... def dst(self) -> timedelta | None: ... @overload # type: ignore[override] - def __le__(self: datetime[_TzInfo], value: datetime[None], /) -> NoReturn: ... # type: ignore[misc] + def __le__( + self: datetime[_TzInfo] | datetime[_TzInfo | None], value: datetime[_TzInfo] | datetime[_TzInfo | None], / + ) -> bool: ... # type: ignore[misc] @overload - def __le__(self: datetime[None], value: datetime[_TzInfo], /) -> NoReturn: ... # type: ignore[misc] + def __le__(self: datetime[None] | datetime[_TzInfo | None], value: datetime[None] | datetime[_TzInfo | None], /) -> bool: ... # type: ignore[misc] @overload - def __le__(self: datetime[Any], value: datetime[Any], /) -> bool: ... + def __le__(self: datetime[Any], value: datetime[Any], /) -> NoReturn: ... @overload # type: ignore[override] - def __lt__(self: datetime[_TzInfo], value: datetime[None], /) -> NoReturn: ... # type: ignore[misc] + def __lt__( + self: datetime[_TzInfo] | datetime[_TzInfo | None], value: datetime[_TzInfo] | datetime[_TzInfo | None], / + ) -> bool: ... # type: ignore[misc] @overload - def __lt__(self: datetime[None], value: datetime[_TzInfo], /) -> NoReturn: ... # type: ignore[misc] + def __lt__(self: datetime[None] | datetime[_TzInfo | None], value: datetime[None] | datetime[_TzInfo | None], /) -> bool: ... # type: ignore[misc] @overload - def __lt__(self: datetime[Any], value: datetime[Any], /) -> bool: ... + def __lt__(self: datetime[Any], value: datetime[Any], /) -> NoReturn: ... @overload # type: ignore[override] - def __ge__(self: datetime[_TzInfo], value: datetime[None], /) -> NoReturn: ... # type: ignore[misc] + def __ge__( + self: datetime[_TzInfo] | datetime[_TzInfo | None], value: datetime[_TzInfo] | datetime[_TzInfo | None], / + ) -> bool: ... # type: ignore[misc] @overload - def __ge__(self: datetime[None], value: datetime[_TzInfo], /) -> NoReturn: ... # type: ignore[misc] + def __ge__(self: datetime[None] | datetime[_TzInfo | None], value: datetime[None] | datetime[_TzInfo | None], /) -> bool: ... # type: ignore[misc] @overload - def __ge__(self: datetime[Any], value: datetime[Any], /) -> bool: ... + def __ge__(self: datetime[Any], value: datetime[Any], /) -> NoReturn: ... @overload # type: ignore[override] - def __gt__(self: datetime[_TzInfo], value: datetime[None], /) -> NoReturn: ... # type: ignore[misc] + def __gt__( + self: datetime[_TzInfo] | datetime[_TzInfo | None], value: datetime[_TzInfo] | datetime[_TzInfo | None], / + ) -> bool: ... # type: ignore[misc] @overload - def __gt__(self: datetime[None], value: datetime[_TzInfo], /) -> NoReturn: ... # type: ignore[misc] + def __gt__(self: datetime[None] | datetime[_TzInfo | None], value: datetime[None] | datetime[_TzInfo | None], /) -> bool: ... # type: ignore[misc] @overload - def __gt__(self: datetime[Any], value: datetime[Any], /) -> bool: ... + def __gt__(self: datetime[Any], value: datetime[Any], /) -> NoReturn: ... def __eq__(self, value: object, /) -> bool: ... def __hash__(self) -> int: ... @overload # type: ignore[override] - def __sub__(self: datetime[_TzInfo], value: datetime[None], /) -> NoReturn: ... + def __sub__( + self: datetime[_TzInfo] | datetime[_TzInfo | None], value: datetime[_TzInfo] | datetime[_TzInfo | None], / + ) -> timedelta: ... @overload - def __sub__(self: datetime[None], value: datetime[_TzInfo], /) -> NoReturn: ... + def __sub__( + self: datetime[None] | datetime[_TzInfo | None], value: datetime[None] | datetime[_TzInfo | None], / + ) -> timedelta: ... @overload - def __sub__(self: datetime[Any], value: datetime[Any], /) -> timedelta: ... + def __sub__(self: datetime[Any], value: datetime[Any], /) -> NoReturn: ... @overload def __sub__(self, value: timedelta, /) -> Self: ... diff --git a/test_cases/stdlib/check_unittest.py b/test_cases/stdlib/check_unittest.py index a50e41dedde8..2879f3c0ecdb 100644 --- a/test_cases/stdlib/check_unittest.py +++ b/test_cases/stdlib/check_unittest.py @@ -28,7 +28,8 @@ case.assertAlmostEqual(2.4, 2.41, places=9, delta=0.02) # type: ignore case.assertAlmostEqual("foo", "bar") # type: ignore -case.assertAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1)) # pyright: ignore +# TODO: mypy and pyright disagree whether this requires a type ignore. +# case.assertAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1)) case.assertAlmostEqual(Decimal("0.4"), Fraction(1, 2)) # type: ignore case.assertAlmostEqual(complex(2, 3), Decimal("0.9")) # type: ignore @@ -44,7 +45,8 @@ case.assertNotAlmostEqual(2.4, 2.41, places=9, delta=0.02) # type: ignore case.assertNotAlmostEqual("foo", "bar") # type: ignore -case.assertNotAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1)) # pyright: ignore +# TODO: mypy and pyright disagree whether this requires a type ignore. +# case.assertNotAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1)) case.assertNotAlmostEqual(Decimal("0.4"), Fraction(1, 2)) # type: ignore case.assertNotAlmostEqual(complex(2, 3), Decimal("0.9")) # type: ignore From b148970bff4fc4e87a9f0f204124fcc52d22a2d6 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Mon, 29 Apr 2024 15:59:13 +0200 Subject: [PATCH 17/23] Add some type ignores --- stdlib/datetime.pyi | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/stdlib/datetime.pyi b/stdlib/datetime.pyi index 2c0f2489d08a..14f99bc7b94f 100644 --- a/stdlib/datetime.pyi +++ b/stdlib/datetime.pyi @@ -391,7 +391,7 @@ class datetime(date, Generic[_TzInfoT]): def tzname(self) -> str | None: ... def dst(self) -> timedelta | None: ... @overload # type: ignore[override] - def __le__( + def __le__( # type: ignore[overload-overlap] self: datetime[_TzInfo] | datetime[_TzInfo | None], value: datetime[_TzInfo] | datetime[_TzInfo | None], / ) -> bool: ... # type: ignore[misc] @overload @@ -399,7 +399,7 @@ class datetime(date, Generic[_TzInfoT]): @overload def __le__(self: datetime[Any], value: datetime[Any], /) -> NoReturn: ... @overload # type: ignore[override] - def __lt__( + def __lt__( # type: ignore[overload-overlap] self: datetime[_TzInfo] | datetime[_TzInfo | None], value: datetime[_TzInfo] | datetime[_TzInfo | None], / ) -> bool: ... # type: ignore[misc] @overload @@ -407,7 +407,7 @@ class datetime(date, Generic[_TzInfoT]): @overload def __lt__(self: datetime[Any], value: datetime[Any], /) -> NoReturn: ... @overload # type: ignore[override] - def __ge__( + def __ge__( # type: ignore[overload-overlap] self: datetime[_TzInfo] | datetime[_TzInfo | None], value: datetime[_TzInfo] | datetime[_TzInfo | None], / ) -> bool: ... # type: ignore[misc] @overload @@ -415,7 +415,7 @@ class datetime(date, Generic[_TzInfoT]): @overload def __ge__(self: datetime[Any], value: datetime[Any], /) -> NoReturn: ... @overload # type: ignore[override] - def __gt__( + def __gt__( # type: ignore[overload-overlap] self: datetime[_TzInfo] | datetime[_TzInfo | None], value: datetime[_TzInfo] | datetime[_TzInfo | None], / ) -> bool: ... # type: ignore[misc] @overload @@ -425,11 +425,11 @@ class datetime(date, Generic[_TzInfoT]): def __eq__(self, value: object, /) -> bool: ... def __hash__(self) -> int: ... @overload # type: ignore[override] - def __sub__( + def __sub__( # type: ignore[overload-overlap] self: datetime[_TzInfo] | datetime[_TzInfo | None], value: datetime[_TzInfo] | datetime[_TzInfo | None], / ) -> timedelta: ... @overload - def __sub__( + def __sub__( # type: ignore[overload-overlap] self: datetime[None] | datetime[_TzInfo | None], value: datetime[None] | datetime[_TzInfo | None], / ) -> timedelta: ... @overload From 605f59849c8d91657e9f2d7d9ed3b1727373fb4a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 10 Nov 2025 10:04:27 +0000 Subject: [PATCH 18/23] [pre-commit.ci] auto fixes from pre-commit.com hooks --- stdlib/datetime.pyi | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stdlib/datetime.pyi b/stdlib/datetime.pyi index ac216d007179..66f242613290 100644 --- a/stdlib/datetime.pyi +++ b/stdlib/datetime.pyi @@ -1,7 +1,7 @@ import sys from abc import abstractmethod from time import struct_time -from typing import Any, ClassVar, Final, Generic, Literal, NamedTuple, NoReturn, SupportsIndex, TypeVar, final, overload, type_check_only +from typing import Any, ClassVar, Final, Generic, NoReturn, SupportsIndex, TypeVar, final, overload, type_check_only from typing_extensions import CapsuleType, Self, TypeAlias, deprecated, disjoint_base if sys.version_info >= (3, 11): @@ -362,6 +362,7 @@ class datetime(date, Generic[_TzInfoT]): tzinfo: _TzInfo | None = ..., fold: int = ..., ) -> Self: ... + @overload def replace( self, From 510c87fc68e19a3deda34ccbe44c7b86a575c75c Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Mon, 10 Nov 2025 11:29:19 +0100 Subject: [PATCH 19/23] Revert unittest tests --- stdlib/@tests/test_cases/check_unittest.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/stdlib/@tests/test_cases/check_unittest.py b/stdlib/@tests/test_cases/check_unittest.py index 16ece1c01e0b..bbc2a2ba4843 100644 --- a/stdlib/@tests/test_cases/check_unittest.py +++ b/stdlib/@tests/test_cases/check_unittest.py @@ -28,8 +28,7 @@ case.assertAlmostEqual(2.4, 2.41, places=9, delta=0.02) # type: ignore case.assertAlmostEqual("foo", "bar") # type: ignore -# TODO: mypy and pyright disagree whether this requires a type ignore. -# case.assertAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1)) +case.assertAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1)) # type: ignore case.assertAlmostEqual(Decimal("0.4"), Fraction(1, 2)) # type: ignore case.assertAlmostEqual(complex(2, 3), Decimal("0.9")) # type: ignore @@ -45,8 +44,7 @@ case.assertNotAlmostEqual(2.4, 2.41, places=9, delta=0.02) # type: ignore case.assertNotAlmostEqual("foo", "bar") # type: ignore -# TODO: mypy and pyright disagree whether this requires a type ignore. -# case.assertNotAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1)) +case.assertNotAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1)) # type: ignore case.assertNotAlmostEqual(Decimal("0.4"), Fraction(1, 2)) # type: ignore case.assertNotAlmostEqual(complex(2, 3), Decimal("0.9")) # type: ignore From 2b2c31425d5dedafecf9648801ab42a7eb93d004 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Mon, 10 Nov 2025 11:37:19 +0100 Subject: [PATCH 20/23] Fix a test --- stubs/python-dateutil/@tests/test_cases/check_inheritance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stubs/python-dateutil/@tests/test_cases/check_inheritance.py b/stubs/python-dateutil/@tests/test_cases/check_inheritance.py index 1e236767b047..dfdbed5e5b63 100644 --- a/stubs/python-dateutil/@tests/test_cases/check_inheritance.py +++ b/stubs/python-dateutil/@tests/test_cases/check_inheritance.py @@ -10,7 +10,7 @@ class MyDateTime(datetime): d = MyDateTime.now() x = d - relativedelta(days=1) -assert_type(x, MyDateTime) +assert_type(x, datetime[None]) d3 = datetime.today() x3 = d3 - relativedelta(days=1) From cbf66a87ce9bd85bcd34e03d3547c0cd50e1f262 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Mon, 10 Nov 2025 11:39:29 +0100 Subject: [PATCH 21/23] Add another type ignore --- stdlib/datetime.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/datetime.pyi b/stdlib/datetime.pyi index 66f242613290..fcf4127b3218 100644 --- a/stdlib/datetime.pyi +++ b/stdlib/datetime.pyi @@ -435,7 +435,7 @@ class datetime(date, Generic[_TzInfoT]): def astimezone(self, tz: _TzInfo | None = None) -> Self: ... def isoformat(self, sep: str = "T", timespec: str = "auto") -> str: ... @classmethod - def strptime(cls, date_string: str, format: str, /) -> datetime[_TzInfo | None]: ... + def strptime(cls, date_string: str, format: str, /) -> datetime[_TzInfo | None]: ... # type: ignore[override] def utcoffset(self) -> timedelta | None: ... def tzname(self) -> str | None: ... def dst(self) -> timedelta | None: ... From 4974026e7162a2cc411ad0aaada83ce72d8322b8 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Mon, 10 Nov 2025 11:55:23 +0100 Subject: [PATCH 22/23] Restore changes to check_unittest --- stdlib/@tests/test_cases/check_unittest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/@tests/test_cases/check_unittest.py b/stdlib/@tests/test_cases/check_unittest.py index bbc2a2ba4843..fa4fc112002a 100644 --- a/stdlib/@tests/test_cases/check_unittest.py +++ b/stdlib/@tests/test_cases/check_unittest.py @@ -28,7 +28,7 @@ case.assertAlmostEqual(2.4, 2.41, places=9, delta=0.02) # type: ignore case.assertAlmostEqual("foo", "bar") # type: ignore -case.assertAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1)) # type: ignore +case.assertAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1)) case.assertAlmostEqual(Decimal("0.4"), Fraction(1, 2)) # type: ignore case.assertAlmostEqual(complex(2, 3), Decimal("0.9")) # type: ignore @@ -44,7 +44,7 @@ case.assertNotAlmostEqual(2.4, 2.41, places=9, delta=0.02) # type: ignore case.assertNotAlmostEqual("foo", "bar") # type: ignore -case.assertNotAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1)) # type: ignore +case.assertNotAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1)) case.assertNotAlmostEqual(Decimal("0.4"), Fraction(1, 2)) # type: ignore case.assertNotAlmostEqual(complex(2, 3), Decimal("0.9")) # type: ignore From d7b69b16d0028e5557d598ccb77ed28e3937c613 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Mon, 10 Nov 2025 11:56:14 +0100 Subject: [PATCH 23/23] Fix grouping --- stdlib/@tests/test_cases/check_unittest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/@tests/test_cases/check_unittest.py b/stdlib/@tests/test_cases/check_unittest.py index fa4fc112002a..977b940fd61e 100644 --- a/stdlib/@tests/test_cases/check_unittest.py +++ b/stdlib/@tests/test_cases/check_unittest.py @@ -19,6 +19,7 @@ case.assertAlmostEqual(2.4, 2.41) case.assertAlmostEqual(Fraction(49, 50), Fraction(48, 50)) case.assertAlmostEqual(3.14, complex(5, 6)) +case.assertAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1)) case.assertAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1), delta=timedelta(hours=1)) case.assertAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1), None, "foo", timedelta(hours=1)) case.assertAlmostEqual(Decimal("1.1"), Decimal("1.11")) @@ -28,7 +29,6 @@ case.assertAlmostEqual(2.4, 2.41, places=9, delta=0.02) # type: ignore case.assertAlmostEqual("foo", "bar") # type: ignore -case.assertAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1)) case.assertAlmostEqual(Decimal("0.4"), Fraction(1, 2)) # type: ignore case.assertAlmostEqual(complex(2, 3), Decimal("0.9")) # type: ignore @@ -39,12 +39,12 @@ case.assertAlmostEqual(1, 2.4) case.assertNotAlmostEqual(Fraction(49, 50), Fraction(48, 50)) case.assertAlmostEqual(3.14, complex(5, 6)) +case.assertNotAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1)) case.assertNotAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1), delta=timedelta(hours=1)) case.assertNotAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1), None, "foo", timedelta(hours=1)) case.assertNotAlmostEqual(2.4, 2.41, places=9, delta=0.02) # type: ignore case.assertNotAlmostEqual("foo", "bar") # type: ignore -case.assertNotAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1)) case.assertNotAlmostEqual(Decimal("0.4"), Fraction(1, 2)) # type: ignore case.assertNotAlmostEqual(complex(2, 3), Decimal("0.9")) # type: ignore