Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion doc/source/whatsnew/v2.1.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,8 @@ I/O

Period
^^^^^^
-
- Bug in :class:`PeriodDtype` constructor failing to raise ``TypeError`` when no argument is passed or when ``None`` is passed (:issue:`27388`)
- Bug in :class:`PeriodDtype` constructor raising ``ValueError`` instead of ``TypeError`` when an invalid type is passed (:issue:`51790`)
-

Plotting
Expand Down
16 changes: 6 additions & 10 deletions pandas/core/dtypes/dtypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -856,22 +856,15 @@ class PeriodDtype(PeriodDtypeBase, PandasExtensionDtype):
_match = re.compile(r"(P|p)eriod\[(?P<freq>.+)\]")
_cache_dtypes: dict[str_type, PandasExtensionDtype] = {}

def __new__(cls, freq=None):
def __new__(cls, freq):
"""
Parameters
----------
freq : frequency
freq : PeriodDtype, BaseOffset, or string
"""
if isinstance(freq, PeriodDtype):
return freq

elif freq is None:
# empty constructor for pickle compat
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you know if this comment is still relevant?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my assumption is that since it isn't in the test suite it should be OK

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this may break pre-0.19 pickle compat but that's fine IMO 6fa2b03

# -10_000 corresponds to PeriodDtypeCode.UNDEFINED
u = PeriodDtypeBase.__new__(cls, -10_000)
u._freq = None
return u

if not isinstance(freq, BaseOffset):
freq = cls._parse_dtype_strict(freq)

Expand Down Expand Up @@ -906,7 +899,10 @@ def _parse_dtype_strict(cls, freq: str_type) -> BaseOffset:
if freq_offset is not None:
return freq_offset

raise ValueError("could not construct PeriodDtype")
raise TypeError(
"PeriodDtype argument should be string or BaseOffet, "
f"got {type(freq).__name__}"
)

@classmethod
def construct_from_string(cls, string: str_type) -> PeriodDtype:
Expand Down
15 changes: 10 additions & 5 deletions pandas/tests/dtypes/test_dtypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -517,11 +517,16 @@ def test_basic(self, dtype):
assert not is_period_dtype(np.dtype("float64"))
assert not is_period_dtype(1.0)

def test_empty(self):
dt = PeriodDtype()
msg = "object has no attribute 'freqstr'"
with pytest.raises(AttributeError, match=msg):
str(dt)
def test_freq_argument_required(self):
# GH#27388
msg = "missing 1 required positional argument: 'freq'"
with pytest.raises(TypeError, match=msg):
PeriodDtype()

msg = "PeriodDtype argument should be string or BaseOffet, got NoneType"
with pytest.raises(TypeError, match=msg):
# GH#51790
PeriodDtype(None)

def test_not_string(self):
# though PeriodDtype has object kind, it cannot be string
Expand Down