From 9009ce2c55b312b272c28a6c890242d5acd7a09d Mon Sep 17 00:00:00 2001 From: Natalia Mokeeva Date: Sun, 25 Feb 2024 23:48:01 +0100 Subject: [PATCH 1/9] remove deprecated units from Timedelta, TimedeltaIndex --- pandas/_libs/tslibs/timedeltas.pyi | 1 + pandas/_libs/tslibs/timedeltas.pyx | 7 ++++ pandas/core/indexes/timedeltas.py | 6 ++- pandas/core/tools/timedeltas.py | 2 + .../indexes/timedeltas/test_constructors.py | 8 ++++ .../scalar/timedelta/test_constructors.py | 38 ++++--------------- 6 files changed, 31 insertions(+), 31 deletions(-) diff --git a/pandas/_libs/tslibs/timedeltas.pyi b/pandas/_libs/tslibs/timedeltas.pyi index 24ec6c8891a89..3cf470e81cf6e 100644 --- a/pandas/_libs/tslibs/timedeltas.pyi +++ b/pandas/_libs/tslibs/timedeltas.pyi @@ -70,6 +70,7 @@ _S = TypeVar("_S", bound=timedelta) def get_unit_for_round(freq, creso: int) -> int: ... def disallow_ambiguous_unit(unit: str | None) -> None: ... +def disallow_deprecated_unit(unit: str | None) -> None: ... def ints_to_pytimedelta( m8values: npt.NDArray[np.timedelta64], box: bool = ..., diff --git a/pandas/_libs/tslibs/timedeltas.pyx b/pandas/_libs/tslibs/timedeltas.pyx index f6c69cf6d3875..10c0ec37c0ac3 100644 --- a/pandas/_libs/tslibs/timedeltas.pyx +++ b/pandas/_libs/tslibs/timedeltas.pyx @@ -829,6 +829,12 @@ cpdef disallow_ambiguous_unit(unit): "represent unambiguous timedelta values durations." ) +cpdef disallow_deprecated_unit(unit): + if unit in {"H", "S", "T", "t", "L", "l", "U", "u", "N", "n"}: + raise ValueError( + f"Unit \'{unit}\' is no longer supported." + ) + cdef int64_t parse_iso_format_string(str ts) except? -1: """ @@ -1819,6 +1825,7 @@ class Timedelta(_Timedelta): raise OutOfBoundsTimedelta(msg) from err disallow_ambiguous_unit(unit) + disallow_deprecated_unit(unit) # GH 30543 if pd.Timedelta already passed, return it # check that only value is passed diff --git a/pandas/core/indexes/timedeltas.py b/pandas/core/indexes/timedeltas.py index a929687544876..82b151f15ada3 100644 --- a/pandas/core/indexes/timedeltas.py +++ b/pandas/core/indexes/timedeltas.py @@ -13,7 +13,10 @@ Timedelta, to_offset, ) -from pandas._libs.tslibs.timedeltas import disallow_ambiguous_unit +from pandas._libs.tslibs.timedeltas import ( + disallow_ambiguous_unit, + disallow_deprecated_unit, +) from pandas.util._exceptions import find_stack_level from pandas.core.dtypes.common import ( @@ -184,6 +187,7 @@ def __new__( cls._raise_scalar_data_error(data) disallow_ambiguous_unit(unit) + disallow_deprecated_unit(unit) if dtype is not None: dtype = pandas_dtype(dtype) diff --git a/pandas/core/tools/timedeltas.py b/pandas/core/tools/timedeltas.py index 5f3963c3d405e..7a6eeaa1d2adc 100644 --- a/pandas/core/tools/timedeltas.py +++ b/pandas/core/tools/timedeltas.py @@ -19,6 +19,7 @@ from pandas._libs.tslibs.timedeltas import ( Timedelta, disallow_ambiguous_unit, + disallow_deprecated_unit, parse_timedelta_unit, ) @@ -180,6 +181,7 @@ def to_timedelta( if unit is not None: unit = parse_timedelta_unit(unit) disallow_ambiguous_unit(unit) + disallow_deprecated_unit(unit) if errors not in ("raise", "coerce"): raise ValueError("errors must be one of 'raise', or 'coerce'.") diff --git a/pandas/tests/indexes/timedeltas/test_constructors.py b/pandas/tests/indexes/timedeltas/test_constructors.py index 0510700bb64d7..b5c1a70cb7b87 100644 --- a/pandas/tests/indexes/timedeltas/test_constructors.py +++ b/pandas/tests/indexes/timedeltas/test_constructors.py @@ -289,3 +289,11 @@ def test_from_categorical(self): ci = pd.CategoricalIndex(tdi) result = TimedeltaIndex(ci) tm.assert_index_equal(result, tdi) + + @pytest.mark.parametrize("unit", ["H", "S", "T", "t", "L", "l", "U", "u", "N", "n"]) + def test_deprecated_unit_raises(self, unit): + msg = f"Unit '{unit}' is no longer supported." + depr_msg = "The 'unit' keyword in TimedeltaIndex construction is deprecated" + with pytest.raises(ValueError, match=msg): + with tm.assert_produces_warning(FutureWarning, match=depr_msg): + TimedeltaIndex([1, 3, 7], unit) diff --git a/pandas/tests/scalar/timedelta/test_constructors.py b/pandas/tests/scalar/timedelta/test_constructors.py index e680ca737b546..6111ffd550c6b 100644 --- a/pandas/tests/scalar/timedelta/test_constructors.py +++ b/pandas/tests/scalar/timedelta/test_constructors.py @@ -32,25 +32,12 @@ def test_unit_m_y_raises(self, unit): with pytest.raises(ValueError, match=msg): to_timedelta([1, 2], unit) - @pytest.mark.parametrize( - "unit,unit_depr", - [ - ("h", "H"), - ("min", "T"), - ("s", "S"), - ("ms", "L"), - ("ns", "N"), - ("us", "U"), - ], - ) - def test_units_H_T_S_L_N_U_deprecated(self, unit, unit_depr): + @pytest.mark.parametrize("unit_depr", ["H", "T", "S", "L", "N", "U"]) + def test_units_H_T_S_L_N_U_raise(self, unit, unit_depr): # GH#52536 - msg = f"'{unit_depr}' is deprecated and will be removed in a future version." - - expected = Timedelta(1, unit=unit) - with tm.assert_produces_warning(FutureWarning, match=msg): - result = Timedelta(1, unit=unit_depr) - tm.assert_equal(result, expected) + msg = f"Unit '{unit_depr}' is no longer supported." + with pytest.raises(ValueError, match=msg): + Timedelta(1, unit=unit_depr) @pytest.mark.parametrize( "unit, np_unit", @@ -103,13 +90,11 @@ def test_units_H_T_S_L_N_U_deprecated(self, unit, unit_depr): "microsecond", "micro", "micros", - "u", "US", "Microseconds", "Microsecond", "Micro", "Micros", - "U", ] ] + [ @@ -120,13 +105,11 @@ def test_units_H_T_S_L_N_U_deprecated(self, unit, unit_depr): "nanosecond", "nano", "nanos", - "n", "NS", "Nanoseconds", "Nanosecond", "Nano", "Nanos", - "N", ] ], ) @@ -139,14 +122,9 @@ def test_unit_parser(self, unit, np_unit, wrapper): dtype="m8[ns]", ) # TODO(2.0): the desired output dtype may have non-nano resolution - msg = f"'{unit}' is deprecated and will be removed in a future version." - - if (unit, np_unit) in (("u", "us"), ("U", "us"), ("n", "ns"), ("N", "ns")): - warn = FutureWarning - else: - warn = FutureWarning - msg = "The 'unit' keyword in TimedeltaIndex construction is deprecated" - with tm.assert_produces_warning(warn, match=msg): + + msg = "The 'unit' keyword in TimedeltaIndex construction is deprecated" + with tm.assert_produces_warning(FutureWarning, match=msg): result = to_timedelta(wrapper(range(5)), unit=unit) tm.assert_index_equal(result, expected) result = TimedeltaIndex(wrapper(range(5)), unit=unit) From c9c02a983a1155b665c00d783f5066d46a95495d Mon Sep 17 00:00:00 2001 From: Natalia Mokeeva Date: Mon, 26 Feb 2024 21:04:32 +0100 Subject: [PATCH 2/9] disallow deprecated units in Timedelta constructor and in parse_timedelta_unit, fix tests --- pandas/_libs/tslibs/dtypes.pyx | 10 --- pandas/_libs/tslibs/offsets.pyx | 3 + pandas/_libs/tslibs/timedeltas.pxd | 1 + pandas/_libs/tslibs/timedeltas.pyx | 13 +--- .../indexes/timedeltas/test_constructors.py | 4 +- .../timedeltas/test_timedelta_range.py | 70 ++++--------------- .../scalar/timedelta/test_constructors.py | 19 +++-- 7 files changed, 33 insertions(+), 87 deletions(-) diff --git a/pandas/_libs/tslibs/dtypes.pyx b/pandas/_libs/tslibs/dtypes.pyx index 52e1133e596c5..3568bbed3c929 100644 --- a/pandas/_libs/tslibs/dtypes.pyx +++ b/pandas/_libs/tslibs/dtypes.pyx @@ -398,18 +398,8 @@ cdef dict c_DEPR_ABBREVS = { "BAS-SEP": "BYS-SEP", "BAS-OCT": "BYS-OCT", "BAS-NOV": "BYS-NOV", - "H": "h", "BH": "bh", "CBH": "cbh", - "T": "min", - "t": "min", - "S": "s", - "L": "ms", - "l": "ms", - "U": "us", - "u": "us", - "N": "ns", - "n": "ns", } diff --git a/pandas/_libs/tslibs/offsets.pyx b/pandas/_libs/tslibs/offsets.pyx index 7301f65f3dbac..4505ae518d581 100644 --- a/pandas/_libs/tslibs/offsets.pyx +++ b/pandas/_libs/tslibs/offsets.pyx @@ -74,6 +74,7 @@ from pandas._libs.tslibs.np_datetime cimport ( pandas_datetime_to_datetimestruct, pydate_to_dtstruct, ) +from pandas._libs.tslibs.timedeltas cimport disallow_deprecated_unit import_pandas_datetime() @@ -4866,6 +4867,8 @@ cpdef to_offset(freq, bint is_period=False): ) prefix = c_DEPR_ABBREVS[prefix] + disallow_deprecated_unit(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 diff --git a/pandas/_libs/tslibs/timedeltas.pxd b/pandas/_libs/tslibs/timedeltas.pxd index f3473e46b6699..665ca319d24f3 100644 --- a/pandas/_libs/tslibs/timedeltas.pxd +++ b/pandas/_libs/tslibs/timedeltas.pxd @@ -11,6 +11,7 @@ cpdef int64_t delta_to_nanoseconds( ) except? -1 cdef convert_to_timedelta64(object ts, str unit) cdef bint is_any_td_scalar(object obj) +cpdef disallow_deprecated_unit(unit) cdef class _Timedelta(timedelta): diff --git a/pandas/_libs/tslibs/timedeltas.pyx b/pandas/_libs/tslibs/timedeltas.pyx index 10c0ec37c0ac3..44ec38157b62d 100644 --- a/pandas/_libs/tslibs/timedeltas.pyx +++ b/pandas/_libs/tslibs/timedeltas.pyx @@ -1,8 +1,6 @@ import collections import warnings -from pandas.util._exceptions import find_stack_level - cimport cython from cpython.object cimport ( Py_EQ, @@ -43,7 +41,6 @@ from pandas._libs.tslibs.conversion cimport ( precision_from_unit, ) from pandas._libs.tslibs.dtypes cimport ( - c_DEPR_ABBREVS, get_supported_reso, is_supported_unit, npy_unit_to_abbrev, @@ -719,15 +716,7 @@ cpdef inline str parse_timedelta_unit(str unit): return "ns" elif unit == "M": return unit - elif unit in c_DEPR_ABBREVS: - warnings.warn( - f"\'{unit}\' is deprecated and will be removed in a " - f"future version. Please use \'{c_DEPR_ABBREVS.get(unit)}\' " - f"instead of \'{unit}\'.", - FutureWarning, - stacklevel=find_stack_level(), - ) - unit = c_DEPR_ABBREVS[unit] + disallow_deprecated_unit(unit) try: return timedelta_abbrevs[unit.lower()] except KeyError: diff --git a/pandas/tests/indexes/timedeltas/test_constructors.py b/pandas/tests/indexes/timedeltas/test_constructors.py index b5c1a70cb7b87..510b7cfb66fa5 100644 --- a/pandas/tests/indexes/timedeltas/test_constructors.py +++ b/pandas/tests/indexes/timedeltas/test_constructors.py @@ -290,8 +290,8 @@ def test_from_categorical(self): result = TimedeltaIndex(ci) tm.assert_index_equal(result, tdi) - @pytest.mark.parametrize("unit", ["H", "S", "T", "t", "L", "l", "U", "u", "N", "n"]) - def test_deprecated_unit_raises(self, unit): + @pytest.mark.parametrize("unit", ["H", "T", "t", "S", "L", "l", "U", "u", "N", "n"]) + def test_unit_H_T_S_L_N_U_raises(self, unit): msg = f"Unit '{unit}' is no longer supported." depr_msg = "The 'unit' keyword in TimedeltaIndex construction is deprecated" with pytest.raises(ValueError, match=msg): diff --git a/pandas/tests/indexes/timedeltas/test_timedelta_range.py b/pandas/tests/indexes/timedeltas/test_timedelta_range.py index f22bdb7a90516..c14a66b8a543b 100644 --- a/pandas/tests/indexes/timedeltas/test_timedelta_range.py +++ b/pandas/tests/indexes/timedeltas/test_timedelta_range.py @@ -3,7 +3,6 @@ from pandas import ( Timedelta, - TimedeltaIndex, timedelta_range, to_timedelta, ) @@ -43,31 +42,12 @@ def test_timedelta_range(self): result = timedelta_range("0 days", freq="30min", periods=50) tm.assert_index_equal(result, expected) - @pytest.mark.parametrize( - "depr_unit, unit", - [ - ("H", "hour"), - ("T", "minute"), - ("t", "minute"), - ("S", "second"), - ("L", "millisecond"), - ("l", "millisecond"), - ("U", "microsecond"), - ("u", "microsecond"), - ("N", "nanosecond"), - ("n", "nanosecond"), - ], - ) - def test_timedelta_units_H_T_S_L_U_N_deprecated(self, depr_unit, unit): - # GH#52536 - depr_msg = ( - f"'{depr_unit}' is deprecated and will be removed in a future version." - ) + @pytest.mark.parametrize("unit", ["H", "T", "t", "S", "L", "l", "U", "u", "N", "n"]) + def test_timedelta_unit_H_T_S_L_U_N_raises(self, unit): + msg = f"Unit '{unit}' is no longer supported." - expected = to_timedelta(np.arange(5), unit=unit) - with tm.assert_produces_warning(FutureWarning, match=depr_msg): - result = to_timedelta(np.arange(5), unit=depr_unit) - tm.assert_index_equal(result, expected) + with pytest.raises(ValueError, match=msg): + to_timedelta(np.arange(5), unit=unit) @pytest.mark.parametrize( "periods, freq", [(3, "2D"), (5, "D"), (6, "19h12min"), (7, "16h"), (9, "12h")] @@ -79,14 +59,11 @@ def test_linspace_behavior(self, periods, freq): tm.assert_index_equal(result, expected) @pytest.mark.parametrize("msg_freq, freq", [("H", "19H12min"), ("T", "19h12T")]) - def test_timedelta_range_H_T_deprecated(self, freq, msg_freq): - # GH#52536 - msg = f"'{msg_freq}' is deprecated and will be removed in a future version." + def test_timedelta_range_H_T_raises(self, msg_freq, freq): + msg = f"Unit '{msg_freq}' is no longer supported." - 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=freq) - tm.assert_index_equal(result, expected) + with pytest.raises(ValueError, match=msg): + timedelta_range(start="0 days", end="4 days", freq=freq) def test_errors(self): # not enough params @@ -134,40 +111,21 @@ def test_timedelta_range_infer_freq(self): assert result.freq is None @pytest.mark.parametrize( - "freq_depr, start, end, expected_values, expected_freq", + "freq_depr, start, end", [ ( "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", ), ( "2.5T", "5 hours", "5 hours 8 minutes", - [ - "0 days 05:00:00", - "0 days 05:02:30", - "0 days 05:05:00", - "0 days 05:07:30", - ], - "150s", ), ], ) - 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) + def test_timedelta_range_deprecated_freq(self, freq_depr, start, end): + msg = f"Unit '{freq_depr[-1]}' is no longer supported." + with pytest.raises(ValueError, match=msg): + timedelta_range(start=start, end=end, freq=freq_depr) diff --git a/pandas/tests/scalar/timedelta/test_constructors.py b/pandas/tests/scalar/timedelta/test_constructors.py index 6111ffd550c6b..39e90c8c5ff03 100644 --- a/pandas/tests/scalar/timedelta/test_constructors.py +++ b/pandas/tests/scalar/timedelta/test_constructors.py @@ -32,13 +32,6 @@ def test_unit_m_y_raises(self, unit): with pytest.raises(ValueError, match=msg): to_timedelta([1, 2], unit) - @pytest.mark.parametrize("unit_depr", ["H", "T", "S", "L", "N", "U"]) - def test_units_H_T_S_L_N_U_raise(self, unit, unit_depr): - # GH#52536 - msg = f"Unit '{unit_depr}' is no longer supported." - with pytest.raises(ValueError, match=msg): - Timedelta(1, unit=unit_depr) - @pytest.mark.parametrize( "unit, np_unit", [(value, "W") for value in ["W", "w"]] @@ -148,6 +141,18 @@ def test_unit_parser(self, unit, np_unit, wrapper): result = Timedelta(f"2{unit}") assert result == expected + @pytest.mark.parametrize("unit", ["H", "T", "t", "S", "L", "l", "U", "u", "N", "n"]) + def test_unit_H_T_S_L_N_U_raises(self, unit): + msg = f"Unit '{unit}' is no longer supported." + with pytest.raises(ValueError, match=msg): + Timedelta(1, unit=unit) + + with pytest.raises(ValueError, match=msg): + to_timedelta(10, unit) + + with pytest.raises(ValueError, match=msg): + to_timedelta([1, 2], unit) + def test_construct_from_kwargs_overflow(): # GH#55503 From 315a5d13c105191afd6a5a4c2326b432efc2cc7f Mon Sep 17 00:00:00 2001 From: Natalia Mokeeva Date: Tue, 27 Feb 2024 22:15:59 +0100 Subject: [PATCH 3/9] correct get_reso_from_freqstr, fix tests for Resolution --- pandas/_libs/tslibs/dtypes.pyx | 26 +++++++++++++++++++++++--- pandas/tests/tslibs/test_resolution.py | 11 +++++++++-- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/pandas/_libs/tslibs/dtypes.pyx b/pandas/_libs/tslibs/dtypes.pyx index 3568bbed3c929..6b00d12ec25b5 100644 --- a/pandas/_libs/tslibs/dtypes.pyx +++ b/pandas/_libs/tslibs/dtypes.pyx @@ -398,8 +398,18 @@ cdef dict c_DEPR_ABBREVS = { "BAS-SEP": "BYS-SEP", "BAS-OCT": "BYS-OCT", "BAS-NOV": "BYS-NOV", + "H": "h", "BH": "bh", "CBH": "cbh", + "T": "min", + "t": "min", + "S": "s", + "L": "ms", + "l": "ms", + "U": "us", + "u": "us", + "N": "ns", + "n": "ns", } @@ -494,12 +504,16 @@ class Resolution(Enum): cdef: str abbrev try: - if freq in c_DEPR_ABBREVS: + if freq in {"H", "S", "T", "L", "U", "N"}: + raise ValueError( + f"Resolution abbreviation \'{freq}\' is no longer supported." + ) + if freq in c_DEPR_ABBREVS and freq.upper() == freq: abbrev = c_DEPR_ABBREVS[freq] warnings.warn( f"\'{freq}\' is deprecated and will be removed in a future " f"version. Please use \'{abbrev}\' " - "instead of \'{freq}\'.", + f"instead of \'{freq}\'.", FutureWarning, stacklevel=find_stack_level(), ) @@ -514,7 +528,13 @@ class Resolution(Enum): 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: + if split_freq[0] in {"H", "S", "T", "L", "U", "N"}: + raise ValueError( + f"Resolution abbreviation \'{split_freq[0]}\' " + "is no longer supported." + ) + if (split_freq[0] in c_DEPR_ABBREVS and + split_freq[0].upper() == split_freq[0]): abbrev = c_DEPR_ABBREVS[split_freq[0]] warnings.warn( f"\'{split_freq[0]}\' is deprecated and will be removed in a " diff --git a/pandas/tests/tslibs/test_resolution.py b/pandas/tests/tslibs/test_resolution.py index 690962f1daa5e..7680beb0b8eec 100644 --- a/pandas/tests/tslibs/test_resolution.py +++ b/pandas/tests/tslibs/test_resolution.py @@ -48,10 +48,17 @@ def test_get_attrname_from_abbrev(freqstr, expected): assert reso.attrname == expected -@pytest.mark.parametrize("freq", ["A", "H", "T", "S", "L", "U", "N"]) -def test_units_A_H_T_S_L_U_N_deprecated_from_attrname_to_abbrevs(freq): +@pytest.mark.parametrize("freq", ["A", "A-NOV"]) +def test_A_deprecated_from_attrname_to_abbrevs(freq): # GH#52536 msg = f"'{freq}' is deprecated and will be removed in a future version." with tm.assert_produces_warning(FutureWarning, match=msg): Resolution.get_reso_from_freqstr(freq) + + +@pytest.mark.parametrize("freq", ["H", "T", "S", "L", "U", "N"]) +def test_reso_abbrev_H_T_S_L_U_N_raises(freq): + msg = f"Resolution abbreviation '{freq}' is no longer supported." + with pytest.raises(ValueError, match=msg): + Resolution.get_reso_from_freqstr(freq) From fe1af623620758ee1a2d92faaeab35d9178fa389 Mon Sep 17 00:00:00 2001 From: Natalia Mokeeva Date: Thu, 29 Feb 2024 15:34:52 +0100 Subject: [PATCH 4/9] remove freqs H, T and smaller from c_DEPR_ABBREVS, move other changes back --- pandas/_libs/tslibs/dtypes.pyx | 24 ++----------------- pandas/_libs/tslibs/offsets.pyx | 3 --- pandas/_libs/tslibs/timedeltas.pxd | 1 - pandas/_libs/tslibs/timedeltas.pyi | 1 - pandas/_libs/tslibs/timedeltas.pyx | 20 +++++++++------- pandas/core/indexes/timedeltas.py | 6 +---- pandas/core/tools/timedeltas.py | 2 -- .../indexes/timedeltas/test_constructors.py | 2 +- .../timedeltas/test_timedelta_range.py | 6 ++--- .../scalar/timedelta/test_constructors.py | 2 +- pandas/tests/tslibs/test_resolution.py | 2 +- 11 files changed, 21 insertions(+), 48 deletions(-) diff --git a/pandas/_libs/tslibs/dtypes.pyx b/pandas/_libs/tslibs/dtypes.pyx index 6b00d12ec25b5..9efed2a3ef013 100644 --- a/pandas/_libs/tslibs/dtypes.pyx +++ b/pandas/_libs/tslibs/dtypes.pyx @@ -398,18 +398,8 @@ cdef dict c_DEPR_ABBREVS = { "BAS-SEP": "BYS-SEP", "BAS-OCT": "BYS-OCT", "BAS-NOV": "BYS-NOV", - "H": "h", "BH": "bh", "CBH": "cbh", - "T": "min", - "t": "min", - "S": "s", - "L": "ms", - "l": "ms", - "U": "us", - "u": "us", - "N": "ns", - "n": "ns", } @@ -504,11 +494,7 @@ class Resolution(Enum): cdef: str abbrev try: - if freq in {"H", "S", "T", "L", "U", "N"}: - raise ValueError( - f"Resolution abbreviation \'{freq}\' is no longer supported." - ) - if freq in c_DEPR_ABBREVS and freq.upper() == freq: + if freq in c_DEPR_ABBREVS: abbrev = c_DEPR_ABBREVS[freq] warnings.warn( f"\'{freq}\' is deprecated and will be removed in a future " @@ -528,13 +514,7 @@ class Resolution(Enum): 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 {"H", "S", "T", "L", "U", "N"}: - raise ValueError( - f"Resolution abbreviation \'{split_freq[0]}\' " - "is no longer supported." - ) - if (split_freq[0] in c_DEPR_ABBREVS and - split_freq[0].upper() == split_freq[0]): + 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 " diff --git a/pandas/_libs/tslibs/offsets.pyx b/pandas/_libs/tslibs/offsets.pyx index 4505ae518d581..7301f65f3dbac 100644 --- a/pandas/_libs/tslibs/offsets.pyx +++ b/pandas/_libs/tslibs/offsets.pyx @@ -74,7 +74,6 @@ from pandas._libs.tslibs.np_datetime cimport ( pandas_datetime_to_datetimestruct, pydate_to_dtstruct, ) -from pandas._libs.tslibs.timedeltas cimport disallow_deprecated_unit import_pandas_datetime() @@ -4867,8 +4866,6 @@ cpdef to_offset(freq, bint is_period=False): ) prefix = c_DEPR_ABBREVS[prefix] - disallow_deprecated_unit(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 diff --git a/pandas/_libs/tslibs/timedeltas.pxd b/pandas/_libs/tslibs/timedeltas.pxd index 665ca319d24f3..f3473e46b6699 100644 --- a/pandas/_libs/tslibs/timedeltas.pxd +++ b/pandas/_libs/tslibs/timedeltas.pxd @@ -11,7 +11,6 @@ cpdef int64_t delta_to_nanoseconds( ) except? -1 cdef convert_to_timedelta64(object ts, str unit) cdef bint is_any_td_scalar(object obj) -cpdef disallow_deprecated_unit(unit) cdef class _Timedelta(timedelta): diff --git a/pandas/_libs/tslibs/timedeltas.pyi b/pandas/_libs/tslibs/timedeltas.pyi index 3cf470e81cf6e..24ec6c8891a89 100644 --- a/pandas/_libs/tslibs/timedeltas.pyi +++ b/pandas/_libs/tslibs/timedeltas.pyi @@ -70,7 +70,6 @@ _S = TypeVar("_S", bound=timedelta) def get_unit_for_round(freq, creso: int) -> int: ... def disallow_ambiguous_unit(unit: str | None) -> None: ... -def disallow_deprecated_unit(unit: str | None) -> None: ... def ints_to_pytimedelta( m8values: npt.NDArray[np.timedelta64], box: bool = ..., diff --git a/pandas/_libs/tslibs/timedeltas.pyx b/pandas/_libs/tslibs/timedeltas.pyx index 44ec38157b62d..f6c69cf6d3875 100644 --- a/pandas/_libs/tslibs/timedeltas.pyx +++ b/pandas/_libs/tslibs/timedeltas.pyx @@ -1,6 +1,8 @@ import collections import warnings +from pandas.util._exceptions import find_stack_level + cimport cython from cpython.object cimport ( Py_EQ, @@ -41,6 +43,7 @@ from pandas._libs.tslibs.conversion cimport ( precision_from_unit, ) from pandas._libs.tslibs.dtypes cimport ( + c_DEPR_ABBREVS, get_supported_reso, is_supported_unit, npy_unit_to_abbrev, @@ -716,7 +719,15 @@ cpdef inline str parse_timedelta_unit(str unit): return "ns" elif unit == "M": return unit - disallow_deprecated_unit(unit) + elif unit in c_DEPR_ABBREVS: + warnings.warn( + f"\'{unit}\' is deprecated and will be removed in a " + f"future version. Please use \'{c_DEPR_ABBREVS.get(unit)}\' " + f"instead of \'{unit}\'.", + FutureWarning, + stacklevel=find_stack_level(), + ) + unit = c_DEPR_ABBREVS[unit] try: return timedelta_abbrevs[unit.lower()] except KeyError: @@ -818,12 +829,6 @@ cpdef disallow_ambiguous_unit(unit): "represent unambiguous timedelta values durations." ) -cpdef disallow_deprecated_unit(unit): - if unit in {"H", "S", "T", "t", "L", "l", "U", "u", "N", "n"}: - raise ValueError( - f"Unit \'{unit}\' is no longer supported." - ) - cdef int64_t parse_iso_format_string(str ts) except? -1: """ @@ -1814,7 +1819,6 @@ class Timedelta(_Timedelta): raise OutOfBoundsTimedelta(msg) from err disallow_ambiguous_unit(unit) - disallow_deprecated_unit(unit) # GH 30543 if pd.Timedelta already passed, return it # check that only value is passed diff --git a/pandas/core/indexes/timedeltas.py b/pandas/core/indexes/timedeltas.py index 82b151f15ada3..a929687544876 100644 --- a/pandas/core/indexes/timedeltas.py +++ b/pandas/core/indexes/timedeltas.py @@ -13,10 +13,7 @@ Timedelta, to_offset, ) -from pandas._libs.tslibs.timedeltas import ( - disallow_ambiguous_unit, - disallow_deprecated_unit, -) +from pandas._libs.tslibs.timedeltas import disallow_ambiguous_unit from pandas.util._exceptions import find_stack_level from pandas.core.dtypes.common import ( @@ -187,7 +184,6 @@ def __new__( cls._raise_scalar_data_error(data) disallow_ambiguous_unit(unit) - disallow_deprecated_unit(unit) if dtype is not None: dtype = pandas_dtype(dtype) diff --git a/pandas/core/tools/timedeltas.py b/pandas/core/tools/timedeltas.py index 7a6eeaa1d2adc..5f3963c3d405e 100644 --- a/pandas/core/tools/timedeltas.py +++ b/pandas/core/tools/timedeltas.py @@ -19,7 +19,6 @@ from pandas._libs.tslibs.timedeltas import ( Timedelta, disallow_ambiguous_unit, - disallow_deprecated_unit, parse_timedelta_unit, ) @@ -181,7 +180,6 @@ def to_timedelta( if unit is not None: unit = parse_timedelta_unit(unit) disallow_ambiguous_unit(unit) - disallow_deprecated_unit(unit) if errors not in ("raise", "coerce"): raise ValueError("errors must be one of 'raise', or 'coerce'.") diff --git a/pandas/tests/indexes/timedeltas/test_constructors.py b/pandas/tests/indexes/timedeltas/test_constructors.py index 510b7cfb66fa5..2d8536b48378e 100644 --- a/pandas/tests/indexes/timedeltas/test_constructors.py +++ b/pandas/tests/indexes/timedeltas/test_constructors.py @@ -292,7 +292,7 @@ def test_from_categorical(self): @pytest.mark.parametrize("unit", ["H", "T", "t", "S", "L", "l", "U", "u", "N", "n"]) def test_unit_H_T_S_L_N_U_raises(self, unit): - msg = f"Unit '{unit}' is no longer supported." + msg = f"Invalid frequency: {unit}" depr_msg = "The 'unit' keyword in TimedeltaIndex construction is deprecated" with pytest.raises(ValueError, match=msg): with tm.assert_produces_warning(FutureWarning, match=depr_msg): diff --git a/pandas/tests/indexes/timedeltas/test_timedelta_range.py b/pandas/tests/indexes/timedeltas/test_timedelta_range.py index c14a66b8a543b..df0809a88736a 100644 --- a/pandas/tests/indexes/timedeltas/test_timedelta_range.py +++ b/pandas/tests/indexes/timedeltas/test_timedelta_range.py @@ -44,7 +44,7 @@ def test_timedelta_range(self): @pytest.mark.parametrize("unit", ["H", "T", "t", "S", "L", "l", "U", "u", "N", "n"]) def test_timedelta_unit_H_T_S_L_U_N_raises(self, unit): - msg = f"Unit '{unit}' is no longer supported." + msg = f"Invalid frequency: {unit}" with pytest.raises(ValueError, match=msg): to_timedelta(np.arange(5), unit=unit) @@ -60,7 +60,7 @@ def test_linspace_behavior(self, periods, freq): @pytest.mark.parametrize("msg_freq, freq", [("H", "19H12min"), ("T", "19h12T")]) def test_timedelta_range_H_T_raises(self, msg_freq, freq): - msg = f"Unit '{msg_freq}' is no longer supported." + msg = f"Invalid frequency: {msg_freq}" with pytest.raises(ValueError, match=msg): timedelta_range(start="0 days", end="4 days", freq=freq) @@ -126,6 +126,6 @@ def test_timedelta_range_infer_freq(self): ], ) def test_timedelta_range_deprecated_freq(self, freq_depr, start, end): - msg = f"Unit '{freq_depr[-1]}' is no longer supported." + msg =f"Invalid frequency: {freq_depr[-1]}" with pytest.raises(ValueError, match=msg): timedelta_range(start=start, end=end, freq=freq_depr) diff --git a/pandas/tests/scalar/timedelta/test_constructors.py b/pandas/tests/scalar/timedelta/test_constructors.py index 39e90c8c5ff03..b4dfb69209249 100644 --- a/pandas/tests/scalar/timedelta/test_constructors.py +++ b/pandas/tests/scalar/timedelta/test_constructors.py @@ -143,7 +143,7 @@ def test_unit_parser(self, unit, np_unit, wrapper): @pytest.mark.parametrize("unit", ["H", "T", "t", "S", "L", "l", "U", "u", "N", "n"]) def test_unit_H_T_S_L_N_U_raises(self, unit): - msg = f"Unit '{unit}' is no longer supported." + msg = f"Invalid frequency: {unit}" with pytest.raises(ValueError, match=msg): Timedelta(1, unit=unit) diff --git a/pandas/tests/tslibs/test_resolution.py b/pandas/tests/tslibs/test_resolution.py index 7680beb0b8eec..61262812e6cb8 100644 --- a/pandas/tests/tslibs/test_resolution.py +++ b/pandas/tests/tslibs/test_resolution.py @@ -59,6 +59,6 @@ def test_A_deprecated_from_attrname_to_abbrevs(freq): @pytest.mark.parametrize("freq", ["H", "T", "S", "L", "U", "N"]) def test_reso_abbrev_H_T_S_L_U_N_raises(freq): - msg = f"Resolution abbreviation '{freq}' is no longer supported." + msg = f"Invalid frequency: {freq}" with pytest.raises(ValueError, match=msg): Resolution.get_reso_from_freqstr(freq) From b36b1d373ea3e06789f79694950dd8e4246b97f2 Mon Sep 17 00:00:00 2001 From: Natalia Mokeeva Date: Fri, 1 Mar 2024 01:00:00 +0100 Subject: [PATCH 5/9] fix tests --- pandas/tests/arrays/test_datetimes.py | 2 +- .../tests/indexes/period/test_period_range.py | 2 +- .../indexes/timedeltas/test_constructors.py | 8 -------- .../timedeltas/test_timedelta_range.py | 8 ++++---- pandas/tests/resample/test_period_index.py | 20 +++++++++---------- pandas/tests/scalar/period/test_asfreq.py | 12 +++++------ .../scalar/timedelta/test_constructors.py | 6 +++--- pandas/tests/tslibs/test_to_offset.py | 2 -- 8 files changed, 24 insertions(+), 36 deletions(-) diff --git a/pandas/tests/arrays/test_datetimes.py b/pandas/tests/arrays/test_datetimes.py index 8f0576cc65a27..067756a91744b 100644 --- a/pandas/tests/arrays/test_datetimes.py +++ b/pandas/tests/arrays/test_datetimes.py @@ -790,7 +790,7 @@ def test_date_range_frequency_M_Q_Y_A_deprecated(self, freq, freq_depr): result = pd.date_range("1/1/2000", periods=4, freq=freq_depr) tm.assert_index_equal(result, expected) - @pytest.mark.parametrize("freq_depr", ["2H", "2CBH", "2MIN", "2S", "2mS", "2Us"]) + @pytest.mark.parametrize("freq_depr", ["2CBH", "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 " diff --git a/pandas/tests/indexes/period/test_period_range.py b/pandas/tests/indexes/period/test_period_range.py index 6f8e6d07da8bf..965c2b8e4238d 100644 --- a/pandas/tests/indexes/period/test_period_range.py +++ b/pandas/tests/indexes/period/test_period_range.py @@ -222,7 +222,7 @@ def test_a_deprecated_from_time_series(self, freq, freq_depr): with tm.assert_produces_warning(FutureWarning, match=msg): period_range(freq=freq_depr, start="1/1/2001", end="12/1/2009") - @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 " diff --git a/pandas/tests/indexes/timedeltas/test_constructors.py b/pandas/tests/indexes/timedeltas/test_constructors.py index 2d8536b48378e..0510700bb64d7 100644 --- a/pandas/tests/indexes/timedeltas/test_constructors.py +++ b/pandas/tests/indexes/timedeltas/test_constructors.py @@ -289,11 +289,3 @@ def test_from_categorical(self): ci = pd.CategoricalIndex(tdi) result = TimedeltaIndex(ci) tm.assert_index_equal(result, tdi) - - @pytest.mark.parametrize("unit", ["H", "T", "t", "S", "L", "l", "U", "u", "N", "n"]) - def test_unit_H_T_S_L_N_U_raises(self, unit): - msg = f"Invalid frequency: {unit}" - depr_msg = "The 'unit' keyword in TimedeltaIndex construction is deprecated" - with pytest.raises(ValueError, match=msg): - with tm.assert_produces_warning(FutureWarning, match=depr_msg): - TimedeltaIndex([1, 3, 7], unit) diff --git a/pandas/tests/indexes/timedeltas/test_timedelta_range.py b/pandas/tests/indexes/timedeltas/test_timedelta_range.py index df0809a88736a..e5d04a78dddfd 100644 --- a/pandas/tests/indexes/timedeltas/test_timedelta_range.py +++ b/pandas/tests/indexes/timedeltas/test_timedelta_range.py @@ -42,9 +42,9 @@ def test_timedelta_range(self): result = timedelta_range("0 days", freq="30min", periods=50) tm.assert_index_equal(result, expected) - @pytest.mark.parametrize("unit", ["H", "T", "t", "S", "L", "l", "U", "u", "N", "n"]) - def test_timedelta_unit_H_T_S_L_U_N_raises(self, unit): - msg = f"Invalid frequency: {unit}" + @pytest.mark.parametrize("unit", ["T", "t", "L", "l", "U", "u", "N", "n"]) + def test_timedelta_unit_T_L_U_N_raises(self, unit): + msg = f"invalid unit abbreviation: {unit}" with pytest.raises(ValueError, match=msg): to_timedelta(np.arange(5), unit=unit) @@ -126,6 +126,6 @@ def test_timedelta_range_infer_freq(self): ], ) def test_timedelta_range_deprecated_freq(self, freq_depr, start, end): - msg =f"Invalid frequency: {freq_depr[-1]}" + msg = f"Invalid frequency: {freq_depr}" with pytest.raises(ValueError, match=msg): timedelta_range(start=start, end=end, freq=freq_depr) diff --git a/pandas/tests/resample/test_period_index.py b/pandas/tests/resample/test_period_index.py index 6fdc398b13835..dd058ada60974 100644 --- a/pandas/tests/resample/test_period_index.py +++ b/pandas/tests/resample/test_period_index.py @@ -982,22 +982,20 @@ def test_sum_min_count(self): def test_resample_t_l_deprecated(self): # GH#52536 - msg_t = "'T' is deprecated and will be removed in a future version." - msg_l = "'L' is deprecated and will be removed in a future version." + msg_t = "Invalid frequency: T" + msg_l = "Invalid frequency: L" - with tm.assert_produces_warning(FutureWarning, match=msg_l): - rng_l = period_range( + with pytest.raises(ValueError, match=msg_l): + period_range( "2020-01-01 00:00:00 00:00", "2020-01-01 00:00:00 00:01", freq="L" ) + rng_l = period_range( + "2020-01-01 00:00:00 00:00", "2020-01-01 00:00:00 00:01", freq="ms" + ) ser = Series(np.arange(len(rng_l)), index=rng_l) - rng = period_range( - "2020-01-01 00:00:00 00:00", "2020-01-01 00:00:00 00:01", freq="min" - ) - expected = Series([29999.5, 60000.0], index=rng) - with tm.assert_produces_warning(FutureWarning, match=msg_t): - result = ser.resample("T").mean() - tm.assert_series_equal(result, expected) + with pytest.raises(ValueError, match=msg_t): + ser.resample("T").mean() @pytest.mark.parametrize( "freq, freq_depr, freq_res, freq_depr_res, data", diff --git a/pandas/tests/scalar/period/test_asfreq.py b/pandas/tests/scalar/period/test_asfreq.py index 73c4d8061c257..bfc6cfa02ff61 100644 --- a/pandas/tests/scalar/period/test_asfreq.py +++ b/pandas/tests/scalar/period/test_asfreq.py @@ -110,18 +110,18 @@ 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 = "'H' is deprecated and will be removed in a future version." - with tm.assert_produces_warning(FutureWarning, match=msg): + msg = "Invalid frequency: H" + 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 - msg = "'T' is deprecated and will be removed in a future version." - with tm.assert_produces_warning(FutureWarning, match=msg): + msg = "Invalid frequency: T" + 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 = "'S' is deprecated and will be removed in a future version." - with tm.assert_produces_warning(FutureWarning, match=msg): + msg = "Invalid frequency: S" + with pytest.raises(ValueError, match=msg): assert ival_A.asfreq("S", "S") == ival_A_to_S_start assert ival_A.asfreq("S", "E") == ival_A_to_S_end diff --git a/pandas/tests/scalar/timedelta/test_constructors.py b/pandas/tests/scalar/timedelta/test_constructors.py index b4dfb69209249..c26f978e9a137 100644 --- a/pandas/tests/scalar/timedelta/test_constructors.py +++ b/pandas/tests/scalar/timedelta/test_constructors.py @@ -141,9 +141,9 @@ def test_unit_parser(self, unit, np_unit, wrapper): result = Timedelta(f"2{unit}") assert result == expected - @pytest.mark.parametrize("unit", ["H", "T", "t", "S", "L", "l", "U", "u", "N", "n"]) - def test_unit_H_T_S_L_N_U_raises(self, unit): - msg = f"Invalid frequency: {unit}" + @pytest.mark.parametrize("unit", ["T", "t", "L", "l", "U", "u", "N", "n"]) + def test_unit_T_L_N_U_raises(self, unit): + msg = f"invalid unit abbreviation: {unit}" with pytest.raises(ValueError, match=msg): Timedelta(1, unit=unit) diff --git a/pandas/tests/tslibs/test_to_offset.py b/pandas/tests/tslibs/test_to_offset.py index ad4e9e2bcf38a..bc16a6b810519 100644 --- a/pandas/tests/tslibs/test_to_offset.py +++ b/pandas/tests/tslibs/test_to_offset.py @@ -203,10 +203,8 @@ def test_to_offset_lowercase_frequency_deprecated(freq_depr): @pytest.mark.parametrize( "freq_depr", [ - "2H", "2BH", "2MIN", - "2S", "2Us", "2NS", ], From 3a9d9267c56c1d58b03f28b6cbb010aea93b3b6e Mon Sep 17 00:00:00 2001 From: Natalia Mokeeva Date: Fri, 1 Mar 2024 16:30:32 +0100 Subject: [PATCH 6/9] correct def get_reso_from_freqstr, fix tests --- pandas/_libs/tslibs/dtypes.pyx | 4 +++ .../indexes/datetimes/test_date_range.py | 29 ++++--------------- pandas/tests/tslibs/test_resolution.py | 2 +- 3 files changed, 10 insertions(+), 25 deletions(-) diff --git a/pandas/_libs/tslibs/dtypes.pyx b/pandas/_libs/tslibs/dtypes.pyx index 151e87aa211a7..e8c619cafe996 100644 --- a/pandas/_libs/tslibs/dtypes.pyx +++ b/pandas/_libs/tslibs/dtypes.pyx @@ -484,6 +484,10 @@ class Resolution(Enum): """ cdef: str abbrev + if freq in {"H", "T", "S", "L", "U", "N"}: + raise ValueError( + f"Frequency \'{freq}\' is no longer supported." + ) try: if freq in c_DEPR_ABBREVS: abbrev = c_DEPR_ABBREVS[freq] diff --git a/pandas/tests/indexes/datetimes/test_date_range.py b/pandas/tests/indexes/datetimes/test_date_range.py index e26f35f4e8258..73745b698ff82 100644 --- a/pandas/tests/indexes/datetimes/test_date_range.py +++ b/pandas/tests/indexes/datetimes/test_date_range.py @@ -772,30 +772,11 @@ def test_freq_dateoffset_with_relateivedelta_nanos(self): ) tm.assert_index_equal(result, expected) - @pytest.mark.parametrize( - "freq,freq_depr", - [ - ("h", "H"), - ("2min", "2T"), - ("1s", "1S"), - ("2ms", "2L"), - ("1us", "1U"), - ("2ns", "2N"), - ], - ) - def test_frequencies_H_T_S_L_U_N_deprecated(self, freq, freq_depr): - # GH#52536 - freq_msg = re.split("[0-9]*", freq, maxsplit=1)[1] - freq_depr_msg = re.split("[0-9]*", freq_depr, maxsplit=1)[1] - msg = ( - f"'{freq_depr_msg}' is deprecated and will be removed in a future version, " - ) - f"please use '{freq_msg}' instead" - - expected = date_range("1/1/2000", periods=2, freq=freq) - with tm.assert_produces_warning(FutureWarning, match=msg): - result = date_range("1/1/2000", periods=2, freq=freq_depr) - tm.assert_index_equal(result, expected) + @pytest.mark.parametrize("freq", ["H", "2T", "1S", "2L", "1U", "2N"]) + def test_frequency_H_T_S_L_U_N_raises(self, freq): + msg = f"Invalid frequency: {freq}" + with pytest.raises(ValueError, match=msg): + date_range("1/1/2000", periods=2, freq=freq) @pytest.mark.parametrize( "freq,freq_depr", diff --git a/pandas/tests/tslibs/test_resolution.py b/pandas/tests/tslibs/test_resolution.py index 61262812e6cb8..410c7e40085f5 100644 --- a/pandas/tests/tslibs/test_resolution.py +++ b/pandas/tests/tslibs/test_resolution.py @@ -59,6 +59,6 @@ def test_A_deprecated_from_attrname_to_abbrevs(freq): @pytest.mark.parametrize("freq", ["H", "T", "S", "L", "U", "N"]) def test_reso_abbrev_H_T_S_L_U_N_raises(freq): - msg = f"Invalid frequency: {freq}" + msg = f"Frequency '{freq}' is no longer supported." with pytest.raises(ValueError, match=msg): Resolution.get_reso_from_freqstr(freq) From 5fc3442df0e0ea95393af017079af5099dd67b15 Mon Sep 17 00:00:00 2001 From: Natalia Mokeeva Date: Wed, 20 Mar 2024 18:53:40 +0100 Subject: [PATCH 7/9] fix test --- pandas/tests/tslibs/test_resolution.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/pandas/tests/tslibs/test_resolution.py b/pandas/tests/tslibs/test_resolution.py index 410c7e40085f5..beb17818b0575 100644 --- a/pandas/tests/tslibs/test_resolution.py +++ b/pandas/tests/tslibs/test_resolution.py @@ -8,8 +8,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 @@ -48,15 +46,6 @@ def test_get_attrname_from_abbrev(freqstr, expected): assert reso.attrname == expected -@pytest.mark.parametrize("freq", ["A", "A-NOV"]) -def test_A_deprecated_from_attrname_to_abbrevs(freq): - # GH#52536 - msg = f"'{freq}' is deprecated and will be removed in a future version." - - with tm.assert_produces_warning(FutureWarning, match=msg): - Resolution.get_reso_from_freqstr(freq) - - @pytest.mark.parametrize("freq", ["H", "T", "S", "L", "U", "N"]) def test_reso_abbrev_H_T_S_L_U_N_raises(freq): msg = f"Frequency '{freq}' is no longer supported." From 8ff702fabc9ab39f05aeeba0a8e909bb283fb448 Mon Sep 17 00:00:00 2001 From: Natalia Mokeeva Date: Wed, 20 Mar 2024 21:44:33 +0100 Subject: [PATCH 8/9] fix benchmarks/timeseries.py --- asv_bench/benchmarks/timeseries.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/asv_bench/benchmarks/timeseries.py b/asv_bench/benchmarks/timeseries.py index 8e1deb99a66a4..06f488f7baaaf 100644 --- a/asv_bench/benchmarks/timeseries.py +++ b/asv_bench/benchmarks/timeseries.py @@ -183,7 +183,7 @@ def setup(self): self.dt_ts = Series(5, rng3, dtype="datetime64[ns]") def time_resample(self): - self.dt_ts.resample("1S").last() + self.dt_ts.resample("1s").last() class AsOf: From 4ce88d5f2bb7e54fe4fa122512512ac1c2861048 Mon Sep 17 00:00:00 2001 From: Natalia Mokeeva Date: Thu, 21 Mar 2024 18:14:48 +0100 Subject: [PATCH 9/9] return units H, S back to c_DEPR_ABBREV, fix and add tests, add nites to v3.0.0 --- doc/source/whatsnew/v3.0.0.rst | 2 + pandas/_libs/tslibs/dtypes.pyx | 4 +- pandas/core/tools/timedeltas.py | 13 ++-- pandas/tests/arrays/test_datetimes.py | 2 +- .../indexes/datetimes/test_date_range.py | 2 +- .../tests/indexes/period/test_constructors.py | 10 ++++ .../tests/indexes/period/test_period_range.py | 2 +- .../timedeltas/test_timedelta_range.py | 59 +++++++++++++++++-- pandas/tests/scalar/period/test_asfreq.py | 8 +-- .../scalar/timedelta/test_constructors.py | 10 ++++ pandas/tests/tslibs/test_resolution.py | 15 ++++- pandas/tests/tslibs/test_to_offset.py | 2 + 12 files changed, 106 insertions(+), 23 deletions(-) diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst index f3fcdcdb79ed6..fec22ce7c521b 100644 --- a/doc/source/whatsnew/v3.0.0.rst +++ b/doc/source/whatsnew/v3.0.0.rst @@ -208,6 +208,8 @@ Removal of prior version deprecations/changes - 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 ``T``, ``L``, ``U``, and ``N`` denoting frequencies in :class:`Minute`, :class:`Second`, :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`) - Enforced silent-downcasting deprecation for :ref:`all relevant methods ` (:issue:`54710`) - In :meth:`DataFrame.stack`, the default value of ``future_stack`` is now ``True``; specifying ``False`` will raise a ``FutureWarning`` (:issue:`55448`) diff --git a/pandas/_libs/tslibs/dtypes.pyx b/pandas/_libs/tslibs/dtypes.pyx index 85b1a07623149..5bfbe211bfd14 100644 --- a/pandas/_libs/tslibs/dtypes.pyx +++ b/pandas/_libs/tslibs/dtypes.pyx @@ -310,8 +310,10 @@ cdef dict c_REVERSE_OFFSET_DEPR_FREQSTR = { # Map deprecated resolution abbreviations to correct resolution abbreviations cdef dict c_DEPR_ABBREVS = { + "H": "h", "BH": "bh", "CBH": "cbh", + "S": "s", } @@ -405,7 +407,7 @@ class Resolution(Enum): """ cdef: str abbrev - if freq in {"H", "T", "S", "L", "U", "N"}: + if freq in {"T", "t", "L", "l", "U", "u", "N", "n"}: raise ValueError( f"Frequency \'{freq}\' is no longer supported." ) diff --git a/pandas/core/tools/timedeltas.py b/pandas/core/tools/timedeltas.py index 409a27ea64488..296168fe7e725 100644 --- a/pandas/core/tools/timedeltas.py +++ b/pandas/core/tools/timedeltas.py @@ -112,18 +112,17 @@ def to_timedelta( * 'W' * 'D' / 'days' / 'day' * 'hours' / 'hour' / 'hr' / 'h' / 'H' - * 'm' / 'minute' / 'min' / 'minutes' / 'T' + * 'm' / 'minute' / 'min' / 'minutes' * 's' / 'seconds' / 'sec' / 'second' / 'S' - * 'ms' / 'milliseconds' / 'millisecond' / 'milli' / 'millis' / 'L' - * 'us' / 'microseconds' / 'microsecond' / 'micro' / 'micros' / 'U' - * 'ns' / 'nanoseconds' / 'nano' / 'nanos' / 'nanosecond' / 'N' + * 'ms' / 'milliseconds' / 'millisecond' / 'milli' / 'millis' + * 'us' / 'microseconds' / 'microsecond' / 'micro' / 'micros' + * 'ns' / 'nanoseconds' / 'nano' / 'nanos' / 'nanosecond' Must not be specified when `arg` contains strings and ``errors="raise"``. .. deprecated:: 2.2.0 - Units 'H', 'T', 'S', 'L', 'U' and 'N' are deprecated and will be removed - in a future version. Please use 'h', 'min', 's', 'ms', 'us', and 'ns' - instead of 'H', 'T', 'S', 'L', 'U' and 'N'. + Units 'H'and 'S' are deprecated and will be removed + in a future version. Please use 'h' and 's'. errors : {'raise', 'coerce'}, default 'raise' - If 'raise', then invalid parsing will raise an exception. diff --git a/pandas/tests/arrays/test_datetimes.py b/pandas/tests/arrays/test_datetimes.py index 373e5d1d49c28..8650be62ae7eb 100644 --- a/pandas/tests/arrays/test_datetimes.py +++ b/pandas/tests/arrays/test_datetimes.py @@ -788,7 +788,7 @@ def test_date_range_frequency_M_Q_Y_A_deprecated(self, freq, freq_depr): result = pd.date_range("1/1/2000", periods=4, freq=freq_depr) tm.assert_index_equal(result, expected) - @pytest.mark.parametrize("freq_depr", ["2CBH", "2MIN", "2mS", "2Us"]) + @pytest.mark.parametrize("freq_depr", ["2H", "2CBH", "2MIN", "2S", "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 " diff --git a/pandas/tests/indexes/datetimes/test_date_range.py b/pandas/tests/indexes/datetimes/test_date_range.py index 6cc882bb55404..d69971ccd8604 100644 --- a/pandas/tests/indexes/datetimes/test_date_range.py +++ b/pandas/tests/indexes/datetimes/test_date_range.py @@ -772,7 +772,7 @@ def test_freq_dateoffset_with_relateivedelta_nanos(self): ) tm.assert_index_equal(result, expected) - @pytest.mark.parametrize("freq", ["H", "2T", "1S", "2L", "1U", "2N"]) + @pytest.mark.parametrize("freq", ["2T", "2L", "1l", "1U", "2N", "2n"]) def test_frequency_H_T_S_L_U_N_raises(self, freq): msg = f"Invalid frequency: {freq}" with pytest.raises(ValueError, match=msg): diff --git a/pandas/tests/indexes/period/test_constructors.py b/pandas/tests/indexes/period/test_constructors.py index 519c09015427e..ec2216c102c3f 100644 --- a/pandas/tests/indexes/period/test_constructors.py +++ b/pandas/tests/indexes/period/test_constructors.py @@ -60,6 +60,16 @@ def test_period_index_from_datetime_index_invalid_freq(self, freq): with pytest.raises(ValueError, match=msg): rng.to_period() + @pytest.mark.parametrize("freq_depr", ["2T", "1l", "2U", "n"]) + def test_period_index_T_L_U_N_raises(self, freq_depr): + # GH#9586 + msg = f"Invalid frequency: {freq_depr}" + + with pytest.raises(ValueError, match=msg): + period_range("2020-01", "2020-05", freq=freq_depr) + with pytest.raises(ValueError, match=msg): + PeriodIndex(["2020-01", "2020-05"], freq=freq_depr) + class TestPeriodIndex: def test_from_ordinals(self): diff --git a/pandas/tests/indexes/period/test_period_range.py b/pandas/tests/indexes/period/test_period_range.py index d6072a93c0622..fb200d071951e 100644 --- a/pandas/tests/indexes/period/test_period_range.py +++ b/pandas/tests/indexes/period/test_period_range.py @@ -205,7 +205,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", ["2MIN", "2US", "2NS"]) + @pytest.mark.parametrize("freq_depr", ["2H", "2MIN", "2S", "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 " diff --git a/pandas/tests/indexes/timedeltas/test_timedelta_range.py b/pandas/tests/indexes/timedeltas/test_timedelta_range.py index e5d04a78dddfd..1b645e2bc607f 100644 --- a/pandas/tests/indexes/timedeltas/test_timedelta_range.py +++ b/pandas/tests/indexes/timedeltas/test_timedelta_range.py @@ -3,6 +3,7 @@ from pandas import ( Timedelta, + TimedeltaIndex, timedelta_range, to_timedelta, ) @@ -42,6 +43,17 @@ def test_timedelta_range(self): result = timedelta_range("0 days", freq="30min", periods=50) tm.assert_index_equal(result, expected) + @pytest.mark.parametrize("depr_unit, unit", [("H", "hour"), ("S", "second")]) + def test_timedelta_units_H_S_deprecated(self, depr_unit, unit): + # GH#52536 + depr_msg = ( + f"'{depr_unit}' is deprecated and will be removed in a future version." + ) + expected = to_timedelta(np.arange(5), unit=unit) + with tm.assert_produces_warning(FutureWarning, match=depr_msg): + result = to_timedelta(np.arange(5), unit=depr_unit) + tm.assert_index_equal(result, expected) + @pytest.mark.parametrize("unit", ["T", "t", "L", "l", "U", "u", "N", "n"]) def test_timedelta_unit_T_L_U_N_raises(self, unit): msg = f"invalid unit abbreviation: {unit}" @@ -58,12 +70,20 @@ def test_linspace_behavior(self, periods, freq): expected = timedelta_range(start="0 days", end="4 days", freq=freq) tm.assert_index_equal(result, expected) - @pytest.mark.parametrize("msg_freq, freq", [("H", "19H12min"), ("T", "19h12T")]) - def test_timedelta_range_H_T_raises(self, msg_freq, freq): - msg = f"Invalid frequency: {msg_freq}" + def test_timedelta_range_H_deprecated(self): + # GH#52536 + msg = "'H' is deprecated and will be removed in a future version." + + 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) + + def test_timedelta_range_T_raises(self): + msg = "Invalid frequency: T" with pytest.raises(ValueError, match=msg): - timedelta_range(start="0 days", end="4 days", freq=freq) + timedelta_range(start="0 days", end="4 days", freq="19h12T") def test_errors(self): # not enough params @@ -111,12 +131,39 @@ def test_timedelta_range_infer_freq(self): assert result.freq is None @pytest.mark.parametrize( - "freq_depr, start, end", + "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", + [ + ( + "3.5l", + "05:03:01", + "05:03:10", ), ( "2.5T", @@ -125,7 +172,7 @@ def test_timedelta_range_infer_freq(self): ), ], ) - def test_timedelta_range_deprecated_freq(self, freq_depr, start, end): + def test_timedelta_range_removed_freq(self, freq_depr, start, end): msg = f"Invalid frequency: {freq_depr}" with pytest.raises(ValueError, match=msg): timedelta_range(start=start, end=end, freq=freq_depr) diff --git a/pandas/tests/scalar/period/test_asfreq.py b/pandas/tests/scalar/period/test_asfreq.py index bfc6cfa02ff61..1a21d234f1d50 100644 --- a/pandas/tests/scalar/period/test_asfreq.py +++ b/pandas/tests/scalar/period/test_asfreq.py @@ -110,8 +110,8 @@ 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 = "Invalid frequency: H" - with pytest.raises(ValueError, match=msg): + msg = "'H' is deprecated and will be removed in a future version." + with tm.assert_produces_warning(FutureWarning, 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 @@ -120,8 +120,8 @@ def test_conv_annual(self): 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 = "Invalid frequency: S" - with pytest.raises(ValueError, match=msg): + msg = "'S' is deprecated and will be removed in a future version." + with tm.assert_produces_warning(FutureWarning, match=msg): assert ival_A.asfreq("S", "S") == ival_A_to_S_start assert ival_A.asfreq("S", "E") == ival_A_to_S_end diff --git a/pandas/tests/scalar/timedelta/test_constructors.py b/pandas/tests/scalar/timedelta/test_constructors.py index c26f978e9a137..c69f572c92bf2 100644 --- a/pandas/tests/scalar/timedelta/test_constructors.py +++ b/pandas/tests/scalar/timedelta/test_constructors.py @@ -32,6 +32,16 @@ def test_unit_m_y_raises(self, unit): with pytest.raises(ValueError, match=msg): to_timedelta([1, 2], unit) + @pytest.mark.parametrize("unit", ["h", "s"]) + def test_units_H_S_deprecated(self, unit): + # GH#52536 + msg = f"'{unit.upper()}' is deprecated and will be removed in a future version." + + expected = Timedelta(1, unit=unit) + with tm.assert_produces_warning(FutureWarning, match=msg): + result = Timedelta(1, unit=unit.upper()) + tm.assert_equal(result, expected) + @pytest.mark.parametrize( "unit, np_unit", [(value, "W") for value in ["W", "w"]] diff --git a/pandas/tests/tslibs/test_resolution.py b/pandas/tests/tslibs/test_resolution.py index beb17818b0575..e9da6b3cf991c 100644 --- a/pandas/tests/tslibs/test_resolution.py +++ b/pandas/tests/tslibs/test_resolution.py @@ -8,6 +8,8 @@ ) from pandas._libs.tslibs.dtypes import NpyDatetimeUnit +import pandas._testing as tm + def test_get_resolution_nano(): # don't return the fallback RESO_DAY @@ -46,8 +48,17 @@ def test_get_attrname_from_abbrev(freqstr, expected): assert reso.attrname == expected -@pytest.mark.parametrize("freq", ["H", "T", "S", "L", "U", "N"]) -def test_reso_abbrev_H_T_S_L_U_N_raises(freq): +@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." + + with tm.assert_produces_warning(FutureWarning, match=msg): + Resolution.get_reso_from_freqstr(freq) + + +@pytest.mark.parametrize("freq", ["T", "t", "L", "U", "N", "n"]) +def test_reso_abbrev_T_L_U_N_raises(freq): msg = f"Frequency '{freq}' is no longer supported." with pytest.raises(ValueError, match=msg): Resolution.get_reso_from_freqstr(freq) diff --git a/pandas/tests/tslibs/test_to_offset.py b/pandas/tests/tslibs/test_to_offset.py index bc16a6b810519..ad4e9e2bcf38a 100644 --- a/pandas/tests/tslibs/test_to_offset.py +++ b/pandas/tests/tslibs/test_to_offset.py @@ -203,8 +203,10 @@ def test_to_offset_lowercase_frequency_deprecated(freq_depr): @pytest.mark.parametrize( "freq_depr", [ + "2H", "2BH", "2MIN", + "2S", "2Us", "2NS", ],