Skip to content

Commit 773e808

Browse files
committed
don't make NaTType a subclass of datetime
1 parent 031397c commit 773e808

File tree

7 files changed

+45
-74
lines changed

7 files changed

+45
-74
lines changed

pandas/_libs/tslibs/nattype.pyi

Lines changed: 26 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ from datetime import (
33
timedelta,
44
tzinfo as _tzinfo,
55
)
6-
from typing import Any
6+
from typing import (
7+
Any,
8+
Union,
9+
)
710

811
import numpy as np
912

@@ -15,7 +18,12 @@ nat_strings: set[str]
1518

1619
def is_null_datetimelike(val: object, inat_is_null: bool = ...) -> bool: ...
1720

18-
class NaTType(datetime):
21+
_NaTComparisonTypes = Union[datetime, timedelta, Period, np.datetime64, np.timedelta64]
22+
23+
class _NatComparison:
24+
def __call__(self, other: _NaTComparisonTypes) -> bool: ...
25+
26+
class NaTType:
1927
value: np.int64
2028
def asm8(self) -> np.datetime64: ...
2129
def to_datetime64(self) -> np.datetime64: ...
@@ -54,26 +62,19 @@ class NaTType(datetime):
5462
def weekofyear(self) -> float: ...
5563
def day_name(self) -> float: ...
5664
def month_name(self) -> float: ...
57-
# error: Return type "float" of "weekday" incompatible with return
58-
# type "int" in supertype "date"
59-
def weekday(self) -> float: ... # type: ignore[override]
60-
# error: Return type "float" of "isoweekday" incompatible with return
61-
# type "int" in supertype "date"
62-
def isoweekday(self) -> float: ... # type: ignore[override]
65+
def weekday(self) -> float: ...
66+
def isoweekday(self) -> float: ...
6367
def total_seconds(self) -> float: ...
64-
# error: Signature of "today" incompatible with supertype "datetime"
65-
def today(self, *args, **kwargs) -> NaTType: ... # type: ignore[override]
66-
# error: Signature of "today" incompatible with supertype "datetime"
67-
def now(self, *args, **kwargs) -> NaTType: ... # type: ignore[override]
68+
def today(self, *args, **kwargs) -> NaTType: ...
69+
def now(self, *args, **kwargs) -> NaTType: ...
6870
def to_pydatetime(self) -> NaTType: ...
6971
def date(self) -> NaTType: ...
7072
def round(self) -> NaTType: ...
7173
def floor(self) -> NaTType: ...
7274
def ceil(self) -> NaTType: ...
7375
def tz_convert(self) -> NaTType: ...
7476
def tz_localize(self) -> NaTType: ...
75-
# error: Signature of "replace" incompatible with supertype "datetime"
76-
def replace( # type: ignore[override]
77+
def replace(
7778
self,
7879
year: int | None = ...,
7980
month: int | None = ...,
@@ -86,38 +87,24 @@ class NaTType(datetime):
8687
tzinfo: _tzinfo | None = ...,
8788
fold: int | None = ...,
8889
) -> NaTType: ...
89-
# error: Return type "float" of "year" incompatible with return
90-
# type "int" in supertype "date"
9190
@property
92-
def year(self) -> float: ... # type: ignore[override]
91+
def year(self) -> float: ...
9392
@property
9493
def quarter(self) -> float: ...
95-
# error: Return type "float" of "month" incompatible with return
96-
# type "int" in supertype "date"
9794
@property
98-
def month(self) -> float: ... # type: ignore[override]
99-
# error: Return type "float" of "day" incompatible with return
100-
# type "int" in supertype "date"
95+
def month(self) -> float: ...
10196
@property
102-
def day(self) -> float: ... # type: ignore[override]
103-
# error: Return type "float" of "hour" incompatible with return
104-
# type "int" in supertype "date"
97+
def day(self) -> float: ...
10598
@property
106-
def hour(self) -> float: ... # type: ignore[override]
107-
# error: Return type "float" of "minute" incompatible with return
108-
# type "int" in supertype "date"
99+
def hour(self) -> float: ...
109100
@property
110-
def minute(self) -> float: ... # type: ignore[override]
111-
# error: Return type "float" of "second" incompatible with return
112-
# type "int" in supertype "date"
101+
def minute(self) -> float: ...
113102
@property
114-
def second(self) -> float: ... # type: ignore[override]
103+
def second(self) -> float: ...
115104
@property
116105
def millisecond(self) -> float: ...
117-
# error: Return type "float" of "microsecond" incompatible with return
118-
# type "int" in supertype "date"
119106
@property
120-
def microsecond(self) -> float: ... # type: ignore[override]
107+
def microsecond(self) -> float: ...
121108
@property
122109
def nanosecond(self) -> float: ...
123110
# inject Timedelta properties
@@ -132,24 +119,7 @@ class NaTType(datetime):
132119
def qyear(self) -> float: ...
133120
def __eq__(self, other: Any) -> bool: ...
134121
def __ne__(self, other: Any) -> bool: ...
135-
# https://github.com/python/mypy/issues/9015
136-
# error: Argument 1 of "__lt__" is incompatible with supertype "date";
137-
# supertype defines the argument type as "date"
138-
def __lt__( # type: ignore[override]
139-
self, other: datetime | timedelta | Period | np.datetime64 | np.timedelta64
140-
) -> bool: ...
141-
# error: Argument 1 of "__le__" is incompatible with supertype "date";
142-
# supertype defines the argument type as "date"
143-
def __le__( # type: ignore[override]
144-
self, other: datetime | timedelta | Period | np.datetime64 | np.timedelta64
145-
) -> bool: ...
146-
# error: Argument 1 of "__gt__" is incompatible with supertype "date";
147-
# supertype defines the argument type as "date"
148-
def __gt__( # type: ignore[override]
149-
self, other: datetime | timedelta | Period | np.datetime64 | np.timedelta64
150-
) -> bool: ...
151-
# error: Argument 1 of "__ge__" is incompatible with supertype "date";
152-
# supertype defines the argument type as "date"
153-
def __ge__( # type: ignore[override]
154-
self, other: datetime | timedelta | Period | np.datetime64 | np.timedelta64
155-
) -> bool: ...
122+
__lt__: _NatComparison
123+
__le__: _NatComparison
124+
__gt__: _NatComparison
125+
__ge__: _NatComparison

pandas/_libs/tslibs/timedeltas.pyi

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,7 @@ from typing import (
77
)
88

99
import numpy as np
10-
11-
from pandas._libs.tslibs import (
12-
NaTType,
13-
Tick,
14-
)
15-
from pandas._typing import npt
10+
import numpy.typing as npt
1611

1712
_S = TypeVar("_S", bound=timedelta)
1813

@@ -26,7 +21,7 @@ def array_to_timedelta64(
2621
errors: str = ...,
2722
) -> np.ndarray: ... # np.ndarray[m8ns]
2823
def parse_timedelta_unit(unit: str | None) -> str: ...
29-
def delta_to_nanoseconds(delta: Tick | np.timedelta64 | timedelta | int) -> int: ...
24+
def delta_to_nanoseconds(delta: np.timedelta64 | timedelta | int) -> int: ...
3025

3126
class Timedelta(timedelta):
3227
min: ClassVar[Timedelta]

pandas/_libs/tslibs/timestamps.pyi

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,10 +144,10 @@ class Timestamp(datetime):
144144
def __add__(self: _DatetimeT, other: timedelta | np.timedelta64) -> _DatetimeT: ...
145145
def __radd__(self: _DatetimeT, other: timedelta) -> _DatetimeT: ...
146146
@overload # type: ignore
147-
def __sub__(self, other: datetime) -> timedelta: ...
147+
def __sub__(self, other: datetime) -> Timedelta: ...
148148
@overload
149149
# TODO: other can also be Tick (but it cannot be resolved)
150-
def __sub__(self, other: timedelta | np.timedelta64) -> datetime: ...
150+
def __sub__(self: _DatetimeT, other: timedelta | np.timedelta64) -> _DatetimeT: ...
151151
def __hash__(self) -> int: ...
152152
def weekday(self) -> int: ...
153153
def isoweekday(self) -> int: ...
@@ -206,3 +206,5 @@ class Timestamp(datetime):
206206
def to_numpy(
207207
self, dtype: np.dtype | None = ..., copy: bool = ...
208208
) -> np.datetime64: ...
209+
@property
210+
def _date_repr(self) -> str: ...

pandas/core/arrays/datetimes.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -775,7 +775,6 @@ def _add_offset(self, offset) -> DatetimeArray:
775775
def _sub_datetimelike_scalar(self, other):
776776
# subtract a datetime from myself, yielding a ndarray[timedelta64[ns]]
777777
assert isinstance(other, (datetime, np.datetime64))
778-
assert other is not NaT
779778
other = Timestamp(other)
780779
# error: Non-overlapping identity check (left operand type: "Timestamp",
781780
# right operand type: "NaTType")

pandas/io/formats/format.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1767,16 +1767,13 @@ def _format_datetime64_dateonly(
17671767
nat_rep: str = "NaT",
17681768
date_format: str | None = None,
17691769
) -> str:
1770-
if x is NaT:
1770+
if x is NaT or isinstance(x, NaTType):
17711771
return nat_rep
17721772

17731773
if date_format:
17741774
return x.strftime(date_format)
17751775
else:
1776-
# error: Item "NaTType" of "Union[NaTType, Any]" has no attribute "_date_repr"
1777-
# The underlying problem here is that mypy doesn't understand that NaT
1778-
# is a singleton, so that the check above excludes it here.
1779-
return x._date_repr # type: ignore[union-attr]
1776+
return x._date_repr
17801777

17811778

17821779
def get_format_datetime64(

pandas/io/sas/sas_xport.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import numpy as np
1818

19+
from pandas._libs.tslibs import NaTType
1920
from pandas._typing import (
2021
FilePath,
2122
ReadBuffer,
@@ -139,7 +140,7 @@
139140
"""
140141

141142

142-
def _parse_date(datestr: str) -> datetime:
143+
def _parse_date(datestr: str) -> datetime | NaTType:
143144
"""Given a date in xport format, return Python date."""
144145
try:
145146
# e.g. "16FEB11:10:07:55"

pandas/tests/resample/test_datetime_index.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
from datetime import datetime
22
from functools import partial
33
from io import StringIO
4+
from typing import (
5+
List,
6+
Union,
7+
)
48

59
import numpy as np
610
import pytest
711
import pytz
812

913
from pandas._libs import lib
14+
from pandas._libs.tslibs import NaTType
1015
from pandas.errors import UnsupportedFunctionCall
1116

1217
import pandas as pd
@@ -1286,7 +1291,7 @@ def test_resample_consistency():
12861291
tm.assert_series_equal(s10_2, rl)
12871292

12881293

1289-
dates1 = [
1294+
dates1: List[Union[datetime, NaTType]] = [
12901295
datetime(2014, 10, 1),
12911296
datetime(2014, 9, 3),
12921297
datetime(2014, 11, 5),
@@ -1295,7 +1300,9 @@ def test_resample_consistency():
12951300
datetime(2014, 7, 15),
12961301
]
12971302

1298-
dates2 = dates1[:2] + [pd.NaT] + dates1[2:4] + [pd.NaT] + dates1[4:]
1303+
dates2: List[Union[datetime, NaTType]] = (
1304+
dates1[:2] + [pd.NaT] + dates1[2:4] + [pd.NaT] + dates1[4:]
1305+
)
12991306
dates3 = [pd.NaT] + dates1 + [pd.NaT] # type: ignore[operator]
13001307

13011308

0 commit comments

Comments
 (0)