Skip to content

Commit b8fc8cd

Browse files
authored
ENH: Raise TypeError for offsets which are not supported as period frequency (#55844)
* raise ValueError for offsets which are not supported as period frequency * correct error message in test_asfreq_MS * simplify the condition for ValueError * simlify the condition, correct tests * change the condition and Error class in Period constructor * add a note to v2.2.0 * change note in v2.2.0
1 parent 1307608 commit b8fc8cd

File tree

7 files changed

+32
-7
lines changed

7 files changed

+32
-7
lines changed

doc/source/whatsnew/v2.2.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ Other enhancements
103103
- :meth:`ExtensionArray.duplicated` added to allow extension type implementations of the ``duplicated`` method (:issue:`55255`)
104104
- DataFrame.apply now allows the usage of numba (via ``engine="numba"``) to JIT compile the passed function, allowing for potential speedups (:issue:`54666`)
105105
- Implement masked algorithms for :meth:`Series.value_counts` (:issue:`54984`)
106+
- Improved error message when constructing :class:`Period` with invalid offsets such as "QS" (:issue:`55785`)
106107

107108
.. ---------------------------------------------------------------------------
108109
.. _whatsnew_220.notable_bug_fixes:

pandas/_libs/tslibs/offsets.pyx

+1-1
Original file line numberDiff line numberDiff line change
@@ -4177,7 +4177,7 @@ cdef class CustomBusinessDay(BusinessDay):
41774177
def _period_dtype_code(self):
41784178
# GH#52534
41794179
raise TypeError(
4180-
"CustomBusinessDay cannot be used with Period or PeriodDtype"
4180+
"CustomBusinessDay is not supported as period frequency"
41814181
)
41824182

41834183
_apply_array = BaseOffset._apply_array

pandas/_libs/tslibs/period.pyx

+9-1
Original file line numberDiff line numberDiff line change
@@ -2726,6 +2726,14 @@ class Period(_Period):
27262726

27272727
if freq is not None:
27282728
freq = cls._maybe_convert_freq(freq)
2729+
try:
2730+
period_dtype_code = freq._period_dtype_code
2731+
except (AttributeError, TypeError):
2732+
# AttributeError: _period_dtype_code might not exist
2733+
# TypeError: _period_dtype_code might intentionally raise
2734+
raise TypeError(
2735+
f"{(type(freq).__name__)} is not supported as period frequency"
2736+
)
27292737
nanosecond = 0
27302738

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

27592767
elif is_period_object(value):
27602768
other = value
2761-
if freq is None or freq._period_dtype_code == other._dtype._dtype_code:
2769+
if freq is None or period_dtype_code == other._dtype._dtype_code:
27622770
ordinal = other.ordinal
27632771
freq = other.freq
27642772
else:

pandas/tests/dtypes/test_dtypes.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -444,7 +444,7 @@ def test_construction(self):
444444

445445
def test_cannot_use_custom_businessday(self):
446446
# GH#52534
447-
msg = "CustomBusinessDay cannot be used with Period or PeriodDtype"
447+
msg = "CustomBusinessDay is not supported as period frequency"
448448
msg2 = r"PeriodDtype\[B\] is deprecated"
449449
with pytest.raises(TypeError, match=msg):
450450
with tm.assert_produces_warning(FutureWarning, match=msg2):

pandas/tests/indexes/period/test_constructors.py

+15
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,21 @@ def test_map_with_string_constructor(self):
553553
# lastly, values should compare equal
554554
tm.assert_index_equal(res, expected)
555555

556+
@pytest.mark.parametrize(
557+
"freq, freq_msg",
558+
[
559+
(offsets.BYearBegin(), "BYearBegin"),
560+
(offsets.YearBegin(2), "YearBegin"),
561+
(offsets.QuarterBegin(startingMonth=12), "QuarterBegin"),
562+
(offsets.BusinessMonthEnd(2), "BusinessMonthEnd"),
563+
],
564+
)
565+
def test_offsets_not_supported(self, freq, freq_msg):
566+
# GH#55785
567+
msg = f"{freq_msg} is not supported as period frequency"
568+
with pytest.raises(TypeError, match=msg):
569+
Period(year=2014, freq=freq)
570+
556571

557572
class TestShallowCopy:
558573
def test_shallow_copy_empty(self):

pandas/tests/scalar/period/test_asfreq.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -825,7 +825,8 @@ def test_asfreq_MS(self):
825825
with pytest.raises(ValueError, match=msg):
826826
initial.asfreq(freq="MS", how="S")
827827

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

831832
assert _period_code_map.get("MS") is None

pandas/tests/scalar/period/test_period.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
class TestPeriodConstruction:
3939
def test_custom_business_day_freq_raises(self):
4040
# GH#52534
41-
msg = "CustomBusinessDay cannot be used with Period or PeriodDtype"
41+
msg = "CustomBusinessDay is not supported as period frequency"
4242
with pytest.raises(TypeError, match=msg):
4343
Period("2023-04-10", freq="C")
4444
with pytest.raises(TypeError, match=msg):
@@ -1628,8 +1628,8 @@ def test_negone_ordinals():
16281628

16291629

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

16351635

0 commit comments

Comments
 (0)