Skip to content

Commit 622c92d

Browse files
authoredMar 8, 2022
TYP: remove NaTType as possible result of Timestamp and Timedelta constructor (#46171)
1 parent 3b795d9 commit 622c92d

File tree

8 files changed

+62
-77
lines changed

8 files changed

+62
-77
lines changed
 

‎pandas/_libs/tslibs/nattype.pyi

+26-56
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

+7-6
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ from typing import (
77
)
88

99
import numpy as np
10+
import numpy.typing as npt
1011

1112
from pandas._libs.tslibs import (
1213
NaTType,
1314
Tick,
1415
)
15-
from pandas._typing import npt
1616

1717
_S = TypeVar("_S", bound=timedelta)
1818

@@ -26,21 +26,22 @@ def array_to_timedelta64(
2626
errors: str = ...,
2727
) -> np.ndarray: ... # np.ndarray[m8ns]
2828
def parse_timedelta_unit(unit: str | None) -> str: ...
29-
def delta_to_nanoseconds(delta: Tick | np.timedelta64 | timedelta | int) -> int: ...
29+
def delta_to_nanoseconds(delta: np.timedelta64 | timedelta | Tick) -> int: ...
3030

3131
class Timedelta(timedelta):
3232
min: ClassVar[Timedelta]
3333
max: ClassVar[Timedelta]
3434
resolution: ClassVar[Timedelta]
3535
value: int # np.int64
36-
37-
# error: "__new__" must return a class instance (got "Union[Timedelta, NaTType]")
38-
def __new__( # type: ignore[misc]
36+
def __new__(
3937
cls: Type[_S],
4038
value=...,
4139
unit: str = ...,
4240
**kwargs: int | float | np.integer | np.floating,
43-
) -> _S | NaTType: ...
41+
) -> _S: ...
42+
# GH 46171
43+
# While Timedelta can return pd.NaT, having the constructor return
44+
# a Union with NaTType makes things awkward for users of pandas
4445
@property
4546
def days(self) -> int: ...
4647
@property

‎pandas/_libs/tslibs/timestamps.pyi

+11-6
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,7 @@ class Timestamp(datetime):
3232

3333
resolution: ClassVar[Timedelta]
3434
value: int # np.int64
35-
36-
# error: "__new__" must return a class instance (got "Union[Timestamp, NaTType]")
37-
def __new__( # type: ignore[misc]
35+
def __new__(
3836
cls: type[_DatetimeT],
3937
ts_input: int
4038
| np.integer
@@ -57,7 +55,10 @@ class Timestamp(datetime):
5755
tzinfo: _tzinfo | None = ...,
5856
*,
5957
fold: int | None = ...,
60-
) -> _DatetimeT | NaTType: ...
58+
) -> _DatetimeT: ...
59+
# GH 46171
60+
# While Timestamp can return pd.NaT, having the constructor return
61+
# a Union with NaTType makes things awkward for users of pandas
6162
def _set_freq(self, freq: BaseOffset | None) -> None: ...
6263
@property
6364
def year(self) -> int: ...
@@ -145,9 +146,11 @@ class Timestamp(datetime):
145146
) -> _DatetimeT: ...
146147
def __radd__(self: _DatetimeT, other: timedelta) -> _DatetimeT: ...
147148
@overload # type: ignore
148-
def __sub__(self, other: datetime) -> timedelta: ...
149+
def __sub__(self, other: datetime) -> Timedelta: ...
149150
@overload
150-
def __sub__(self, other: timedelta | np.timedelta64 | Tick) -> datetime: ...
151+
def __sub__(
152+
self: _DatetimeT, other: timedelta | np.timedelta64 | Tick
153+
) -> _DatetimeT: ...
151154
def __hash__(self) -> int: ...
152155
def weekday(self) -> int: ...
153156
def isoweekday(self) -> int: ...
@@ -206,3 +209,5 @@ class Timestamp(datetime):
206209
def to_numpy(
207210
self, dtype: np.dtype | None = ..., copy: bool = ...
208211
) -> np.datetime64: ...
212+
@property
213+
def _date_repr(self) -> str: ...

‎pandas/_typing.py

+5
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import numpy.typing as npt
3636

3737
from pandas._libs import (
38+
NaTType,
3839
Period,
3940
Timedelta,
4041
Timestamp,
@@ -308,3 +309,7 @@ def closed(self) -> bool:
308309
# Interval closed type
309310

310311
IntervalClosedType = Literal["left", "right", "both", "neither"]
312+
313+
# datetime and NaTType
314+
315+
DatetimeNaTType = Union[datetime, "NaTType"]

‎pandas/core/arrays/datetimes.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -777,7 +777,9 @@ def _add_offset(self, offset) -> DatetimeArray:
777777
def _sub_datetimelike_scalar(self, other):
778778
# subtract a datetime from myself, yielding a ndarray[timedelta64[ns]]
779779
assert isinstance(other, (datetime, np.datetime64))
780-
assert other is not NaT
780+
# error: Non-overlapping identity check (left operand type: "Union[datetime,
781+
# datetime64]", right operand type: "NaTType") [comparison-overlap]
782+
assert other is not NaT # type: ignore[comparison-overlap]
781783
other = Timestamp(other)
782784
# error: Non-overlapping identity check (left operand type: "Timestamp",
783785
# right operand type: "NaTType")

‎pandas/io/formats/format.py

+2-5
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 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

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import numpy as np
1818

1919
from pandas._typing import (
20+
DatetimeNaTType,
2021
FilePath,
2122
ReadBuffer,
2223
)
@@ -139,7 +140,7 @@
139140
"""
140141

141142

142-
def _parse_date(datestr: str) -> datetime:
143+
def _parse_date(datestr: str) -> DatetimeNaTType:
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

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
from datetime import datetime
22
from functools import partial
33
from io import StringIO
4+
from typing import List
45

56
import numpy as np
67
import pytest
78
import pytz
89

910
from pandas._libs import lib
11+
from pandas._typing import DatetimeNaTType
1012
from pandas.errors import UnsupportedFunctionCall
1113

1214
import pandas as pd
@@ -1286,7 +1288,7 @@ def test_resample_consistency():
12861288
tm.assert_series_equal(s10_2, rl)
12871289

12881290

1289-
dates1 = [
1291+
dates1: List[DatetimeNaTType] = [
12901292
datetime(2014, 10, 1),
12911293
datetime(2014, 9, 3),
12921294
datetime(2014, 11, 5),
@@ -1295,7 +1297,9 @@ def test_resample_consistency():
12951297
datetime(2014, 7, 15),
12961298
]
12971299

1298-
dates2 = dates1[:2] + [pd.NaT] + dates1[2:4] + [pd.NaT] + dates1[4:]
1300+
dates2: List[DatetimeNaTType] = (
1301+
dates1[:2] + [pd.NaT] + dates1[2:4] + [pd.NaT] + dates1[4:]
1302+
)
12991303
dates3 = [pd.NaT] + dates1 + [pd.NaT] # type: ignore[operator]
13001304

13011305

0 commit comments

Comments
 (0)
Please sign in to comment.