Skip to content

Commit

Permalink
CLN: enforced the deprecation of strings 'H', 'BH', 'CBH' in favor of…
Browse files Browse the repository at this point in the history
… 'h', 'bh', 'cbh' (#59143)

* CLN: enforced the deprecation of strings ‘H’, ‘BH’, ‘CBH’  in favour of ‘h’, ‘bh’, ‘cbh’

* fix tests

* add a note to v3.0.0

* fixup

* add def INVALID_FREQ_ERR_MSG to dtypes.pxd

* Revert "add def INVALID_FREQ_ERR_MSG to dtypes.pxd"

This reverts commit 4085d5c.

* remove dict c_REMOVED_ABBREVS, add msg if raise KeyError in get_reso_from_freqstr
  • Loading branch information
natmokval authored Jul 9, 2024
1 parent 3a34e07 commit d966462
Show file tree
Hide file tree
Showing 11 changed files with 50 additions and 130 deletions.
2 changes: 2 additions & 0 deletions doc/source/whatsnew/v3.0.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,8 @@ Other Removals
- Enforced deprecation of string ``A`` denoting frequency in :class:`YearEnd` and strings ``A-DEC``, ``A-JAN``, etc. denoting annual frequencies with various fiscal year ends (:issue:`57699`)
- Enforced deprecation of string ``BAS`` denoting frequency in :class:`BYearBegin` and strings ``BAS-DEC``, ``BAS-JAN``, etc. denoting annual frequencies with various fiscal year starts (:issue:`57793`)
- Enforced deprecation of string ``BA`` denoting frequency in :class:`BYearEnd` and strings ``BA-DEC``, ``BA-JAN``, etc. denoting annual frequencies with various fiscal year ends (:issue:`57793`)
- Enforced deprecation of strings ``H``, ``BH``, and ``CBH`` denoting frequencies in :class:`Hour`, :class:`BusinessHour`, :class:`CustomBusinessHour` (:issue:`59143`)
- Enforced deprecation of strings ``H``, ``BH``, and ``CBH`` denoting units in :class:`Timedelta` (:issue:`59143`)
- Enforced deprecation of strings ``T``, ``L``, ``U``, and ``N`` denoting frequencies in :class:`Minute`, :class:`Milli`, :class:`Micro`, :class:`Nano` (:issue:`57627`)
- Enforced deprecation of strings ``T``, ``L``, ``U``, and ``N`` denoting units in :class:`Timedelta` (:issue:`57627`)
- Enforced deprecation of the behavior of :func:`concat` when ``len(keys) != len(objs)`` would truncate to the shorter of the two. Now this raises a ``ValueError`` (:issue:`43485`)
Expand Down
2 changes: 1 addition & 1 deletion pandas/_libs/tslibs/dtypes.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ cdef bint is_supported_unit(NPY_DATETIMEUNIT reso)
cdef dict c_OFFSET_TO_PERIOD_FREQSTR
cdef dict c_PERIOD_TO_OFFSET_FREQSTR
cdef dict c_OFFSET_RENAMED_FREQSTR
cdef dict c_DEPR_ABBREVS
cdef dict c_DEPR_UNITS
cdef dict c_PERIOD_AND_OFFSET_DEPR_FREQSTR
cdef dict attrname_to_abbrevs
cdef dict npy_unit_to_attrname
cdef dict attrname_to_npy_unit
cdef str INVALID_FREQ_ERR_MSG

cdef enum c_FreqGroup:
# Mirrors FreqGroup in the .pyx file
Expand Down
42 changes: 6 additions & 36 deletions pandas/_libs/tslibs/dtypes.pyx
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
# period frequency constants corresponding to scikits timeseries
# originals
from enum import Enum
import warnings

from pandas.util._exceptions import find_stack_level

from pandas._libs.tslibs.ccalendar cimport c_MONTH_NUMBERS
from pandas._libs.tslibs.np_datetime cimport (
Expand Down Expand Up @@ -338,14 +335,6 @@ PERIOD_TO_OFFSET_FREQSTR = {
cdef dict c_OFFSET_TO_PERIOD_FREQSTR = OFFSET_TO_PERIOD_FREQSTR
cdef dict c_PERIOD_TO_OFFSET_FREQSTR = PERIOD_TO_OFFSET_FREQSTR

# Map deprecated resolution abbreviations to correct resolution abbreviations
cdef dict c_DEPR_ABBREVS = {
"H": "h",
"BH": "bh",
"CBH": "cbh",
"S": "s",
}

cdef dict c_DEPR_UNITS = {
"w": "W",
"d": "D",
Expand All @@ -372,6 +361,8 @@ cdef dict c_PERIOD_AND_OFFSET_DEPR_FREQSTR = {
"MIN": "min",
}

cdef str INVALID_FREQ_ERR_MSG = "Invalid frequency: {0}"


class FreqGroup(Enum):
# Mirrors c_FreqGroup in the .pxd file
Expand Down Expand Up @@ -461,39 +452,18 @@ class Resolution(Enum):
>>> Resolution.get_reso_from_freqstr('h') == Resolution.RESO_HR
True
"""
cdef:
str abbrev
try:
if freq in c_DEPR_ABBREVS:
abbrev = c_DEPR_ABBREVS[freq]
warnings.warn(
f"\'{freq}\' is deprecated and will be removed in a future "
f"version. Please use \'{abbrev}\' "
f"instead of \'{freq}\'.",
FutureWarning,
stacklevel=find_stack_level(),
)
freq = abbrev
attr_name = _abbrev_to_attrnames[freq]
except KeyError:
except KeyError as exc:
msg = INVALID_FREQ_ERR_MSG.format(freq)
# For quarterly and yearly resolutions, we need to chop off
# a month string.
split_freq = freq.split("-")
if len(split_freq) != 2:
raise
raise ValueError(msg) from exc
if split_freq[1] not in _month_names:
# i.e. we want e.g. "Q-DEC", not "Q-INVALID"
raise
if split_freq[0] in c_DEPR_ABBREVS:
abbrev = c_DEPR_ABBREVS[split_freq[0]]
warnings.warn(
f"\'{split_freq[0]}\' is deprecated and will be removed in a "
f"future version. Please use \'{abbrev}\' "
f"instead of \'{split_freq[0]}\'.",
FutureWarning,
stacklevel=find_stack_level(),
)
split_freq[0] = abbrev
raise ValueError(msg) from exc
attr_name = _abbrev_to_attrnames[split_freq[0]]

return cls.from_attrname(attr_name)
Expand Down
11 changes: 0 additions & 11 deletions pandas/_libs/tslibs/offsets.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ from pandas._libs.tslibs.ccalendar cimport (
)
from pandas._libs.tslibs.conversion cimport localize_pydatetime
from pandas._libs.tslibs.dtypes cimport (
c_DEPR_ABBREVS,
c_OFFSET_RENAMED_FREQSTR,
c_OFFSET_TO_PERIOD_FREQSTR,
c_PERIOD_AND_OFFSET_DEPR_FREQSTR,
Expand Down Expand Up @@ -4908,16 +4907,6 @@ cpdef to_offset(freq, bint is_period=False):
if not stride:
stride = 1

if prefix in c_DEPR_ABBREVS:
warnings.warn(
f"\'{prefix}\' is deprecated and will be removed "
f"in a future version, please use "
f"\'{c_DEPR_ABBREVS.get(prefix)}\' instead.",
FutureWarning,
stacklevel=find_stack_level(),
)
prefix = c_DEPR_ABBREVS[prefix]

if prefix in {"D", "h", "min", "s", "ms", "us", "ns"}:
# For these prefixes, we have something like "3h" or
# "2.5min", so we can construct a Timedelta with the
Expand Down
9 changes: 8 additions & 1 deletion pandas/tests/arrays/test_datetimes.py
Original file line number Diff line number Diff line change
Expand Up @@ -760,7 +760,7 @@ def test_date_range_frequency_M_Q_Y_raises(self, freq):
with pytest.raises(ValueError, match=msg):
pd.date_range("1/1/2000", periods=4, freq=freq)

@pytest.mark.parametrize("freq_depr", ["2H", "2CBH", "2MIN", "2S", "2mS", "2Us"])
@pytest.mark.parametrize("freq_depr", ["2MIN", "2mS", "2Us"])
def test_date_range_uppercase_frequency_deprecated(self, freq_depr):
# GH#9586, GH#54939
depr_msg = f"'{freq_depr[1:]}' is deprecated and will be removed in a "
Expand Down Expand Up @@ -807,6 +807,13 @@ def test_date_range_frequency_A_raises(self, freq):
with pytest.raises(ValueError, match=msg):
pd.date_range("1/1/2000", periods=4, freq=freq)

@pytest.mark.parametrize("freq", ["2H", "2CBH", "2S"])
def test_date_range_uppercase_frequency_raises(self, freq):
msg = f"Invalid frequency: {freq}"

with pytest.raises(ValueError, match=msg):
pd.date_range("1/1/2000", periods=4, freq=freq)


def test_factorize_sort_without_freq():
dta = DatetimeArray._from_sequence([0, 2, 1], dtype="M8[ns]")
Expand Down
27 changes: 5 additions & 22 deletions pandas/tests/indexes/datetimes/test_datetime.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,29 +133,12 @@ def test_asarray_tz_aware(self):

tm.assert_numpy_array_equal(result, expected)

def test_CBH_deprecated(self):
msg = "'CBH' is deprecated and will be removed in a future version."

with tm.assert_produces_warning(FutureWarning, match=msg):
expected = date_range(
dt.datetime(2022, 12, 11), dt.datetime(2022, 12, 13), freq="CBH"
)
result = DatetimeIndex(
[
"2022-12-12 09:00:00",
"2022-12-12 10:00:00",
"2022-12-12 11:00:00",
"2022-12-12 12:00:00",
"2022-12-12 13:00:00",
"2022-12-12 14:00:00",
"2022-12-12 15:00:00",
"2022-12-12 16:00:00",
],
dtype="datetime64[ns]",
freq="cbh",
)
@pytest.mark.parametrize("freq", ["2H", "2BH", "2S"])
def test_CBH_raises(self, freq):
msg = f"Invalid frequency: {freq}"

tm.assert_index_equal(result, expected)
with pytest.raises(ValueError, match=msg):
date_range(dt.datetime(2022, 12, 11), dt.datetime(2022, 12, 13), freq=freq)

@pytest.mark.parametrize("freq", ["2BM", "1bm", "2BQ", "1BQ-MAR", "2BY-JUN", "1by"])
def test_BM_BQ_BY_raises(self, freq):
Expand Down
8 changes: 4 additions & 4 deletions pandas/tests/indexes/period/test_period_range.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ def test_constructor_U(self):
with pytest.raises(ValueError, match="Invalid frequency: X"):
period_range("2007-1-1", periods=500, freq="X")

@pytest.mark.parametrize("freq_depr", ["2H", "2MIN", "2S", "2US", "2NS"])
@pytest.mark.parametrize("freq_depr", ["2MIN", "2US", "2NS"])
def test_uppercase_freq_deprecated_from_time_series(self, freq_depr):
# GH#52536, GH#54939
msg = f"'{freq_depr[1:]}' is deprecated and will be removed in a "
Expand All @@ -212,9 +212,9 @@ def test_uppercase_freq_deprecated_from_time_series(self, freq_depr):
with tm.assert_produces_warning(FutureWarning, match=msg):
period_range("2020-01-01 00:00:00 00:00", periods=2, freq=freq_depr)

@pytest.mark.parametrize("freq", ["2m", "2q-sep", "2y"])
def test_lowercase_freq_from_time_series_raises(self, freq):
# GH#52536, GH#54939
@pytest.mark.parametrize("freq", ["2m", "2q-sep", "2y", "2H", "2S"])
def test_incorrect_case_freq_from_time_series_raises(self, freq):
# GH#52536, GH#54939, GH#59143
msg = f"Invalid frequency: {freq}"

with pytest.raises(ValueError, match=msg):
Expand Down
44 changes: 10 additions & 34 deletions pandas/tests/indexes/timedeltas/test_timedelta_range.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

from pandas import (
Timedelta,
TimedeltaIndex,
timedelta_range,
to_timedelta,
)
Expand Down Expand Up @@ -70,14 +69,12 @@ def test_linspace_behavior(self, periods, freq):
expected = timedelta_range(start="0 days", end="4 days", freq=freq)
tm.assert_index_equal(result, expected)

def test_timedelta_range_H_deprecated(self):
def test_timedelta_range_H_raises(self):
# GH#52536
msg = "'H' is deprecated and will be removed in a future version."
msg = "Invalid frequency: H"

result = timedelta_range(start="0 days", end="4 days", periods=6)
with tm.assert_produces_warning(FutureWarning, match=msg):
expected = timedelta_range(start="0 days", end="4 days", freq="19H12min")
tm.assert_index_equal(result, expected)
with pytest.raises(ValueError, match=msg):
timedelta_range(start="0 days", end="4 days", freq="19H12min")

def test_timedelta_range_T_raises(self):
msg = "Invalid frequency: T"
Expand Down Expand Up @@ -130,33 +127,6 @@ def test_timedelta_range_infer_freq(self):
result = timedelta_range("0s", "1s", periods=31)
assert result.freq is None

@pytest.mark.parametrize(
"freq_depr, start, end, expected_values, expected_freq",
[
(
"3.5S",
"05:03:01",
"05:03:10",
["0 days 05:03:01", "0 days 05:03:04.500000", "0 days 05:03:08"],
"3500ms",
),
],
)
def test_timedelta_range_deprecated_freq(
self, freq_depr, start, end, expected_values, expected_freq
):
# GH#52536
msg = (
f"'{freq_depr[-1]}' is deprecated and will be removed in a future version."
)

with tm.assert_produces_warning(FutureWarning, match=msg):
result = timedelta_range(start=start, end=end, freq=freq_depr)
expected = TimedeltaIndex(
expected_values, dtype="timedelta64[ns]", freq=expected_freq
)
tm.assert_index_equal(result, expected)

@pytest.mark.parametrize(
"freq_depr, start, end",
[
Expand All @@ -170,9 +140,15 @@ def test_timedelta_range_deprecated_freq(
"5 hours",
"5 hours 8 minutes",
),
(
"3.5S",
"05:03:01",
"05:03:10",
),
],
)
def test_timedelta_range_removed_freq(self, freq_depr, start, end):
# GH#59143
msg = f"Invalid frequency: {freq_depr}"
with pytest.raises(ValueError, match=msg):
timedelta_range(start=start, end=end, freq=freq_depr)
5 changes: 1 addition & 4 deletions pandas/tests/scalar/period/test_asfreq.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,17 +111,14 @@ def test_conv_annual(self):
assert ival_A.asfreq("B", "E") == ival_A_to_B_end
assert ival_A.asfreq("D", "s") == ival_A_to_D_start
assert ival_A.asfreq("D", "E") == ival_A_to_D_end
msg_depr = "'H' is deprecated and will be removed in a future version."
with tm.assert_produces_warning(FutureWarning, match=msg_depr):
with pytest.raises(ValueError, match=msg):
assert ival_A.asfreq("H", "s") == ival_A_to_H_start
assert ival_A.asfreq("H", "E") == ival_A_to_H_end
assert ival_A.asfreq("min", "s") == ival_A_to_T_start
assert ival_A.asfreq("min", "E") == ival_A_to_T_end
with pytest.raises(ValueError, match=msg):
assert ival_A.asfreq("T", "s") == ival_A_to_T_start
assert ival_A.asfreq("T", "E") == ival_A_to_T_end
msg_depr = "'S' is deprecated and will be removed in a future version."
with tm.assert_produces_warning(FutureWarning, match=msg_depr):
assert ival_A.asfreq("S", "S") == ival_A_to_S_start
assert ival_A.asfreq("S", "E") == ival_A_to_S_end

Expand Down
10 changes: 4 additions & 6 deletions pandas/tests/tslibs/test_resolution.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
)
from pandas._libs.tslibs.dtypes import NpyDatetimeUnit

import pandas._testing as tm


def test_get_resolution_nano():
# don't return the fallback RESO_DAY
Expand Down Expand Up @@ -50,9 +48,9 @@ def test_get_attrname_from_abbrev(freqstr, expected):


@pytest.mark.parametrize("freq", ["H", "S"])
def test_units_H_S_deprecated_from_attrname_to_abbrevs(freq):
# GH#52536
msg = f"'{freq}' is deprecated and will be removed in a future version."
def test_unit_H_S_raises(freq):
# GH#59143
msg = f"Invalid frequency: {freq}"

with tm.assert_produces_warning(FutureWarning, match=msg):
with pytest.raises(ValueError, match=msg):
Resolution.get_reso_from_freqstr(freq)
20 changes: 9 additions & 11 deletions pandas/tests/tslibs/test_to_offset.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,17 +203,7 @@ def test_to_offset_lowercase_frequency_raises(freq_depr):
to_offset(freq_depr)


@pytest.mark.parametrize(
"freq_depr",
[
"2H",
"2BH",
"2MIN",
"2S",
"2Us",
"2NS",
],
)
@pytest.mark.parametrize("freq_depr", ["2MIN", "2Us", "2NS"])
def test_to_offset_uppercase_frequency_deprecated(freq_depr):
# GH#54939
depr_msg = f"'{freq_depr[1:]}' is deprecated and will be removed in a "
Expand All @@ -238,3 +228,11 @@ def test_to_offset_lowercase_frequency_deprecated(freq_depr, expected):
with tm.assert_produces_warning(FutureWarning, match=msg):
result = to_offset(freq_depr)
assert result == expected


@pytest.mark.parametrize("freq", ["2H", "2BH", "2S"])
def test_to_offset_uppercase_frequency_raises(freq):
msg = f"Invalid frequency: {freq}"

with pytest.raises(ValueError, match=msg):
to_offset(freq)

0 comments on commit d966462

Please sign in to comment.