Skip to content

ENH: Raise TypeError for offsets which are not supported as period frequency #55844

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
1 change: 1 addition & 0 deletions doc/source/whatsnew/v2.2.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ Other enhancements
- :meth:`ExtensionArray.duplicated` added to allow extension type implementations of the ``duplicated`` method (:issue:`55255`)
- DataFrame.apply now allows the usage of numba (via ``engine="numba"``) to JIT compile the passed function, allowing for potential speedups (:issue:`54666`)
- Implement masked algorithms for :meth:`Series.value_counts` (:issue:`54984`)
- Improved error message when constructing :class:`Period` with invalid offsets such as "QS" (:issue:`55785`)

.. ---------------------------------------------------------------------------
.. _whatsnew_220.notable_bug_fixes:
Expand Down
2 changes: 1 addition & 1 deletion pandas/_libs/tslibs/offsets.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -4177,7 +4177,7 @@ cdef class CustomBusinessDay(BusinessDay):
def _period_dtype_code(self):
# GH#52534
raise TypeError(
"CustomBusinessDay cannot be used with Period or PeriodDtype"
"CustomBusinessDay is not supported as period frequency"
)

_apply_array = BaseOffset._apply_array
Expand Down
10 changes: 9 additions & 1 deletion pandas/_libs/tslibs/period.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -2726,6 +2726,14 @@ class Period(_Period):

if freq is not None:
freq = cls._maybe_convert_freq(freq)
try:
period_dtype_code = freq._period_dtype_code
except (AttributeError, TypeError):
# AttributeError: _period_dtype_code might not exist
# TypeError: _period_dtype_code might intentionally raise
raise TypeError(
f"{(type(freq).__name__)} is not supported as period frequency"
)
nanosecond = 0

if ordinal is not None and value is not None:
Expand Down Expand Up @@ -2758,7 +2766,7 @@ class Period(_Period):

elif is_period_object(value):
other = value
if freq is None or freq._period_dtype_code == other._dtype._dtype_code:
if freq is None or period_dtype_code == other._dtype._dtype_code:
ordinal = other.ordinal
freq = other.freq
else:
Expand Down
2 changes: 1 addition & 1 deletion pandas/tests/dtypes/test_dtypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@ def test_construction(self):

def test_cannot_use_custom_businessday(self):
# GH#52534
msg = "CustomBusinessDay cannot be used with Period or PeriodDtype"
msg = "CustomBusinessDay is not supported as period frequency"
msg2 = r"PeriodDtype\[B\] is deprecated"
with pytest.raises(TypeError, match=msg):
with tm.assert_produces_warning(FutureWarning, match=msg2):
Expand Down
15 changes: 15 additions & 0 deletions pandas/tests/indexes/period/test_constructors.py
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,21 @@ def test_map_with_string_constructor(self):
# lastly, values should compare equal
tm.assert_index_equal(res, expected)

@pytest.mark.parametrize(
"freq, freq_msg",
[
(offsets.BYearBegin(), "BYearBegin"),
(offsets.YearBegin(2), "YearBegin"),
(offsets.QuarterBegin(startingMonth=12), "QuarterBegin"),
(offsets.BusinessMonthEnd(2), "BusinessMonthEnd"),
],
)
def test_offsets_not_supported(self, freq, freq_msg):
# GH#55785
msg = f"{freq_msg} is not supported as period frequency"
with pytest.raises(TypeError, match=msg):
Period(year=2014, freq=freq)


class TestShallowCopy:
def test_shallow_copy_empty(self):
Expand Down
3 changes: 2 additions & 1 deletion pandas/tests/scalar/period/test_asfreq.py
Original file line number Diff line number Diff line change
Expand Up @@ -825,7 +825,8 @@ def test_asfreq_MS(self):
with pytest.raises(ValueError, match=msg):
initial.asfreq(freq="MS", how="S")

with pytest.raises(ValueError, match=msg):
msg = "MonthBegin is not supported as period frequency"
with pytest.raises(TypeError, match=msg):
Period("2013-01", "MS")

assert _period_code_map.get("MS") is None
6 changes: 3 additions & 3 deletions pandas/tests/scalar/period/test_period.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
class TestPeriodConstruction:
def test_custom_business_day_freq_raises(self):
# GH#52534
msg = "CustomBusinessDay cannot be used with Period or PeriodDtype"
msg = "CustomBusinessDay is not supported as period frequency"
with pytest.raises(TypeError, match=msg):
Period("2023-04-10", freq="C")
with pytest.raises(TypeError, match=msg):
Expand Down Expand Up @@ -1628,8 +1628,8 @@ def test_negone_ordinals():


def test_invalid_frequency_error_message():
msg = "Invalid frequency: <WeekOfMonth: week=0, weekday=0>"
with pytest.raises(ValueError, match=msg):
msg = "WeekOfMonth is not supported as period frequency"
with pytest.raises(TypeError, match=msg):
Period("2012-01-02", freq="WOM-1MON")


Expand Down