Skip to content

Commit b1d3a15

Browse files
mroeschkeYi Wei
authored and
Yi Wei
committed
BUG: PeriodDtype incorrectly caching different DateOffset freqs (pandas-dev#52991)
* BUG: PeriodDtype incorrectly caching different DateOffset freqs * Change typing * Address review * Add more specific typing and error message
1 parent c9a2f23 commit b1d3a15

File tree

3 files changed

+15
-4
lines changed

3 files changed

+15
-4
lines changed

doc/source/whatsnew/v2.1.0.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -376,12 +376,12 @@ Period
376376
^^^^^^
377377
- :meth:`PeriodIndex.map` with ``na_action="ignore"`` now works as expected (:issue:`51644`)
378378
- Bug in :class:`PeriodDtype` constructor failing to raise ``TypeError`` when no argument is passed or when ``None`` is passed (:issue:`27388`)
379+
- Bug in :class:`PeriodDtype` constructor incorrectly returning the same ``normalize`` for different :class:`DateOffset` ``freq`` inputs (:issue:`24121`)
379380
- Bug in :class:`PeriodDtype` constructor raising ``ValueError`` instead of ``TypeError`` when an invalid type is passed (:issue:`51790`)
380381
- Bug in :func:`read_csv` not processing empty strings as a null value, with ``engine="pyarrow"`` (:issue:`52087`)
381382
- Bug in :func:`read_csv` returning ``object`` dtype columns instead of ``float64`` dtype columns with ``engine="pyarrow"`` for columns that are all null with ``engine="pyarrow"`` (:issue:`52087`)
382383
- Bug in :meth:`arrays.PeriodArray.map` and :meth:`PeriodIndex.map`, where the supplied callable operated array-wise instead of element-wise (:issue:`51977`)
383384
- Bug in incorrectly allowing construction of :class:`Period` or :class:`PeriodDtype` with :class:`CustomBusinessDay` freq; use :class:`BusinessDay` instead (:issue:`52534`)
384-
-
385385

386386
Plotting
387387
^^^^^^^^

pandas/core/dtypes/dtypes.py

+6-3
Original file line numberDiff line numberDiff line change
@@ -899,7 +899,10 @@ class PeriodDtype(PeriodDtypeBase, PandasExtensionDtype):
899899
num = 102
900900
_metadata = ("freq",)
901901
_match = re.compile(r"(P|p)eriod\[(?P<freq>.+)\]")
902-
_cache_dtypes: dict[str_type, PandasExtensionDtype] = {}
902+
# error: Incompatible types in assignment (expression has type
903+
# "Dict[int, PandasExtensionDtype]", base class "PandasExtensionDtype"
904+
# defined the type as "Dict[str, PandasExtensionDtype]") [assignment]
905+
_cache_dtypes: dict[BaseOffset, PeriodDtype] = {} # type: ignore[assignment] # noqa:E501
903906
__hash__ = PeriodDtypeBase.__hash__
904907
_freq: BaseOffset
905908

@@ -916,12 +919,12 @@ def __new__(cls, freq):
916919
freq = cls._parse_dtype_strict(freq)
917920

918921
try:
919-
return cls._cache_dtypes[freq.freqstr]
922+
return cls._cache_dtypes[freq]
920923
except KeyError:
921924
dtype_code = freq._period_dtype_code
922925
u = PeriodDtypeBase.__new__(cls, dtype_code, freq.n)
923926
u._freq = freq
924-
cls._cache_dtypes[freq.freqstr] = u
927+
cls._cache_dtypes[freq] = u
925928
return u
926929

927930
def __reduce__(self):

pandas/tests/dtypes/test_dtypes.py

+8
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,14 @@ def test_not_string(self):
559559
# though PeriodDtype has object kind, it cannot be string
560560
assert not is_string_dtype(PeriodDtype("D"))
561561

562+
def test_perioddtype_caching_dateoffset_normalize(self):
563+
# GH 24121
564+
per_d = PeriodDtype(pd.offsets.YearEnd(normalize=True))
565+
assert per_d.freq.normalize
566+
567+
per_d2 = PeriodDtype(pd.offsets.YearEnd(normalize=False))
568+
assert not per_d2.freq.normalize
569+
562570

563571
class TestIntervalDtype(Base):
564572
@pytest.fixture

0 commit comments

Comments
 (0)