Skip to content

Commit a49839d

Browse files
natmokvalmeeseeksmachine
authored andcommitted
Backport PR pandas-dev#56910: DEPR: lowercase freqs 'ye', 'qe', etc. raise a ValueError
1 parent 55c9fbd commit a49839d

File tree

8 files changed

+218
-53
lines changed

8 files changed

+218
-53
lines changed

pandas/_libs/tslibs/offsets.pyx

+56-35
Original file line numberDiff line numberDiff line change
@@ -4711,29 +4711,7 @@ _lite_rule_alias = {
47114711
"ns": "ns",
47124712
}
47134713

4714-
_dont_uppercase = {
4715-
"h",
4716-
"bh",
4717-
"cbh",
4718-
"MS",
4719-
"ms",
4720-
"s",
4721-
"me",
4722-
"qe",
4723-
"qe-dec",
4724-
"qe-jan",
4725-
"qe-feb",
4726-
"qe-mar",
4727-
"qe-apr",
4728-
"qe-may",
4729-
"qe-jun",
4730-
"qe-jul",
4731-
"qe-aug",
4732-
"qe-sep",
4733-
"qe-oct",
4734-
"qe-nov",
4735-
"ye",
4736-
}
4714+
_dont_uppercase = _dont_uppercase = {"h", "bh", "cbh", "MS", "ms", "s"}
47374715

47384716

47394717
INVALID_FREQ_ERR_MSG = "Invalid frequency: {0}"
@@ -4752,7 +4730,29 @@ def _get_offset(name: str) -> BaseOffset:
47524730
--------
47534731
_get_offset('EOM') --> BMonthEnd(1)
47544732
"""
4755-
if name.lower() not in _dont_uppercase:
4733+
if (
4734+
name not in _lite_rule_alias
4735+
and (name.upper() in _lite_rule_alias)
4736+
and name != "ms"
4737+
):
4738+
warnings.warn(
4739+
f"\'{name}\' is deprecated and will be removed "
4740+
f"in a future version, please use \'{name.upper()}\' instead.",
4741+
FutureWarning,
4742+
stacklevel=find_stack_level(),
4743+
)
4744+
elif (
4745+
name not in _lite_rule_alias
4746+
and (name.lower() in _lite_rule_alias)
4747+
and name != "MS"
4748+
):
4749+
warnings.warn(
4750+
f"\'{name}\' is deprecated and will be removed "
4751+
f"in a future version, please use \'{name.lower()}\' instead.",
4752+
FutureWarning,
4753+
stacklevel=find_stack_level(),
4754+
)
4755+
if name not in _dont_uppercase:
47564756
name = name.upper()
47574757
name = _lite_rule_alias.get(name, name)
47584758
name = _lite_rule_alias.get(name.lower(), name)
@@ -4845,7 +4845,7 @@ cpdef to_offset(freq, bint is_period=False):
48454845

48464846
tups = zip(split[0::4], split[1::4], split[2::4])
48474847
for n, (sep, stride, name) in enumerate(tups):
4848-
if is_period is False and name.upper() in c_OFFSET_DEPR_FREQSTR:
4848+
if not is_period and name.upper() in c_OFFSET_DEPR_FREQSTR:
48494849
warnings.warn(
48504850
f"\'{name}\' is deprecated and will be removed "
48514851
f"in a future version, please use "
@@ -4854,31 +4854,52 @@ cpdef to_offset(freq, bint is_period=False):
48544854
stacklevel=find_stack_level(),
48554855
)
48564856
name = c_OFFSET_DEPR_FREQSTR[name.upper()]
4857-
if is_period is True and name in c_REVERSE_OFFSET_DEPR_FREQSTR:
4858-
if name.startswith("Y"):
4857+
if (not is_period and
4858+
name != name.upper() and
4859+
name.lower() not in {"s", "ms", "us", "ns"} and
4860+
name.upper().split("-")[0].endswith(("S", "E"))):
4861+
warnings.warn(
4862+
f"\'{name}\' is deprecated and will be removed "
4863+
f"in a future version, please use "
4864+
f"\'{name.upper()}\' instead.",
4865+
FutureWarning,
4866+
stacklevel=find_stack_level(),
4867+
)
4868+
name = name.upper()
4869+
if is_period and name.upper() in c_REVERSE_OFFSET_DEPR_FREQSTR:
4870+
if name.upper().startswith("Y"):
48594871
raise ValueError(
4860-
f"for Period, please use \'Y{name[2:]}\' "
4872+
f"for Period, please use \'Y{name.upper()[2:]}\' "
48614873
f"instead of \'{name}\'"
48624874
)
4863-
if (name.startswith("B") or
4864-
name.startswith("S") or name.startswith("C")):
4875+
if (name.upper().startswith("B") or
4876+
name.upper().startswith("S") or
4877+
name.upper().startswith("C")):
48654878
raise ValueError(INVALID_FREQ_ERR_MSG.format(name))
48664879
else:
48674880
raise ValueError(
48684881
f"for Period, please use "
4869-
f"\'{c_REVERSE_OFFSET_DEPR_FREQSTR.get(name)}\' "
4882+
f"\'{c_REVERSE_OFFSET_DEPR_FREQSTR.get(name.upper())}\' "
48704883
f"instead of \'{name}\'"
48714884
)
4872-
elif is_period is True and name in c_OFFSET_DEPR_FREQSTR:
4873-
if name.startswith("A"):
4885+
elif is_period and name.upper() in c_OFFSET_DEPR_FREQSTR:
4886+
if name.upper().startswith("A"):
48744887
warnings.warn(
48754888
f"\'{name}\' is deprecated and will be removed in a future "
4876-
f"version, please use \'{c_DEPR_ABBREVS.get(name)}\' "
4889+
f"version, please use "
4890+
f"\'{c_DEPR_ABBREVS.get(name.upper())}\' instead.",
4891+
FutureWarning,
4892+
stacklevel=find_stack_level(),
4893+
)
4894+
if name.upper() != name:
4895+
warnings.warn(
4896+
f"\'{name}\' is deprecated and will be removed in "
4897+
f"a future version, please use \'{name.upper()}\' "
48774898
f"instead.",
48784899
FutureWarning,
48794900
stacklevel=find_stack_level(),
48804901
)
4881-
name = c_OFFSET_DEPR_FREQSTR.get(name)
4902+
name = c_OFFSET_DEPR_FREQSTR.get(name.upper())
48824903

48834904
if sep != "" and not sep.isspace():
48844905
raise ValueError("separator must be spaces")

pandas/tests/arrays/test_datetimes.py

+42
Original file line numberDiff line numberDiff line change
@@ -766,12 +766,18 @@ def test_iter_zoneinfo_fold(self, tz):
766766
"freq, freq_depr",
767767
[
768768
("2ME", "2M"),
769+
("2SME", "2SM"),
770+
("2SME", "2sm"),
769771
("2QE", "2Q"),
770772
("2QE-SEP", "2Q-SEP"),
771773
("1YE", "1Y"),
772774
("2YE-MAR", "2Y-MAR"),
773775
("1YE", "1A"),
774776
("2YE-MAR", "2A-MAR"),
777+
("2ME", "2m"),
778+
("2QE-SEP", "2q-sep"),
779+
("2YE-MAR", "2a-mar"),
780+
("2YE", "2y"),
775781
],
776782
)
777783
def test_date_range_frequency_M_Q_Y_A_deprecated(self, freq, freq_depr):
@@ -784,6 +790,42 @@ def test_date_range_frequency_M_Q_Y_A_deprecated(self, freq, freq_depr):
784790
result = pd.date_range("1/1/2000", periods=4, freq=freq_depr)
785791
tm.assert_index_equal(result, expected)
786792

793+
@pytest.mark.parametrize("freq_depr", ["2H", "2CBH", "2MIN", "2S", "2mS", "2Us"])
794+
def test_date_range_uppercase_frequency_deprecated(self, freq_depr):
795+
# GH#9586, GH#54939
796+
depr_msg = f"'{freq_depr[1:]}' is deprecated and will be removed in a "
797+
f"future version. Please use '{freq_depr.lower()[1:]}' instead."
798+
799+
expected = pd.date_range("1/1/2000", periods=4, freq=freq_depr.lower())
800+
with tm.assert_produces_warning(FutureWarning, match=depr_msg):
801+
result = pd.date_range("1/1/2000", periods=4, freq=freq_depr)
802+
tm.assert_index_equal(result, expected)
803+
804+
@pytest.mark.parametrize(
805+
"freq_depr",
806+
[
807+
"2ye-mar",
808+
"2ys",
809+
"2qe",
810+
"2qs-feb",
811+
"2bqs",
812+
"2sms",
813+
"2bms",
814+
"2cbme",
815+
"2me",
816+
"2w",
817+
],
818+
)
819+
def test_date_range_lowercase_frequency_deprecated(self, freq_depr):
820+
# GH#9586, GH#54939
821+
depr_msg = f"'{freq_depr[1:]}' is deprecated and will be removed in a "
822+
f"future version, please use '{freq_depr.upper()[1:]}' instead."
823+
824+
expected = pd.date_range("1/1/2000", periods=4, freq=freq_depr.upper())
825+
with tm.assert_produces_warning(FutureWarning, match=depr_msg):
826+
result = pd.date_range("1/1/2000", periods=4, freq=freq_depr)
827+
tm.assert_index_equal(result, expected)
828+
787829

788830
def test_factorize_sort_without_freq():
789831
dta = DatetimeArray._from_sequence([0, 2, 1], dtype="M8[ns]")

pandas/tests/indexes/datetimes/test_partial_slicing.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ def test_partial_slice_second_precision(self):
236236
rng = date_range(
237237
start=datetime(2005, 1, 1, 0, 0, 59, microsecond=999990),
238238
periods=20,
239-
freq="US",
239+
freq="us",
240240
)
241241
s = Series(np.arange(20), rng)
242242

pandas/tests/indexes/period/test_constructors.py

+11-4
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,12 @@ class TestPeriodIndexDisallowedFreqs:
2626
("2M", "2ME"),
2727
("2Q-MAR", "2QE-MAR"),
2828
("2Y-FEB", "2YE-FEB"),
29+
("2M", "2me"),
30+
("2Q-MAR", "2qe-MAR"),
31+
("2Y-FEB", "2yE-feb"),
2932
],
3033
)
31-
def test_period_index_frequency_ME_error_message(self, freq, freq_depr):
34+
def test_period_index_offsets_frequency_error_message(self, freq, freq_depr):
3235
# GH#52064
3336
msg = f"for Period, please use '{freq[1:]}' instead of '{freq_depr[1:]}'"
3437

@@ -38,7 +41,7 @@ def test_period_index_frequency_ME_error_message(self, freq, freq_depr):
3841
with pytest.raises(ValueError, match=msg):
3942
period_range(start="2020-01-01", end="2020-01-02", freq=freq_depr)
4043

41-
@pytest.mark.parametrize("freq_depr", ["2SME", "2CBME", "2BYE"])
44+
@pytest.mark.parametrize("freq_depr", ["2SME", "2sme", "2CBME", "2BYE", "2Bye"])
4245
def test_period_index_frequency_invalid_freq(self, freq_depr):
4346
# GH#9586
4447
msg = f"Invalid frequency: {freq_depr[1:]}"
@@ -547,7 +550,9 @@ def test_period_range_length(self):
547550
assert i1.freq == end_intv.freq
548551
assert i1[-1] == end_intv
549552

550-
end_intv = Period("2006-12-31", "1w")
553+
msg = "'w' is deprecated and will be removed in a future version."
554+
with tm.assert_produces_warning(FutureWarning, match=msg):
555+
end_intv = Period("2006-12-31", "1w")
551556
i2 = period_range(end=end_intv, periods=10)
552557
assert len(i1) == len(i2)
553558
assert (i1 == i2).all()
@@ -576,7 +581,9 @@ def test_mixed_freq_raises(self):
576581
with tm.assert_produces_warning(FutureWarning, match=msg):
577582
end_intv = Period("2005-05-01", "B")
578583

579-
vals = [end_intv, Period("2006-12-31", "w")]
584+
msg = "'w' is deprecated and will be removed in a future version."
585+
with tm.assert_produces_warning(FutureWarning, match=msg):
586+
vals = [end_intv, Period("2006-12-31", "w")]
580587
msg = r"Input has different freq=W-SUN from PeriodIndex\(freq=B\)"
581588
depr_msg = r"PeriodDtype\[B\] is deprecated"
582589
with pytest.raises(IncompatibleFrequency, match=msg):

pandas/tests/indexes/period/test_period_range.py

+32-12
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,9 @@ def test_construction_from_period(self):
181181

182182
def test_mismatched_start_end_freq_raises(self):
183183
depr_msg = "Period with BDay freq is deprecated"
184-
end_w = Period("2006-12-31", "1w")
184+
msg = "'w' is deprecated and will be removed in a future version."
185+
with tm.assert_produces_warning(FutureWarning, match=msg):
186+
end_w = Period("2006-12-31", "1w")
185187

186188
with tm.assert_produces_warning(FutureWarning, match=depr_msg):
187189
start_b = Period("02-Apr-2005", "B")
@@ -203,19 +205,37 @@ def test_constructor_U(self):
203205
with pytest.raises(ValueError, match="Invalid frequency: X"):
204206
period_range("2007-1-1", periods=500, freq="X")
205207

206-
def test_H_deprecated_from_time_series(self):
208+
@pytest.mark.parametrize(
209+
"freq,freq_depr",
210+
[
211+
("2Y", "2A"),
212+
("2Y", "2a"),
213+
("2Y-AUG", "2A-AUG"),
214+
("2Y-AUG", "2A-aug"),
215+
],
216+
)
217+
def test_a_deprecated_from_time_series(self, freq, freq_depr):
207218
# GH#52536
208-
msg = "'H' is deprecated and will be removed in a future version."
219+
msg = f"'{freq_depr[1:]}' is deprecated and will be removed in a "
220+
f"future version. Please use '{freq[1:]}' instead."
221+
222+
with tm.assert_produces_warning(FutureWarning, match=msg):
223+
period_range(freq=freq_depr, start="1/1/2001", end="12/1/2009")
224+
225+
@pytest.mark.parametrize("freq_depr", ["2H", "2MIN", "2S", "2US", "2NS"])
226+
def test_uppercase_freq_deprecated_from_time_series(self, freq_depr):
227+
# GH#52536, GH#54939
228+
msg = f"'{freq_depr[1:]}' is deprecated and will be removed in a "
229+
f"future version. Please use '{freq_depr.lower()[1:]}' instead."
230+
209231
with tm.assert_produces_warning(FutureWarning, match=msg):
210-
period_range(freq="2H", start="1/1/2001", end="12/1/2009")
232+
period_range("2020-01-01 00:00:00 00:00", periods=2, freq=freq_depr)
233+
234+
@pytest.mark.parametrize("freq_depr", ["2m", "2q-sep", "2y", "2w"])
235+
def test_lowercase_freq_deprecated_from_time_series(self, freq_depr):
236+
# GH#52536, GH#54939
237+
msg = f"'{freq_depr[1:]}' is deprecated and will be removed in a "
238+
f"future version. Please use '{freq_depr.upper()[1:]}' instead."
211239

212-
@pytest.mark.parametrize("freq_depr", ["2A", "A-DEC", "200A-AUG"])
213-
def test_a_deprecated_from_time_series(self, freq_depr):
214-
# GH#52536
215-
freq_msg = freq_depr[freq_depr.index("A") :]
216-
msg = (
217-
f"'{freq_msg}' is deprecated and will be removed in a future version, "
218-
f"please use 'Y{freq_msg[1:]}' instead."
219-
)
220240
with tm.assert_produces_warning(FutureWarning, match=msg):
221241
period_range(freq=freq_depr, start="1/1/2001", end="12/1/2009")

pandas/tests/resample/test_period_index.py

+29
Original file line numberDiff line numberDiff line change
@@ -1006,6 +1006,32 @@ def test_resample_t_l_deprecated(self):
10061006
result = ser.resample("T").mean()
10071007
tm.assert_series_equal(result, expected)
10081008

1009+
@pytest.mark.parametrize(
1010+
"freq, freq_depr, freq_res, freq_depr_res, data",
1011+
[
1012+
("2Q", "2q", "2Y", "2y", [0.5]),
1013+
("2M", "2m", "2Q", "2q", [1.0, 3.0]),
1014+
],
1015+
)
1016+
def test_resample_lowercase_frequency_deprecated(
1017+
self, freq, freq_depr, freq_res, freq_depr_res, data
1018+
):
1019+
depr_msg = f"'{freq_depr[1:]}' is deprecated and will be removed in a "
1020+
f"future version. Please use '{freq[1:]}' instead."
1021+
depr_msg_res = f"'{freq_depr_res[1:]}' is deprecated and will be removed in a "
1022+
f"future version. Please use '{freq_res[1:]}' instead."
1023+
1024+
with tm.assert_produces_warning(FutureWarning, match=depr_msg):
1025+
rng_l = period_range("2020-01-01", "2020-08-01", freq=freq_depr)
1026+
ser = Series(np.arange(len(rng_l)), index=rng_l)
1027+
1028+
rng = period_range("2020-01-01", "2020-08-01", freq=freq_res)
1029+
expected = Series(data=data, index=rng)
1030+
1031+
with tm.assert_produces_warning(FutureWarning, match=depr_msg_res):
1032+
result = ser.resample(freq_depr_res).mean()
1033+
tm.assert_series_equal(result, expected)
1034+
10091035
@pytest.mark.parametrize(
10101036
"offset",
10111037
[
@@ -1031,6 +1057,9 @@ def test_asfreq_invalid_period_freq(self, offset, series_and_frame):
10311057
("2Q-FEB", "2QE-FEB"),
10321058
("2Y", "2YE"),
10331059
("2Y-MAR", "2YE-MAR"),
1060+
("2M", "2me"),
1061+
("2Q", "2qe"),
1062+
("2Y-MAR", "2ye-mar"),
10341063
],
10351064
)
10361065
def test_resample_frequency_ME_QE_YE_error_message(series_and_frame, freq, freq_depr):

pandas/tests/scalar/period/test_period.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,9 @@ def test_construction(self):
106106
assert i1 == i3
107107

108108
i1 = Period("1982", freq="min")
109-
i2 = Period("1982", freq="MIN")
109+
msg = "'MIN' is deprecated and will be removed in a future version."
110+
with tm.assert_produces_warning(FutureWarning, match=msg):
111+
i2 = Period("1982", freq="MIN")
110112
assert i1 == i2
111113

112114
i1 = Period(year=2005, month=3, day=1, freq="D")

0 commit comments

Comments
 (0)