From e750d5005511fdf915ef0836fe5e0194148a61db Mon Sep 17 00:00:00 2001 From: Richard Shadrach Date: Mon, 18 Aug 2025 17:05:12 -0400 Subject: [PATCH 1/7] CLN: Replace warnings with Pandas4Warning --- pandas/_libs/tslibs/offsets.pyx | 2 ++ pandas/_libs/tslibs/period.pyx | 1 + pandas/_libs/tslibs/timedeltas.pyx | 1 + pandas/core/arrays/arrow/array.py | 1 + pandas/core/arrays/string_.py | 4 +++- pandas/core/arrays/string_arrow.py | 1 + pandas/core/frame.py | 4 ++-- pandas/core/generic.py | 1 + pandas/core/internals/__init__.py | 8 +++++--- pandas/core/resample.py | 1 + pandas/core/series.py | 7 ++++--- pandas/io/stata.py | 3 ++- 12 files changed, 24 insertions(+), 10 deletions(-) diff --git a/pandas/_libs/tslibs/offsets.pyx b/pandas/_libs/tslibs/offsets.pyx index f07efa91cda0e..d1af1b24a2b4e 100644 --- a/pandas/_libs/tslibs/offsets.pyx +++ b/pandas/_libs/tslibs/offsets.pyx @@ -5192,6 +5192,7 @@ def _warn_about_deprecated_aliases(name: str, is_period: bool) -> str: if name in _lite_rule_alias: return name if name in c_PERIOD_AND_OFFSET_DEPR_FREQSTR: + # TODO: Enforce in 3.0 (#59240) warnings.warn( f"\'{name}\' is deprecated and will be removed " f"in a future version, please use " @@ -5206,6 +5207,7 @@ def _warn_about_deprecated_aliases(name: str, is_period: bool) -> str: if name == _name: continue if _name in c_PERIOD_AND_OFFSET_DEPR_FREQSTR.values(): + # TODO: Enforce in 3.0 (#59240) warnings.warn( f"\'{name}\' is deprecated and will be removed " f"in a future version, please use " diff --git a/pandas/_libs/tslibs/period.pyx b/pandas/_libs/tslibs/period.pyx index 22c1b98bc54bb..95a18f8cb2cad 100644 --- a/pandas/_libs/tslibs/period.pyx +++ b/pandas/_libs/tslibs/period.pyx @@ -3018,6 +3018,7 @@ class Period(_Period): # GH#53446 import warnings + # TODO: Enforce in 3.0 (#53511) from pandas.util._exceptions import find_stack_level warnings.warn( "Period with BDay freq is deprecated and will be removed " diff --git a/pandas/_libs/tslibs/timedeltas.pyx b/pandas/_libs/tslibs/timedeltas.pyx index c719219639689..21ea1fcd1755a 100644 --- a/pandas/_libs/tslibs/timedeltas.pyx +++ b/pandas/_libs/tslibs/timedeltas.pyx @@ -722,6 +722,7 @@ cpdef inline str parse_timedelta_unit(str unit): elif unit == "M": return unit elif unit in c_DEPR_UNITS: + # TODO: Enforce in 3.0 (#59240) warnings.warn( f"\'{unit}\' is deprecated and will be removed in a " f"future version. Please use \'{c_DEPR_UNITS.get(unit)}\' " diff --git a/pandas/core/arrays/arrow/array.py b/pandas/core/arrays/arrow/array.py index 109f674fb9043..3eb256815f93b 100644 --- a/pandas/core/arrays/arrow/array.py +++ b/pandas/core/arrays/arrow/array.py @@ -930,6 +930,7 @@ def _logical_method(self, other, op) -> Self: and isinstance(other, np.ndarray) and other.dtype == bool ): + # TODO: Enforce in 3.0 (#60234) # GH#60234 backward compatibility for the move to StringDtype in 3.0 op_name = op.__name__[1:].strip("_") warnings.warn( diff --git a/pandas/core/arrays/string_.py b/pandas/core/arrays/string_.py index 4d91f33a8df87..2de5495bd0b62 100644 --- a/pandas/core/arrays/string_.py +++ b/pandas/core/arrays/string_.py @@ -29,6 +29,7 @@ pa_version_under12p1, ) from pandas.compat.numpy import function as nv +from pandas.errors import Pandas4Warning from pandas.util._decorators import ( doc, set_module, @@ -167,6 +168,7 @@ def __init__( storage = "python" if storage == "pyarrow_numpy": + # TODO: Enforce in 3.0 (#60152) warnings.warn( "The 'pyarrow_numpy' storage option name is deprecated and will be " 'removed in pandas 3.0. Use \'pd.StringDtype(storage="pyarrow", ' @@ -400,7 +402,7 @@ def _logical_method(self, other, op): f"'{op_name}' operations between boolean dtype and {self.dtype} are " "deprecated and will raise in a future version. Explicitly " "cast the strings to a boolean dtype before operating instead.", - FutureWarning, + Pandas4Warning, stacklevel=find_stack_level(), ) return op(other, self.astype(bool)) diff --git a/pandas/core/arrays/string_arrow.py b/pandas/core/arrays/string_arrow.py index 39e84a3abdcf5..77e0ad24ce4f3 100644 --- a/pandas/core/arrays/string_arrow.py +++ b/pandas/core/arrays/string_arrow.py @@ -228,6 +228,7 @@ def insert(self, loc: int, item) -> ArrowStringArray: def _convert_bool_result(self, values, na=lib.no_default, method_name=None): if na is not lib.no_default and not isna(na) and not isinstance(na, bool): + # TODO: Enforce in 3.0 (#59615) # GH#59561 warnings.warn( f"Allowing a non-bool 'na' in obj.str.{method_name} is deprecated " diff --git a/pandas/core/frame.py b/pandas/core/frame.py index ec8c8116e5aee..23b248b913bd8 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -727,7 +727,7 @@ def __init__( f"Passing a {type(data).__name__} to {type(self).__name__} " "is deprecated and will raise in a future version. " "Use public APIs instead.", - DeprecationWarning, + Pandas4Warning, stacklevel=2, ) @@ -9887,7 +9887,7 @@ def stack( "removed in a future version of pandas. See the What's New notes " "for pandas 2.1.0 for details. Do not specify the future_stack " "argument to adopt the new implementation and silence this warning.", - FutureWarning, + Pandas4Warning, stacklevel=find_stack_level(), ) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 33fcc94f906d5..6557388d88f20 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -9133,6 +9133,7 @@ def resample( from pandas.core.resample import get_resampler if convention is not lib.no_default: + # TODO: Enforce in 3.0 (#55968) warnings.warn( f"The 'convention' keyword in {type(self).__name__}.resample is " "deprecated and will be removed in a future version. " diff --git a/pandas/core/internals/__init__.py b/pandas/core/internals/__init__.py index 12999a44a446b..9031cbae0b6dc 100644 --- a/pandas/core/internals/__init__.py +++ b/pandas/core/internals/__init__.py @@ -1,3 +1,5 @@ +from pandas.errors import Pandas4Warning + from pandas.core.internals.api import make_block # 2023-09-18 pyarrow uses this from pandas.core.internals.concat import concatenate_managers from pandas.core.internals.managers import ( @@ -21,11 +23,11 @@ def __getattr__(name: str): import warnings if name == "create_block_manager_from_blocks": - # GH#33892 + # GH#33892, GH#58715 warnings.warn( f"{name} is deprecated and will be removed in a future version. " "Use public APIs instead.", - FutureWarning, + Pandas4Warning, # https://github.com/pandas-dev/pandas/pull/55139#pullrequestreview-1720690758 # on hard-coding stacklevel stacklevel=2, @@ -42,7 +44,7 @@ def __getattr__(name: str): warnings.warn( f"{name} is deprecated and will be removed in a future version. " "Use public APIs instead.", - FutureWarning, + Pandas4Warning, # https://github.com/pandas-dev/pandas/pull/55139#pullrequestreview-1720690758 # on hard-coding stacklevel stacklevel=2, diff --git a/pandas/core/resample.py b/pandas/core/resample.py index 4bca2528490ff..c4035ee941fbe 100644 --- a/pandas/core/resample.py +++ b/pandas/core/resample.py @@ -1945,6 +1945,7 @@ class PeriodIndexResampler(DatetimeIndexResampler): @property def _resampler_for_grouping(self): + # TODO: Enforce in 3.0 (#55968) warnings.warn( "Resampling a groupby with a PeriodIndex is deprecated. " "Cast to DatetimeIndex before resampling instead.", diff --git a/pandas/core/series.py b/pandas/core/series.py index 6055e65c2786b..ff57e5aaee92d 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -384,7 +384,7 @@ def __init__( f"Passing a {type(data).__name__} to {type(self).__name__} " "is deprecated and will raise in a future version. " "Use public APIs instead.", - DeprecationWarning, + Pandas4Warning, stacklevel=2, ) data = data.copy(deep=False) @@ -408,7 +408,7 @@ def __init__( f"Passing a {type(data).__name__} to {type(self).__name__} " "is deprecated and will raise in a future version. " "Use public APIs instead.", - DeprecationWarning, + Pandas4Warning, stacklevel=2, ) @@ -477,7 +477,7 @@ def __init__( f"Passing a {type(data).__name__} to {type(self).__name__} " "is deprecated and will raise in a future version. " "Use public APIs instead.", - DeprecationWarning, + Pandas4Warning, stacklevel=2, ) allow_mgr = True @@ -4432,6 +4432,7 @@ def map( if "arg" in kwargs: # `.map(arg=my_func)` func = kwargs.pop("arg") + # TODO: Enforce in 3.0 (#61264) warnings.warn( "The parameter `arg` has been renamed to `func`, and it " "will stop being supported in a future version of pandas.", diff --git a/pandas/io/stata.py b/pandas/io/stata.py index 5b102a567f409..27e1bac7ac8e0 100644 --- a/pandas/io/stata.py +++ b/pandas/io/stata.py @@ -39,6 +39,7 @@ from pandas.errors import ( CategoricalConversionWarning, InvalidColumnName, + Pandas4Warning, PossiblePrecisionLoss, ValueLabelTypeMismatch, ) @@ -398,7 +399,7 @@ def parse_dates_safe( "Converting object-dtype columns of datetimes to datetime64 when " "writing to stata is deprecated. Call " "`df=df.infer_objects(copy=False)` before writing to stata instead.", - FutureWarning, + Pandas4Warning, stacklevel=find_stack_level(), ) if delta: From a7c7798146897588bb5e16de7d5012d2ef6db277 Mon Sep 17 00:00:00 2001 From: Richard Shadrach Date: Wed, 20 Aug 2025 21:41:01 -0400 Subject: [PATCH 2/7] Update tests --- pandas/_libs/tslibs/offsets.pyx | 12 ++++++++---- pandas/_libs/tslibs/timedeltas.pyx | 8 ++++++-- pandas/core/arrays/string_.py | 4 ++-- pandas/core/dtypes/common.py | 15 ++++++++------- pandas/core/internals/__init__.py | 4 ++-- pandas/core/internals/api.py | 2 +- pandas/core/internals/blocks.py | 3 ++- pandas/core/series.py | 4 ++-- pandas/tests/arrays/test_datetimes.py | 5 +++-- pandas/tests/dtypes/test_common.py | 9 +++++---- pandas/tests/dtypes/test_dtypes.py | 5 +++-- pandas/tests/dtypes/test_inference.py | 3 ++- pandas/tests/extension/decimal/test_decimal.py | 4 +++- pandas/tests/frame/methods/test_astype.py | 3 ++- pandas/tests/frame/methods/test_reindex.py | 3 ++- pandas/tests/frame/test_block_internals.py | 6 ++++-- pandas/tests/frame/test_stack_unstack.py | 3 ++- .../tests/indexes/datetimes/methods/test_snap.py | 6 ++++-- pandas/tests/indexes/datetimes/test_date_range.py | 7 +++++-- pandas/tests/indexes/period/test_constructors.py | 11 ++++++++--- pandas/tests/indexes/period/test_period_range.py | 6 ++++-- .../tests/indexes/timedeltas/test_constructors.py | 6 ++++-- pandas/tests/indexes/timedeltas/test_indexing.py | 6 ++++-- .../indexes/timedeltas/test_scalar_compat.py | 3 ++- pandas/tests/indexes/timedeltas/test_setops.py | 4 +++- .../indexes/timedeltas/test_timedelta_range.py | 4 +++- pandas/tests/internals/test_api.py | 10 ++++++---- pandas/tests/internals/test_internals.py | 3 ++- pandas/tests/io/parser/test_parse_dates.py | 6 ++++-- pandas/tests/io/test_stata.py | 3 ++- pandas/tests/resample/test_datetime_index.py | 8 ++++---- pandas/tests/scalar/period/test_period.py | 13 +++++++++---- .../tests/scalar/timedelta/test_constructors.py | 3 ++- pandas/tests/scalar/timedelta/test_timedelta.py | 11 +++++++---- pandas/tests/series/methods/test_map.py | 4 +++- pandas/tests/tools/test_to_timedelta.py | 7 +++++-- pandas/tests/tslibs/test_to_offset.py | 7 ++++--- pandas/tests/window/test_groupby.py | 4 +++- pandas/tests/window/test_rolling.py | 3 ++- 39 files changed, 147 insertions(+), 81 deletions(-) diff --git a/pandas/_libs/tslibs/offsets.pyx b/pandas/_libs/tslibs/offsets.pyx index d1af1b24a2b4e..a0d85fc44eb96 100644 --- a/pandas/_libs/tslibs/offsets.pyx +++ b/pandas/_libs/tslibs/offsets.pyx @@ -5192,13 +5192,15 @@ def _warn_about_deprecated_aliases(name: str, is_period: bool) -> str: if name in _lite_rule_alias: return name if name in c_PERIOD_AND_OFFSET_DEPR_FREQSTR: - # TODO: Enforce in 3.0 (#59240) + from pandas.errors import Pandas4Warning + + # https://github.com/pandas-dev/pandas/pull/59240 warnings.warn( f"\'{name}\' is deprecated and will be removed " f"in a future version, please use " f"\'{c_PERIOD_AND_OFFSET_DEPR_FREQSTR.get(name)}\' " f"instead.", - FutureWarning, + Pandas4Warning, stacklevel=find_stack_level(), ) return c_PERIOD_AND_OFFSET_DEPR_FREQSTR[name] @@ -5207,13 +5209,15 @@ def _warn_about_deprecated_aliases(name: str, is_period: bool) -> str: if name == _name: continue if _name in c_PERIOD_AND_OFFSET_DEPR_FREQSTR.values(): - # TODO: Enforce in 3.0 (#59240) + from pandas.errors import Pandas4Warning + + # https://github.com/pandas-dev/pandas/pull/59240 warnings.warn( f"\'{name}\' is deprecated and will be removed " f"in a future version, please use " f"\'{_name}\' " f"instead.", - FutureWarning, + Pandas4Warning, stacklevel=find_stack_level(), ) return _name diff --git a/pandas/_libs/tslibs/timedeltas.pyx b/pandas/_libs/tslibs/timedeltas.pyx index 21ea1fcd1755a..c13b0c4cd78a5 100644 --- a/pandas/_libs/tslibs/timedeltas.pyx +++ b/pandas/_libs/tslibs/timedeltas.pyx @@ -78,7 +78,9 @@ from pandas._libs.tslibs.np_datetime import ( ) from pandas._libs.tslibs.offsets cimport is_tick_object + from pandas._libs.tslibs.offsets import Day + from pandas._libs.tslibs.util cimport ( is_array, is_float_object, @@ -722,12 +724,14 @@ cpdef inline str parse_timedelta_unit(str unit): elif unit == "M": return unit elif unit in c_DEPR_UNITS: - # TODO: Enforce in 3.0 (#59240) + from pandas.errors import Pandas4Warning + + # https://github.com/pandas-dev/pandas/pull/59240 warnings.warn( f"\'{unit}\' is deprecated and will be removed in a " f"future version. Please use \'{c_DEPR_UNITS.get(unit)}\' " f"instead of \'{unit}\'.", - FutureWarning, + Pandas4Warning, stacklevel=find_stack_level(), ) unit = c_DEPR_UNITS[unit] diff --git a/pandas/core/arrays/string_.py b/pandas/core/arrays/string_.py index 2de5495bd0b62..54dbe4a56b7ee 100644 --- a/pandas/core/arrays/string_.py +++ b/pandas/core/arrays/string_.py @@ -29,7 +29,6 @@ pa_version_under12p1, ) from pandas.compat.numpy import function as nv -from pandas.errors import Pandas4Warning from pandas.util._decorators import ( doc, set_module, @@ -398,11 +397,12 @@ def _logical_method(self, other, op): ): # GH#60234 backward compatibility for the move to StringDtype in 3.0 op_name = op.__name__[1:].strip("_") + # TODO: Enforce in 3.0 (#60234) warnings.warn( f"'{op_name}' operations between boolean dtype and {self.dtype} are " "deprecated and will raise in a future version. Explicitly " "cast the strings to a boolean dtype before operating instead.", - Pandas4Warning, + FutureWarning, stacklevel=find_stack_level(), ) return op(other, self.astype(bool)) diff --git a/pandas/core/dtypes/common.py b/pandas/core/dtypes/common.py index 99415b6fc6ec8..03a29a7199e6b 100644 --- a/pandas/core/dtypes/common.py +++ b/pandas/core/dtypes/common.py @@ -21,6 +21,7 @@ lib, ) from pandas._libs.tslibs import conversion +from pandas.errors import Pandas4Warning from pandas.util._exceptions import find_stack_level from pandas.core.dtypes.base import _registry as registry @@ -235,7 +236,7 @@ def is_sparse(arr) -> bool: warnings.warn( "is_sparse is deprecated and will be removed in a future " "version. Check `isinstance(dtype, pd.SparseDtype)` instead.", - DeprecationWarning, + Pandas4Warning, stacklevel=2, ) @@ -370,7 +371,7 @@ def is_datetime64tz_dtype(arr_or_dtype) -> bool: warnings.warn( "is_datetime64tz_dtype is deprecated and will be removed in a future " "version. Check `isinstance(dtype, pd.DatetimeTZDtype)` instead.", - DeprecationWarning, + Pandas4Warning, stacklevel=2, ) if isinstance(arr_or_dtype, DatetimeTZDtype): @@ -466,7 +467,7 @@ def is_period_dtype(arr_or_dtype) -> bool: warnings.warn( "is_period_dtype is deprecated and will be removed in a future version. " "Use `isinstance(dtype, pd.PeriodDtype)` instead", - DeprecationWarning, + Pandas4Warning, stacklevel=2, ) if isinstance(arr_or_dtype, ExtensionDtype): @@ -524,7 +525,7 @@ def is_interval_dtype(arr_or_dtype) -> bool: warnings.warn( "is_interval_dtype is deprecated and will be removed in a future version. " "Use `isinstance(dtype, pd.IntervalDtype)` instead", - DeprecationWarning, + Pandas4Warning, stacklevel=2, ) if isinstance(arr_or_dtype, ExtensionDtype): @@ -578,7 +579,7 @@ def is_categorical_dtype(arr_or_dtype) -> bool: warnings.warn( "is_categorical_dtype is deprecated and will be removed in a future " "version. Use isinstance(dtype, pd.CategoricalDtype) instead", - DeprecationWarning, + Pandas4Warning, stacklevel=2, ) if isinstance(arr_or_dtype, ExtensionDtype): @@ -973,7 +974,7 @@ def is_int64_dtype(arr_or_dtype) -> bool: warnings.warn( "is_int64_dtype is deprecated and will be removed in a future " "version. Use dtype == np.int64 instead.", - DeprecationWarning, + Pandas4Warning, stacklevel=2, ) return _is_dtype_type(arr_or_dtype, classes(np.int64)) @@ -1436,7 +1437,7 @@ def is_bool_dtype(arr_or_dtype) -> bool: "The behavior of is_bool_dtype with an object-dtype Index " "of bool objects is deprecated. In a future version, " "this will return False. Cast the Index to a bool dtype instead.", - DeprecationWarning, + Pandas4Warning, stacklevel=2, ) return True diff --git a/pandas/core/internals/__init__.py b/pandas/core/internals/__init__.py index 9031cbae0b6dc..0ff3933a5c255 100644 --- a/pandas/core/internals/__init__.py +++ b/pandas/core/internals/__init__.py @@ -1,5 +1,3 @@ -from pandas.errors import Pandas4Warning - from pandas.core.internals.api import make_block # 2023-09-18 pyarrow uses this from pandas.core.internals.concat import concatenate_managers from pandas.core.internals.managers import ( @@ -22,6 +20,8 @@ def __getattr__(name: str): # GH#55139 import warnings + from pandas.errors import Pandas4Warning + if name == "create_block_manager_from_blocks": # GH#33892, GH#58715 warnings.warn( diff --git a/pandas/core/internals/api.py b/pandas/core/internals/api.py index 3413403b6ed24..6031d2f61ef42 100644 --- a/pandas/core/internals/api.py +++ b/pandas/core/internals/api.py @@ -171,7 +171,7 @@ def maybe_infer_ndim(values, placement: BlockPlacement, ndim: int | None) -> int """ warnings.warn( "maybe_infer_ndim is deprecated and will be removed in a future version.", - DeprecationWarning, + Pandas4Warning, stacklevel=2, ) return _maybe_infer_ndim(values, placement, ndim) diff --git a/pandas/core/internals/blocks.py b/pandas/core/internals/blocks.py index 72a03d1ff0cde..5468a0de2a41d 100644 --- a/pandas/core/internals/blocks.py +++ b/pandas/core/internals/blocks.py @@ -39,6 +39,7 @@ from pandas.errors import ( AbstractMethodError, OutOfBoundsDatetime, + Pandas4Warning, ) from pandas.util._decorators import cache_readonly from pandas.util._exceptions import find_stack_level @@ -1899,7 +1900,7 @@ def fillna( "need to implement this keyword or an exception will be " "raised. In the interim, the keyword is ignored by " f"{type(self.values).__name__}.", - DeprecationWarning, + Pandas4Warning, stacklevel=find_stack_level(), ) diff --git a/pandas/core/series.py b/pandas/core/series.py index ff57e5aaee92d..fd679731d793a 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -4432,11 +4432,11 @@ def map( if "arg" in kwargs: # `.map(arg=my_func)` func = kwargs.pop("arg") - # TODO: Enforce in 3.0 (#61264) + # https://github.com/pandas-dev/pandas/pull/61264 warnings.warn( "The parameter `arg` has been renamed to `func`, and it " "will stop being supported in a future version of pandas.", - FutureWarning, + Pandas4Warning, stacklevel=find_stack_level(), ) else: diff --git a/pandas/tests/arrays/test_datetimes.py b/pandas/tests/arrays/test_datetimes.py index 944bd84145f05..a2a2d57a33925 100644 --- a/pandas/tests/arrays/test_datetimes.py +++ b/pandas/tests/arrays/test_datetimes.py @@ -11,6 +11,7 @@ import pytest from pandas._libs.tslibs import tz_compare +from pandas.errors import Pandas4Warning from pandas.core.dtypes.dtypes import DatetimeTZDtype @@ -775,7 +776,7 @@ def test_date_range_uppercase_frequency_deprecated(self, freq_depr): ) expected = pd.date_range("1/1/2000", periods=4, freq=freq_depr.lower()) - with tm.assert_produces_warning(FutureWarning, match=depr_msg): + with tm.assert_produces_warning(Pandas4Warning, match=depr_msg): result = pd.date_range("1/1/2000", periods=4, freq=freq_depr) tm.assert_index_equal(result, expected) @@ -804,7 +805,7 @@ def test_date_range_lowercase_frequency_deprecated(self): depr_msg = "'w' is deprecated and will be removed in a future version" expected = pd.date_range("1/1/2000", periods=4, freq="2W") - with tm.assert_produces_warning(FutureWarning, match=depr_msg): + with tm.assert_produces_warning(Pandas4Warning, match=depr_msg): result = pd.date_range("1/1/2000", periods=4, freq="2w") tm.assert_index_equal(result, expected) diff --git a/pandas/tests/dtypes/test_common.py b/pandas/tests/dtypes/test_common.py index cd5050cab8ad5..d16729b088f1d 100644 --- a/pandas/tests/dtypes/test_common.py +++ b/pandas/tests/dtypes/test_common.py @@ -4,6 +4,7 @@ import pytest from pandas.compat import HAS_PYARROW +from pandas.errors import Pandas4Warning import pandas.util._test_decorators as td from pandas.core.dtypes.astype import astype_array @@ -184,7 +185,7 @@ def test_get_dtype_error_catch(func): or func is com.is_categorical_dtype or func is com.is_period_dtype ): - warn = DeprecationWarning + warn = Pandas4Warning with tm.assert_produces_warning(warn, match=msg): assert not func(None) @@ -204,7 +205,7 @@ def test_is_object(): ) def test_is_sparse(check_scipy): msg = "is_sparse is deprecated" - with tm.assert_produces_warning(DeprecationWarning, match=msg): + with tm.assert_produces_warning(Pandas4Warning, match=msg): assert com.is_sparse(SparseArray([1, 2, 3])) assert not com.is_sparse(np.array([1, 2, 3])) @@ -234,7 +235,7 @@ def test_is_datetime64_dtype(): def test_is_datetime64tz_dtype(): msg = "is_datetime64tz_dtype is deprecated" - with tm.assert_produces_warning(DeprecationWarning, match=msg): + with tm.assert_produces_warning(Pandas4Warning, match=msg): assert not com.is_datetime64tz_dtype(object) assert not com.is_datetime64tz_dtype([1, 2, 3]) assert not com.is_datetime64tz_dtype(pd.DatetimeIndex([1, 2, 3])) @@ -250,7 +251,7 @@ def kind(self) -> str: not_tz_dtype = NotTZDtype() msg = "is_datetime64tz_dtype is deprecated" - with tm.assert_produces_warning(DeprecationWarning, match=msg): + with tm.assert_produces_warning(Pandas4Warning, match=msg): assert not com.is_datetime64tz_dtype(not_tz_dtype) assert not com.needs_i8_conversion(not_tz_dtype) diff --git a/pandas/tests/dtypes/test_dtypes.py b/pandas/tests/dtypes/test_dtypes.py index c9d3f83ce9237..38c84e45c6fe8 100644 --- a/pandas/tests/dtypes/test_dtypes.py +++ b/pandas/tests/dtypes/test_dtypes.py @@ -5,6 +5,7 @@ import pytest from pandas._libs.tslibs.dtypes import NpyDatetimeUnit +from pandas.errors import Pandas4Warning from pandas.core.dtypes.base import _registry as registry from pandas.core.dtypes.common import ( @@ -165,7 +166,7 @@ def test_is_dtype(self, dtype): def test_basic(self, dtype): msg = "is_categorical_dtype is deprecated" - with tm.assert_produces_warning(DeprecationWarning, match=msg): + with tm.assert_produces_warning(Pandas4Warning, match=msg): assert is_categorical_dtype(dtype) factor = Categorical(["a", "b", "b", "a", "a", "c", "c", "c"]) @@ -295,7 +296,7 @@ def test_subclass(self): def test_compat(self, dtype): msg = "is_datetime64tz_dtype is deprecated" - with tm.assert_produces_warning(DeprecationWarning, match=msg): + with tm.assert_produces_warning(Pandas4Warning, match=msg): assert is_datetime64tz_dtype(dtype) assert is_datetime64tz_dtype("datetime64[ns, US/Eastern]") assert is_datetime64_any_dtype(dtype) diff --git a/pandas/tests/dtypes/test_inference.py b/pandas/tests/dtypes/test_inference.py index b83a09e7f2e18..1305cf7e1fd9c 100644 --- a/pandas/tests/dtypes/test_inference.py +++ b/pandas/tests/dtypes/test_inference.py @@ -35,6 +35,7 @@ ops as libops, ) from pandas.compat.numpy import np_version_gt2 +from pandas.errors import Pandas4Warning from pandas.core.dtypes import inference from pandas.core.dtypes.cast import find_result_type @@ -1858,7 +1859,7 @@ def test_is_datetime_dtypes(self): assert is_datetime64_any_dtype(ts) assert is_datetime64_any_dtype(tsa) - with tm.assert_produces_warning(DeprecationWarning, match=msg): + with tm.assert_produces_warning(Pandas4Warning, match=msg): assert not is_datetime64tz_dtype("datetime64") assert not is_datetime64tz_dtype("datetime64[ns]") assert not is_datetime64tz_dtype(ts) diff --git a/pandas/tests/extension/decimal/test_decimal.py b/pandas/tests/extension/decimal/test_decimal.py index 5247dfcbb275b..a7fc5061a267d 100644 --- a/pandas/tests/extension/decimal/test_decimal.py +++ b/pandas/tests/extension/decimal/test_decimal.py @@ -5,6 +5,8 @@ import numpy as np import pytest +from pandas.errors import Pandas4Warning + import pandas as pd import pandas._testing as tm from pandas.tests.extension import base @@ -134,7 +136,7 @@ def test_arith_series_with_array(self, data, all_arithmetic_operators): def test_fillna_frame(self, data_missing): msg = "ExtensionArray.fillna added a 'copy' keyword" with tm.assert_produces_warning( - DeprecationWarning, match=msg, check_stacklevel=False + Pandas4Warning, match=msg, check_stacklevel=False ): super().test_fillna_frame(data_missing) diff --git a/pandas/tests/frame/methods/test_astype.py b/pandas/tests/frame/methods/test_astype.py index 59cc0eab2f62e..3a10a08223da5 100644 --- a/pandas/tests/frame/methods/test_astype.py +++ b/pandas/tests/frame/methods/test_astype.py @@ -3,6 +3,7 @@ import numpy as np import pytest +from pandas.errors import Pandas4Warning import pandas.util._test_decorators as td import pandas as pd @@ -718,7 +719,7 @@ def test_astype_ignores_errors_for_extension_dtypes(self, data, dtype, errors): def test_astype_tz_conversion(self): # GH 35973, GH#58998 msg = "'d' is deprecated and will be removed in a future version." - with tm.assert_produces_warning(FutureWarning, match=msg): + with tm.assert_produces_warning(Pandas4Warning, match=msg): val = { "tz": date_range("2020-08-30", freq="d", periods=2, tz="Europe/London") } diff --git a/pandas/tests/frame/methods/test_reindex.py b/pandas/tests/frame/methods/test_reindex.py index f0f01529db58f..3973f47aed240 100644 --- a/pandas/tests/frame/methods/test_reindex.py +++ b/pandas/tests/frame/methods/test_reindex.py @@ -13,6 +13,7 @@ is_platform_windows, ) from pandas.compat.numpy import np_version_gt2 +from pandas.errors import Pandas4Warning import pandas as pd from pandas import ( @@ -756,7 +757,7 @@ def test_reindex_axes(self): ) msg = "'d' is deprecated and will be removed in a future version." - with tm.assert_produces_warning(FutureWarning, match=msg): + with tm.assert_produces_warning(Pandas4Warning, match=msg): time_freq = date_range("2012-01-01", "2012-01-03", freq="d") some_cols = ["a", "b"] diff --git a/pandas/tests/frame/test_block_internals.py b/pandas/tests/frame/test_block_internals.py index f084d16e387a8..f9056a5487da6 100644 --- a/pandas/tests/frame/test_block_internals.py +++ b/pandas/tests/frame/test_block_internals.py @@ -7,6 +7,8 @@ import numpy as np import pytest +from pandas.errors import Pandas4Warning + import pandas as pd from pandas import ( Categorical, @@ -44,14 +46,14 @@ def test_setitem_invalidates_datetime_index_freq(self): def test_cast_internals(self, float_frame): msg = "Passing a BlockManager to DataFrame" with tm.assert_produces_warning( - DeprecationWarning, match=msg, check_stacklevel=False + Pandas4Warning, match=msg, check_stacklevel=False ): casted = DataFrame(float_frame._mgr, dtype=int) expected = DataFrame(float_frame._series, dtype=int) tm.assert_frame_equal(casted, expected) with tm.assert_produces_warning( - DeprecationWarning, match=msg, check_stacklevel=False + Pandas4Warning, match=msg, check_stacklevel=False ): casted = DataFrame(float_frame._mgr, dtype=np.int32) expected = DataFrame(float_frame._series, dtype=np.int32) diff --git a/pandas/tests/frame/test_stack_unstack.py b/pandas/tests/frame/test_stack_unstack.py index 33510abac6ab6..756d454ebd11f 100644 --- a/pandas/tests/frame/test_stack_unstack.py +++ b/pandas/tests/frame/test_stack_unstack.py @@ -6,6 +6,7 @@ import pytest from pandas._libs import lib +from pandas.errors import Pandas4Warning import pandas as pd from pandas import ( @@ -1066,7 +1067,7 @@ def test_stack_datetime_column_multiIndex(self, future_stack): # GH 8039 t = datetime(2014, 1, 1) df = DataFrame([1, 2, 3, 4], columns=MultiIndex.from_tuples([(t, "A", "B")])) - warn = None if future_stack else FutureWarning + warn = None if future_stack else Pandas4Warning msg = "The previous implementation of stack is deprecated" with tm.assert_produces_warning(warn, match=msg): result = df.stack(future_stack=future_stack) diff --git a/pandas/tests/indexes/datetimes/methods/test_snap.py b/pandas/tests/indexes/datetimes/methods/test_snap.py index a3c06ac6257cf..d3d8f76313e7a 100644 --- a/pandas/tests/indexes/datetimes/methods/test_snap.py +++ b/pandas/tests/indexes/datetimes/methods/test_snap.py @@ -1,5 +1,7 @@ import pytest +from pandas.errors import Pandas4Warning + from pandas import ( DatetimeIndex, date_range, @@ -30,7 +32,7 @@ def test_dti_snap(name, tz, unit): result = dti.snap(freq="W-MON") msg = "'w-mon' is deprecated and will be removed in a future version." - with tm.assert_produces_warning(FutureWarning, match=msg): + with tm.assert_produces_warning(Pandas4Warning, match=msg): expected = date_range("12/31/2001", "1/7/2002", name=name, tz=tz, freq="w-mon") expected = expected.repeat([3, 4]) expected = expected.as_unit(unit) @@ -42,7 +44,7 @@ def test_dti_snap(name, tz, unit): result = dti.snap(freq="B") msg = "'b' is deprecated and will be removed in a future version." - with tm.assert_produces_warning(FutureWarning, match=msg): + with tm.assert_produces_warning(Pandas4Warning, match=msg): expected = date_range("1/1/2002", "1/7/2002", name=name, tz=tz, freq="b") expected = expected.repeat([1, 1, 1, 2, 2]) expected = expected.as_unit(unit) diff --git a/pandas/tests/indexes/datetimes/test_date_range.py b/pandas/tests/indexes/datetimes/test_date_range.py index 1ec936f830768..83e1a7a276875 100644 --- a/pandas/tests/indexes/datetimes/test_date_range.py +++ b/pandas/tests/indexes/datetimes/test_date_range.py @@ -20,7 +20,10 @@ MonthEnd, prefix_mapping, ) -from pandas.errors import OutOfBoundsDatetime +from pandas.errors import ( + OutOfBoundsDatetime, + Pandas4Warning, +) import pandas.util._test_decorators as td import pandas as pd @@ -807,7 +810,7 @@ def test_date_range_depr_lowercase_frequency(self, freq, freq_depr): ) expected = date_range("1/1/2000", periods=4, freq=freq) - with tm.assert_produces_warning(FutureWarning, match=depr_msg): + with tm.assert_produces_warning(Pandas4Warning, match=depr_msg): result = date_range("1/1/2000", periods=4, freq=freq_depr) tm.assert_index_equal(result, expected) diff --git a/pandas/tests/indexes/period/test_constructors.py b/pandas/tests/indexes/period/test_constructors.py index be07a71b283fd..ecaaf6183c027 100644 --- a/pandas/tests/indexes/period/test_constructors.py +++ b/pandas/tests/indexes/period/test_constructors.py @@ -2,6 +2,7 @@ import pytest from pandas._libs.tslibs.period import IncompatibleFrequency +from pandas.errors import Pandas4Warning from pandas.core.dtypes.dtypes import PeriodDtype @@ -85,13 +86,17 @@ def test_period_index_depr_lowercase_frequency(self, freq, freq_depr): f"'{freq_depr[1:]}' is deprecated and will be removed in a future version." ) - with tm.assert_produces_warning(FutureWarning, match=msg): + with tm.assert_produces_warning( + Pandas4Warning, match=msg, raise_on_extra_warnings=False + ): result = PeriodIndex(["2020-01-01", "2020-01-02"], freq=freq_depr) expected = PeriodIndex(["2020-01-01", "2020-01-02"], freq=freq) tm.assert_index_equal(result, expected) - with tm.assert_produces_warning(FutureWarning, match=msg): + with tm.assert_produces_warning( + Pandas4Warning, match=msg, raise_on_extra_warnings=False + ): result = period_range(start="2020-01-01", end="2020-01-02", freq=freq_depr) expected = period_range(start="2020-01-01", end="2020-01-02", freq=freq) @@ -539,7 +544,7 @@ def test_period_range_length(self): assert i1[-1] == end_intv msg = "'w' is deprecated and will be removed in a future version." - with tm.assert_produces_warning(FutureWarning, match=msg): + with tm.assert_produces_warning(Pandas4Warning, match=msg): end_intv = Period("2006-12-31", "1w") i2 = period_range(end=end_intv, periods=10) assert len(i1) == len(i2) diff --git a/pandas/tests/indexes/period/test_period_range.py b/pandas/tests/indexes/period/test_period_range.py index 85af43e7d2e5e..07e7b4461f203 100644 --- a/pandas/tests/indexes/period/test_period_range.py +++ b/pandas/tests/indexes/period/test_period_range.py @@ -1,6 +1,8 @@ import numpy as np import pytest +from pandas.errors import Pandas4Warning + from pandas import ( NaT, Period, @@ -211,7 +213,7 @@ def test_uppercase_freq_deprecated_from_time_series(self, freq_depr): f"future version, please use '{freq_depr.lower()[1:]}' instead." ) - with tm.assert_produces_warning(FutureWarning, match=msg): + with tm.assert_produces_warning(Pandas4Warning, 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", "2H", "2S"]) @@ -237,5 +239,5 @@ def test_lowercase_freq_from_time_series_deprecated(self, freq): f"future version, please use '{freq.upper()[1:]}' instead." ) - with tm.assert_produces_warning(FutureWarning, match=msg): + with tm.assert_produces_warning(Pandas4Warning, match=msg): period_range(freq=freq, start="1/1/2001", end="12/1/2009") diff --git a/pandas/tests/indexes/timedeltas/test_constructors.py b/pandas/tests/indexes/timedeltas/test_constructors.py index ace0ab7990138..63d2161dcec09 100644 --- a/pandas/tests/indexes/timedeltas/test_constructors.py +++ b/pandas/tests/indexes/timedeltas/test_constructors.py @@ -3,6 +3,8 @@ import numpy as np import pytest +from pandas.errors import Pandas4Warning + import pandas as pd from pandas import ( Timedelta, @@ -257,10 +259,10 @@ def test_unit_deprecated(self, unit, unit_depr): msg = f"'{unit_depr}' is deprecated and will be removed in a future version." expected = TimedeltaIndex([f"1{unit}", f"2{unit}"]) - with tm.assert_produces_warning(FutureWarning, match=msg): + with tm.assert_produces_warning(Pandas4Warning, match=msg): result = TimedeltaIndex([f"1{unit_depr}", f"2{unit_depr}"]) tm.assert_index_equal(result, expected) - with tm.assert_produces_warning(FutureWarning, match=msg): + with tm.assert_produces_warning(Pandas4Warning, match=msg): tdi = to_timedelta([1, 2], unit=unit_depr) tm.assert_index_equal(tdi, expected) diff --git a/pandas/tests/indexes/timedeltas/test_indexing.py b/pandas/tests/indexes/timedeltas/test_indexing.py index 426083cb6b67c..ca126a68cbd43 100644 --- a/pandas/tests/indexes/timedeltas/test_indexing.py +++ b/pandas/tests/indexes/timedeltas/test_indexing.py @@ -4,6 +4,8 @@ import numpy as np import pytest +from pandas.errors import Pandas4Warning + from pandas import ( Index, NaT, @@ -22,7 +24,7 @@ class TestGetItem: def test_getitem_slice_keeps_name(self): # GH#4226, GH#59051 msg = "'d' is deprecated and will be removed in a future version." - with tm.assert_produces_warning(FutureWarning, match=msg): + with tm.assert_produces_warning(Pandas4Warning, match=msg): tdi = timedelta_range("1d", "5d", freq="h", name="timebucket") assert tdi[1:].name == tdi.name @@ -340,7 +342,7 @@ def test_contains(self): # Checking for any NaT-like objects # GH#13603, GH#59051 msg = "'d' is deprecated and will be removed in a future version." - with tm.assert_produces_warning(FutureWarning, match=msg): + with tm.assert_produces_warning(Pandas4Warning, match=msg): td = to_timedelta(range(5), unit="d") + offsets.Hour(1) for v in [NaT, None, float("nan"), np.nan]: assert v not in td diff --git a/pandas/tests/indexes/timedeltas/test_scalar_compat.py b/pandas/tests/indexes/timedeltas/test_scalar_compat.py index 9a00c556dc515..a79b7bb524368 100644 --- a/pandas/tests/indexes/timedeltas/test_scalar_compat.py +++ b/pandas/tests/indexes/timedeltas/test_scalar_compat.py @@ -6,6 +6,7 @@ import pytest from pandas._libs.tslibs.offsets import INVALID_FREQ_ERR_MSG +from pandas.errors import Pandas4Warning from pandas import ( Index, @@ -105,7 +106,7 @@ def test_round(self): # note that negative times round DOWN! so don't give whole numbers msg = "'d' is deprecated and will be removed in a future version." - with tm.assert_produces_warning(FutureWarning, match=msg): + with tm.assert_produces_warning(Pandas4Warning, match=msg): for freq, s1, s2 in [ ("ns", t1, t2), ("us", t1, t2), diff --git a/pandas/tests/indexes/timedeltas/test_setops.py b/pandas/tests/indexes/timedeltas/test_setops.py index ae88caf18fdae..3ab3c3e1c8633 100644 --- a/pandas/tests/indexes/timedeltas/test_setops.py +++ b/pandas/tests/indexes/timedeltas/test_setops.py @@ -1,6 +1,8 @@ import numpy as np import pytest +from pandas.errors import Pandas4Warning + import pandas as pd from pandas import ( Index, @@ -44,7 +46,7 @@ def test_union_sort_false(self): def test_union_coverage(self): # GH#59051 msg = "'d' is deprecated and will be removed in a future version." - with tm.assert_produces_warning(FutureWarning, match=msg): + with tm.assert_produces_warning(Pandas4Warning, match=msg): idx = TimedeltaIndex(["3d", "1d", "2d"]) ordered = TimedeltaIndex(idx.sort_values(), freq="infer") result = ordered.union(idx) diff --git a/pandas/tests/indexes/timedeltas/test_timedelta_range.py b/pandas/tests/indexes/timedeltas/test_timedelta_range.py index 6f3d29fb4240a..01ff536652d2f 100644 --- a/pandas/tests/indexes/timedeltas/test_timedelta_range.py +++ b/pandas/tests/indexes/timedeltas/test_timedelta_range.py @@ -1,6 +1,8 @@ import numpy as np import pytest +from pandas.errors import Pandas4Warning + from pandas import ( Timedelta, timedelta_range, @@ -49,7 +51,7 @@ def test_timedelta_units_H_S_deprecated(self, depr_unit, unit): 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): + with tm.assert_produces_warning(Pandas4Warning, match=depr_msg): result = to_timedelta(np.arange(5), unit=depr_unit) tm.assert_index_equal(result, expected) diff --git a/pandas/tests/internals/test_api.py b/pandas/tests/internals/test_api.py index f38bed42a3aa2..9368105a1fa5b 100644 --- a/pandas/tests/internals/test_api.py +++ b/pandas/tests/internals/test_api.py @@ -8,6 +8,8 @@ import numpy as np import pytest +from pandas.errors import Pandas4Warning + import pandas as pd import pandas._testing as tm from pandas.api.internals import create_dataframe_from_blocks @@ -38,7 +40,7 @@ def test_namespace(): ] result = [x for x in dir(internals) if not x.startswith("__")] - assert set(result) == set(expected + modules) + assert set(result) == set(expected + modules), set(result) ^ set(expected + modules) @pytest.mark.parametrize( @@ -51,7 +53,7 @@ def test_namespace(): def test_deprecations(name): # GH#55139 msg = f"{name} is deprecated.* Use public APIs instead" - with tm.assert_produces_warning(FutureWarning, match=msg): + with tm.assert_produces_warning(Pandas4Warning, match=msg): getattr(internals, name) @@ -60,7 +62,7 @@ def test_make_block_2d_with_dti(): dti = pd.date_range("2012", periods=3, tz="UTC") msg = "make_block is deprecated" - with tm.assert_produces_warning(DeprecationWarning, match=msg): + with tm.assert_produces_warning(Pandas4Warning, match=msg): blk = api.make_block(dti, placement=[0]) assert blk.shape == (1, 3) @@ -75,7 +77,7 @@ def test_create_block_manager_from_blocks_deprecated(): "create_block_manager_from_blocks is deprecated and will be " "removed in a future version. Use public APIs instead" ) - with tm.assert_produces_warning(FutureWarning, match=msg): + with tm.assert_produces_warning(Pandas4Warning, match=msg): internals.create_block_manager_from_blocks diff --git a/pandas/tests/internals/test_internals.py b/pandas/tests/internals/test_internals.py index 11e6b99204aee..0add072a63053 100644 --- a/pandas/tests/internals/test_internals.py +++ b/pandas/tests/internals/test_internals.py @@ -10,6 +10,7 @@ from pandas._libs.internals import BlockPlacement from pandas.compat import IS64 +from pandas.errors import Pandas4Warning from pandas.core.dtypes.common import is_scalar @@ -1376,7 +1377,7 @@ def test_validate_ndim(): depr_msg = "make_block is deprecated" with pytest.raises(ValueError, match=msg): - with tm.assert_produces_warning(DeprecationWarning, match=depr_msg): + with tm.assert_produces_warning(Pandas4Warning, match=depr_msg): make_block(values, placement, ndim=2) diff --git a/pandas/tests/io/parser/test_parse_dates.py b/pandas/tests/io/parser/test_parse_dates.py index 9a15d9bc84a2e..4ced43b6463e1 100644 --- a/pandas/tests/io/parser/test_parse_dates.py +++ b/pandas/tests/io/parser/test_parse_dates.py @@ -13,6 +13,8 @@ import numpy as np import pytest +from pandas.errors import Pandas4Warning + import pandas as pd from pandas import ( DataFrame, @@ -548,8 +550,8 @@ def test_date_parser_and_names(all_parsers): data = StringIO("""x,y\n1,2""") warn = UserWarning if parser.engine == "pyarrow": - # DeprecationWarning for passing a Manager object - warn = (UserWarning, DeprecationWarning) + # Pandas4Warning for passing a Manager object + warn = (UserWarning, Pandas4Warning) result = parser.read_csv_check_warnings( warn, "Could not infer format", diff --git a/pandas/tests/io/test_stata.py b/pandas/tests/io/test_stata.py index 3ebf4416f7289..73731cb82dc9b 100644 --- a/pandas/tests/io/test_stata.py +++ b/pandas/tests/io/test_stata.py @@ -13,6 +13,7 @@ import numpy as np import pytest +from pandas.errors import Pandas4Warning import pandas.util._test_decorators as td import pandas as pd @@ -1035,7 +1036,7 @@ def test_big_dates(self, datapath, temp_file): "when writing to stata is deprecated" ) exp_object = expected.astype(object) - with tm.assert_produces_warning(FutureWarning, match=msg): + with tm.assert_produces_warning(Pandas4Warning, match=msg): exp_object.to_stata(path, convert_dates=date_conversion) written_and_read_again = self.read_dta(path) diff --git a/pandas/tests/resample/test_datetime_index.py b/pandas/tests/resample/test_datetime_index.py index 262f032f20187..7b02b62bfd10c 100644 --- a/pandas/tests/resample/test_datetime_index.py +++ b/pandas/tests/resample/test_datetime_index.py @@ -243,7 +243,7 @@ def test_resample_how_callables(unit): # GH#7929 data = np.arange(5, dtype=np.int64) msg = "'d' is deprecated and will be removed in a future version." - with tm.assert_produces_warning(FutureWarning, match=msg): + with tm.assert_produces_warning(Pandas4Warning, match=msg): ind = date_range(start="2014-01-01", periods=len(data), freq="d").as_unit(unit) df = DataFrame({"A": data, "B": data}, index=ind) @@ -340,7 +340,7 @@ def test_resample_basic_from_daily(unit): # to weekly msg = "'w-sun' is deprecated and will be removed in a future version." - with tm.assert_produces_warning(FutureWarning, match=msg): + with tm.assert_produces_warning(Pandas4Warning, match=msg): result = s.resample("w-sun").last() assert len(result) == 3 @@ -1215,7 +1215,7 @@ def test_anchored_lowercase_buglet(unit): ts = Series(np.random.default_rng(2).standard_normal(len(dates)), index=dates) # it works! msg = "'d' is deprecated and will be removed in a future version." - with tm.assert_produces_warning(FutureWarning, match=msg): + with tm.assert_produces_warning(Pandas4Warning, match=msg): ts.resample("d").mean() @@ -2065,7 +2065,7 @@ def test_resample_depr_lowercase_frequency(freq, freq_depr, data): msg = f"'{freq_depr[1:]}' is deprecated and will be removed in a future version." s = Series(range(5), index=date_range("20130101", freq="h", periods=5)) - with tm.assert_produces_warning(FutureWarning, match=msg): + with tm.assert_produces_warning(Pandas4Warning, match=msg): result = s.resample(freq_depr).mean() exp_dti = DatetimeIndex(data=data, dtype="datetime64[ns]", freq=freq) diff --git a/pandas/tests/scalar/period/test_period.py b/pandas/tests/scalar/period/test_period.py index baaedaa853565..c199b57b18c63 100644 --- a/pandas/tests/scalar/period/test_period.py +++ b/pandas/tests/scalar/period/test_period.py @@ -16,6 +16,7 @@ from pandas._libs.tslibs.np_datetime import OutOfBoundsDatetime from pandas._libs.tslibs.parsing import DateParseError from pandas._libs.tslibs.period import INVALID_FREQ_ERR_MSG +from pandas.errors import Pandas4Warning from pandas import ( NaT, @@ -109,7 +110,7 @@ def test_construction(self): i1 = Period("1982", freq="min") msg = "'MIN' is deprecated and will be removed in a future version." - with tm.assert_produces_warning(FutureWarning, match=msg): + with tm.assert_produces_warning(Pandas4Warning, match=msg): i2 = Period("1982", freq="MIN") assert i1 == i2 @@ -118,7 +119,7 @@ def test_construction(self): assert i1 == i2 msg = "'d' is deprecated and will be removed in a future version." - with tm.assert_produces_warning(FutureWarning, match=msg): + with tm.assert_produces_warning(Pandas4Warning, match=msg): i3 = Period(year=2005, month=3, day=1, freq="d") assert i1 == i3 @@ -169,7 +170,9 @@ def test_construction_from_timestamp_nanos(self): def test_construction_bday(self): # Biz day construction, roll forward if non-weekday - with tm.assert_produces_warning(FutureWarning, match=bday_msg): + with tm.assert_produces_warning( + Pandas4Warning, match="'b' is deprecated", raise_on_extra_warnings=False + ): i1 = Period("3/10/12", freq="B") i2 = Period("3/10/12", freq="D") assert i1 == i2.asfreq("B") @@ -628,7 +631,9 @@ def test_period_deprecated_lowercase_freq(self, freq, freq_depr): f"'{freq_depr[1:]}' is deprecated and will be removed in a future version." ) - with tm.assert_produces_warning(FutureWarning, match=msg): + with tm.assert_produces_warning( + Pandas4Warning, match=msg, raise_on_extra_warnings=False + ): result = Period("2016-03-01 09:00", freq=freq_depr) expected = Period("2016-03-01 09:00", freq=freq) diff --git a/pandas/tests/scalar/timedelta/test_constructors.py b/pandas/tests/scalar/timedelta/test_constructors.py index 6b70049c64bfe..c9904a318e22d 100644 --- a/pandas/tests/scalar/timedelta/test_constructors.py +++ b/pandas/tests/scalar/timedelta/test_constructors.py @@ -6,6 +6,7 @@ from pandas._libs.tslibs import OutOfBoundsTimedelta from pandas._libs.tslibs.dtypes import NpyDatetimeUnit +from pandas.errors import Pandas4Warning from pandas import ( Index, @@ -49,7 +50,7 @@ def test_unit_deprecated(self, unit, unit_depr): 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): + with tm.assert_produces_warning(Pandas4Warning, match=msg): result = Timedelta(1, unit=unit_depr) tm.assert_equal(result, expected) diff --git a/pandas/tests/scalar/timedelta/test_timedelta.py b/pandas/tests/scalar/timedelta/test_timedelta.py index 8be2ec846a6d9..a2fbec77df596 100644 --- a/pandas/tests/scalar/timedelta/test_timedelta.py +++ b/pandas/tests/scalar/timedelta/test_timedelta.py @@ -17,7 +17,10 @@ iNaT, ) from pandas._libs.tslibs.dtypes import NpyDatetimeUnit -from pandas.errors import OutOfBoundsTimedelta +from pandas.errors import ( + OutOfBoundsTimedelta, + Pandas4Warning, +) from pandas import ( Timedelta, @@ -491,7 +494,7 @@ def conv(v): assert Timedelta("1000ns") == np.timedelta64(1000, "ns") msg = "'NS' is deprecated and will be removed in a future version." - with tm.assert_produces_warning(FutureWarning, match=msg): + with tm.assert_produces_warning(Pandas4Warning, match=msg): assert Timedelta("1000NS") == np.timedelta64(1000, "ns") assert Timedelta("10us") == np.timedelta64(10000, "ns") @@ -512,7 +515,7 @@ def conv(v): assert Timedelta("1000s") == np.timedelta64(1000000000000, "ns") msg = "'d' is deprecated and will be removed in a future version." - with tm.assert_produces_warning(FutureWarning, match=msg): + with tm.assert_produces_warning(Pandas4Warning, match=msg): assert Timedelta("1d") == conv(np.timedelta64(1, "D")) assert Timedelta("-1D") == -conv(np.timedelta64(1, "D")) assert Timedelta("1D") == conv(np.timedelta64(1, "D")) @@ -684,7 +687,7 @@ def test_unit_deprecated(self, unit, unit_depr): # GH#59051 msg = f"'{unit_depr}' is deprecated and will be removed in a future version." - with tm.assert_produces_warning(FutureWarning, match=msg): + with tm.assert_produces_warning(Pandas4Warning, match=msg): result = Timedelta(1, unit_depr) assert result == Timedelta(1, unit) diff --git a/pandas/tests/series/methods/test_map.py b/pandas/tests/series/methods/test_map.py index 0ec973dea23d5..104f779d2d213 100644 --- a/pandas/tests/series/methods/test_map.py +++ b/pandas/tests/series/methods/test_map.py @@ -8,6 +8,8 @@ import numpy as np import pytest +from pandas.errors import Pandas4Warning + import pandas as pd from pandas import ( DataFrame, @@ -616,7 +618,7 @@ def test_map_kwargs(): def test_map_arg_as_kwarg(): with tm.assert_produces_warning( - FutureWarning, match="`arg` has been renamed to `func`" + Pandas4Warning, match="`arg` has been renamed to `func`" ): Series([1, 2]).map(arg={}) diff --git a/pandas/tests/tools/test_to_timedelta.py b/pandas/tests/tools/test_to_timedelta.py index 9ec2689069da9..bfbefc50e65ba 100644 --- a/pandas/tests/tools/test_to_timedelta.py +++ b/pandas/tests/tools/test_to_timedelta.py @@ -10,7 +10,10 @@ IS64, WASM, ) -from pandas.errors import OutOfBoundsTimedelta +from pandas.errors import ( + OutOfBoundsTimedelta, + Pandas4Warning, +) import pandas as pd from pandas import ( @@ -58,7 +61,7 @@ def test_to_timedelta_series(self): expected = Series([timedelta(days=1), timedelta(days=1, seconds=1)]) msg = "'d' is deprecated and will be removed in a future version." - with tm.assert_produces_warning(FutureWarning, match=msg): + with tm.assert_produces_warning(Pandas4Warning, match=msg): result = to_timedelta(Series(["1d", "1days 00:00:01"])) tm.assert_series_equal(result, expected) diff --git a/pandas/tests/tslibs/test_to_offset.py b/pandas/tests/tslibs/test_to_offset.py index f3d323f315002..961a636a64345 100644 --- a/pandas/tests/tslibs/test_to_offset.py +++ b/pandas/tests/tslibs/test_to_offset.py @@ -7,6 +7,7 @@ offsets, to_offset, ) +from pandas.errors import Pandas4Warning import pandas._testing as tm @@ -130,7 +131,7 @@ def test_to_offset_leading_zero(freqstr, expected): @pytest.mark.parametrize( - "freqstr,expected,wrn", [("+1d", 1, FutureWarning), ("+2h30min", 150, None)] + "freqstr,expected,wrn", [("+1d", 1, Pandas4Warning), ("+2h30min", 150, None)] ) def test_to_offset_leading_plus(freqstr, expected, wrn): msg = "'d' is deprecated and will be removed in a future version." @@ -212,7 +213,7 @@ def test_to_offset_uppercase_frequency_deprecated(freq_depr): f"future version, please use '{freq_depr.lower()[1:]}' instead." ) - with tm.assert_produces_warning(FutureWarning, match=depr_msg): + with tm.assert_produces_warning(Pandas4Warning, match=depr_msg): to_offset(freq_depr) @@ -228,7 +229,7 @@ def test_to_offset_lowercase_frequency_deprecated(freq_depr, expected): # GH#54939, GH#58998 msg = f"'{freq_depr[1:]}' is deprecated and will be removed in a future version." - with tm.assert_produces_warning(FutureWarning, match=msg): + with tm.assert_produces_warning(Pandas4Warning, match=msg): result = to_offset(freq_depr) assert result == expected diff --git a/pandas/tests/window/test_groupby.py b/pandas/tests/window/test_groupby.py index 1dcdad2bfd73d..f9595970bdb63 100644 --- a/pandas/tests/window/test_groupby.py +++ b/pandas/tests/window/test_groupby.py @@ -1,6 +1,8 @@ import numpy as np import pytest +from pandas.errors import Pandas4Warning + from pandas import ( DataFrame, DatetimeIndex, @@ -639,7 +641,7 @@ def test_groupby_rolling_count_closed_on(self, unit): ) msg = "'d' is deprecated and will be removed in a future version." - with tm.assert_produces_warning(FutureWarning, match=msg): + with tm.assert_produces_warning(Pandas4Warning, match=msg): result = ( df.groupby("group") .rolling("3d", on="date", closed="left")["column1"] diff --git a/pandas/tests/window/test_rolling.py b/pandas/tests/window/test_rolling.py index 6e8f075d35490..18aafa0d7b71e 100644 --- a/pandas/tests/window/test_rolling.py +++ b/pandas/tests/window/test_rolling.py @@ -12,6 +12,7 @@ is_platform_power, is_platform_riscv64, ) +from pandas.errors import Pandas4Warning from pandas import ( DataFrame, @@ -1460,7 +1461,7 @@ def test_rolling_descending_date_order_with_offset(frame_or_series): # GH#40002 msg = "'d' is deprecated and will be removed in a future version." - with tm.assert_produces_warning(FutureWarning, match=msg): + with tm.assert_produces_warning(Pandas4Warning, match=msg): idx = date_range(start="2020-01-01", end="2020-01-03", freq="1d") obj = frame_or_series(range(1, 4), index=idx) result = obj.rolling("1d", closed="left").sum() From 53a41bb43acc6b23cf9f17db3f47fabb6ed5adce Mon Sep 17 00:00:00 2001 From: Richard Shadrach Date: Sat, 23 Aug 2025 08:29:41 -0400 Subject: [PATCH 3/7] Change warning --- pandas/core/arrays/arrow/array.py | 4 ++-- pandas/core/arrays/string_.py | 4 ++-- pandas/tests/strings/test_strings.py | 4 +++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/pandas/core/arrays/arrow/array.py b/pandas/core/arrays/arrow/array.py index 3eb256815f93b..89d59d83cdb32 100644 --- a/pandas/core/arrays/arrow/array.py +++ b/pandas/core/arrays/arrow/array.py @@ -28,6 +28,7 @@ pa_version_under12p1, pa_version_under13p0, ) +from pandas.errors import Pandas4Warning from pandas.util._decorators import doc from pandas.util._exceptions import find_stack_level @@ -930,14 +931,13 @@ def _logical_method(self, other, op) -> Self: and isinstance(other, np.ndarray) and other.dtype == bool ): - # TODO: Enforce in 3.0 (#60234) # GH#60234 backward compatibility for the move to StringDtype in 3.0 op_name = op.__name__[1:].strip("_") warnings.warn( f"'{op_name}' operations between boolean dtype and {self.dtype} are " "deprecated and will raise in a future version. Explicitly " "cast the strings to a boolean dtype before operating instead.", - FutureWarning, + Pandas4Warning, stacklevel=find_stack_level(), ) return op(other, self.astype(bool)) diff --git a/pandas/core/arrays/string_.py b/pandas/core/arrays/string_.py index 54dbe4a56b7ee..2de5495bd0b62 100644 --- a/pandas/core/arrays/string_.py +++ b/pandas/core/arrays/string_.py @@ -29,6 +29,7 @@ pa_version_under12p1, ) from pandas.compat.numpy import function as nv +from pandas.errors import Pandas4Warning from pandas.util._decorators import ( doc, set_module, @@ -397,12 +398,11 @@ def _logical_method(self, other, op): ): # GH#60234 backward compatibility for the move to StringDtype in 3.0 op_name = op.__name__[1:].strip("_") - # TODO: Enforce in 3.0 (#60234) warnings.warn( f"'{op_name}' operations between boolean dtype and {self.dtype} are " "deprecated and will raise in a future version. Explicitly " "cast the strings to a boolean dtype before operating instead.", - FutureWarning, + Pandas4Warning, stacklevel=find_stack_level(), ) return op(other, self.astype(bool)) diff --git a/pandas/tests/strings/test_strings.py b/pandas/tests/strings/test_strings.py index fb3a3b8d60b6b..ea893261890ac 100644 --- a/pandas/tests/strings/test_strings.py +++ b/pandas/tests/strings/test_strings.py @@ -6,6 +6,8 @@ import numpy as np import pytest +from pandas.errors import Pandas4Warning + from pandas import ( NA, DataFrame, @@ -803,7 +805,7 @@ def test_decode_with_dtype_none(): def test_reversed_logical_ops(any_string_dtype): # GH#60234 dtype = any_string_dtype - warn = None if dtype == object else FutureWarning + warn = None if dtype == object else Pandas4Warning left = Series([True, False, False, True]) right = Series(["", "", "b", "c"], dtype=dtype) From 90f72c3bef6f4b6829c6427cf0335d8dfa99ea5e Mon Sep 17 00:00:00 2001 From: Richard Shadrach Date: Sat, 23 Aug 2025 15:46:02 -0400 Subject: [PATCH 4/7] CLN: Lint for usage of Deprecation/FutureWarning --- .pre-commit-config.yaml | 5 ++ pandas/core/generic.py | 2 +- pandas/core/groupby/groupby.py | 2 +- pandas/core/internals/__init__.py | 2 +- pandas/core/series.py | 2 +- pandas/core/strings/object_array.py | 6 +-- .../tests/test_validate_unwanted_patterns.py | 34 ++++++++++++++ scripts/validate_unwanted_patterns.py | 46 +++++++++++++++++++ 8 files changed, 92 insertions(+), 7 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 51be4c3f77973..9dbb6f0458ba4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -266,6 +266,11 @@ repos: language: python entry: python scripts/validate_unwanted_patterns.py --validation-type="nodefault_used_not_only_for_typing" types: [python] + - id: unwanted-patterns-doesnt-use-pandas-warnings + name: Check that warning classes for deprecations use pandas' warning classes + language: python + entry: python scripts/validate_unwanted_patterns.py --validation-type="doesnt_use_pandas_warnings" + types: [ python ] - id: no-return-exception name: Use raise instead of return for exceptions language: pygrep diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 33fcc94f906d5..60944567a4dab 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -9138,7 +9138,7 @@ def resample( "deprecated and will be removed in a future version. " "Explicitly cast PeriodIndex to DatetimeIndex before resampling " "instead.", - FutureWarning, + FutureWarning, # pdlint: ignore stacklevel=find_stack_level(), ) else: diff --git a/pandas/core/groupby/groupby.py b/pandas/core/groupby/groupby.py index b0ae89b1b954d..2a3c433c54461 100644 --- a/pandas/core/groupby/groupby.py +++ b/pandas/core/groupby/groupby.py @@ -557,7 +557,7 @@ def groups(self) -> dict[Hashable, Index]: "and will be removed. In a future version `groups` by one element " "list will return tuple. Use ``df.groupby(by='a').groups`` " "instead of ``df.groupby(by=['a']).groups`` to avoid this warning", - FutureWarning, + FutureWarning, # pdlint: ignore stacklevel=find_stack_level(), ) return self._grouper.groups diff --git a/pandas/core/internals/__init__.py b/pandas/core/internals/__init__.py index 12999a44a446b..80ec99f7760a4 100644 --- a/pandas/core/internals/__init__.py +++ b/pandas/core/internals/__init__.py @@ -25,7 +25,7 @@ def __getattr__(name: str): warnings.warn( f"{name} is deprecated and will be removed in a future version. " "Use public APIs instead.", - FutureWarning, + FutureWarning, # pdlint: ignore # https://github.com/pandas-dev/pandas/pull/55139#pullrequestreview-1720690758 # on hard-coding stacklevel stacklevel=2, diff --git a/pandas/core/series.py b/pandas/core/series.py index b40e71f2b5b42..d7cbea440d925 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -4435,7 +4435,7 @@ def map( warnings.warn( "The parameter `arg` has been renamed to `func`, and it " "will stop being supported in a future version of pandas.", - FutureWarning, + FutureWarning, # pdlint: ignore stacklevel=find_stack_level(), ) else: diff --git a/pandas/core/strings/object_array.py b/pandas/core/strings/object_array.py index 9f6baaf691577..a77db5710852b 100644 --- a/pandas/core/strings/object_array.py +++ b/pandas/core/strings/object_array.py @@ -163,7 +163,7 @@ def _str_contains( warnings.warn( "Allowing a non-bool 'na' in obj.str.contains is deprecated " "and will raise in a future version.", - FutureWarning, + FutureWarning, # pdlint: ignore stacklevel=find_stack_level(), ) return self._str_map(f, na, dtype=np.dtype("bool")) @@ -175,7 +175,7 @@ def _str_startswith(self, pat, na=lib.no_default): warnings.warn( "Allowing a non-bool 'na' in obj.str.startswith is deprecated " "and will raise in a future version.", - FutureWarning, + FutureWarning, # pdlint: ignore stacklevel=find_stack_level(), ) return self._str_map(f, na_value=na, dtype=np.dtype(bool)) @@ -187,7 +187,7 @@ def _str_endswith(self, pat, na=lib.no_default): warnings.warn( "Allowing a non-bool 'na' in obj.str.endswith is deprecated " "and will raise in a future version.", - FutureWarning, + FutureWarning, # pdlint: ignore stacklevel=find_stack_level(), ) return self._str_map(f, na_value=na, dtype=np.dtype(bool)) diff --git a/scripts/tests/test_validate_unwanted_patterns.py b/scripts/tests/test_validate_unwanted_patterns.py index 4c433d03aff4d..803bea76ee773 100644 --- a/scripts/tests/test_validate_unwanted_patterns.py +++ b/scripts/tests/test_validate_unwanted_patterns.py @@ -296,3 +296,37 @@ def test_nodefault_used_not_only_for_typing_raises(self, data, expected) -> None fd = io.StringIO(data.strip()) result = list(validate_unwanted_patterns.nodefault_used_not_only_for_typing(fd)) assert result == expected + + +@pytest.mark.parametrize("function", ["warnings.warn", "warn"]) +@pytest.mark.parametrize("positional", [True, False]) +@pytest.mark.parametrize( + "category", + [ + "FutureWarning", + "DeprecationWarning", + "PendingDeprecationWarning", + "Pandas4Warning", + "RuntimeWarning" + ], +) +@pytest.mark.parametrize("pdlint_ignore", [True, False]) + +def test_doesnt_use_pandas_warnings(function, positional, category, pdlint_ignore): + code = ( + f"{function}({' # pdlint: ignore[warning_class]' if pdlint_ignore else ''}\n" + f' "message",\n' + f" {'' if positional else 'category='}{category},\n" + f")\n" + ) + flag_issue = ( + category in ["FutureWarning", "DeprecationWarning", "PendingDeprecationWarning"] + and not pdlint_ignore + ) + fd = io.StringIO(code) + result = list(validate_unwanted_patterns.doesnt_use_pandas_warnings(fd)) + if flag_issue: + assert len(result) == 1 + assert result[0] == (1, f"Don't use {category}") + else: + assert len(result) == 0 diff --git a/scripts/validate_unwanted_patterns.py b/scripts/validate_unwanted_patterns.py index 8475747a80367..973a0843960e7 100755 --- a/scripts/validate_unwanted_patterns.py +++ b/scripts/validate_unwanted_patterns.py @@ -16,11 +16,15 @@ Callable, Iterable, ) +import re import sys import token import tokenize from typing import IO +DEPRECATION_WARNINGS_PATTERN = re.compile( + r"(PendingDeprecation|Deprecation|Future)Warning" +) PRIVATE_IMPORTS_TO_IGNORE: set[str] = { "_extension_array_shared_docs", "_index_shared_docs", @@ -344,6 +348,47 @@ def nodefault_used_not_only_for_typing(file_obj: IO[str]) -> Iterable[tuple[int, if isinstance(value, ast.AST) ) +def doesnt_use_pandas_warnings(file_obj: IO[str]) -> Iterable[tuple[int, str]]: + """ + Checking that a private function is not used across modules. + Parameters + ---------- + file_obj : IO + File-like object containing the Python code to validate. + Yields + ------ + line_number : int + Line number of the private function that is used across modules. + msg : str + Explanation of the error. + """ + contents = file_obj.read() + lines = contents.split("\n") + tree = ast.parse(contents) + for node in ast.walk(tree): + if not isinstance(node, ast.Call): + continue + + if isinstance(node.func, ast.Attribute): + if node.func.attr != "warn": + continue + elif isinstance(node.func, ast.Name): + if node.func.id != "warn": + continue + if any( + "# pdlint: ignore[warning_class]" in lines[k] + for k in range(node.lineno - 1, node.end_lineno + 1) + ): + continue + values = ( + [arg.id for arg in node.args if isinstance(arg, ast.Name)] + + [kw.value.id for kw in node.keywords if kw.arg == "category"] + ) + for value in values: + matches = re.match(DEPRECATION_WARNINGS_PATTERN, value) + if matches is not None: + yield (node.lineno, f"Don't use {matches[0]}") + def main( function: Callable[[IO[str]], Iterable[tuple[int, str]]], @@ -397,6 +442,7 @@ def main( "private_import_across_module", "strings_with_wrong_placed_whitespace", "nodefault_used_not_only_for_typing", + "doesnt_use_pandas_warnings", ] parser = argparse.ArgumentParser(description="Unwanted patterns checker.") From 7640355f428bf0948c25930206da3416238cd877 Mon Sep 17 00:00:00 2001 From: Richard Shadrach Date: Sat, 23 Aug 2025 15:50:20 -0400 Subject: [PATCH 5/7] cleanup --- scripts/validate_unwanted_patterns.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/scripts/validate_unwanted_patterns.py b/scripts/validate_unwanted_patterns.py index 973a0843960e7..6bd6401cc1f70 100755 --- a/scripts/validate_unwanted_patterns.py +++ b/scripts/validate_unwanted_patterns.py @@ -350,15 +350,17 @@ def nodefault_used_not_only_for_typing(file_obj: IO[str]) -> Iterable[tuple[int, def doesnt_use_pandas_warnings(file_obj: IO[str]) -> Iterable[tuple[int, str]]: """ - Checking that a private function is not used across modules. + Checking that pandas-specific warnings are used for deprecations. + Parameters ---------- file_obj : IO File-like object containing the Python code to validate. + Yields ------ line_number : int - Line number of the private function that is used across modules. + Line number of the warning. msg : str Explanation of the error. """ @@ -387,7 +389,12 @@ def doesnt_use_pandas_warnings(file_obj: IO[str]) -> Iterable[tuple[int, str]]: for value in values: matches = re.match(DEPRECATION_WARNINGS_PATTERN, value) if matches is not None: - yield (node.lineno, f"Don't use {matches[0]}") + yield ( + node.lineno, + f"Don't use {matches[0]}, use a pandas-specific warning in " + f"pd.errors instead. You can add " + f"`# pdlint: ignore[warning_class]` to override." + ) def main( From 739305436bc54ecb02971da4700065ff93f2f1c5 Mon Sep 17 00:00:00 2001 From: Richard Shadrach Date: Sun, 24 Aug 2025 08:23:26 -0400 Subject: [PATCH 6/7] Fixup tests --- scripts/tests/test_validate_unwanted_patterns.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/tests/test_validate_unwanted_patterns.py b/scripts/tests/test_validate_unwanted_patterns.py index 803bea76ee773..6ade3d2833e16 100644 --- a/scripts/tests/test_validate_unwanted_patterns.py +++ b/scripts/tests/test_validate_unwanted_patterns.py @@ -311,7 +311,6 @@ def test_nodefault_used_not_only_for_typing_raises(self, data, expected) -> None ], ) @pytest.mark.parametrize("pdlint_ignore", [True, False]) - def test_doesnt_use_pandas_warnings(function, positional, category, pdlint_ignore): code = ( f"{function}({' # pdlint: ignore[warning_class]' if pdlint_ignore else ''}\n" @@ -327,6 +326,7 @@ def test_doesnt_use_pandas_warnings(function, positional, category, pdlint_ignor result = list(validate_unwanted_patterns.doesnt_use_pandas_warnings(fd)) if flag_issue: assert len(result) == 1 - assert result[0] == (1, f"Don't use {category}") + assert result[0][0] == 1 + assert result[0][1].startswith(f"Don't use {category}") else: assert len(result) == 0 From c20e3b8abb8d1e4ff08c3c20a46a9e0691c45027 Mon Sep 17 00:00:00 2001 From: Richard Shadrach Date: Mon, 25 Aug 2025 20:01:32 -0400 Subject: [PATCH 7/7] Add ignores --- pandas/_config/config.py | 8 +++++-- pandas/core/arrays/string_.py | 2 +- pandas/core/arrays/string_arrow.py | 2 +- pandas/core/dtypes/dtypes.py | 2 +- pandas/core/frame.py | 4 ++-- pandas/core/generic.py | 2 +- pandas/core/groupby/groupby.py | 2 +- pandas/core/resample.py | 4 ++-- pandas/core/strings/object_array.py | 6 ++--- .../util/test_assert_produces_warning.py | 24 ++++++++++++------- pandas/tests/util/test_rewrite_warning.py | 5 +++- 11 files changed, 37 insertions(+), 24 deletions(-) diff --git a/pandas/_config/config.py b/pandas/_config/config.py index 50dac1925c936..43e01dafc5005 100644 --- a/pandas/_config/config.py +++ b/pandas/_config/config.py @@ -716,7 +716,7 @@ def _warn_if_deprecated(key: str) -> bool: if d.msg: warnings.warn( d.msg, - FutureWarning, + FutureWarning, # pdlint: ignore[warning_class] stacklevel=find_stack_level(), ) else: @@ -728,7 +728,11 @@ def _warn_if_deprecated(key: str) -> bool: else: msg += ", please refrain from using it." - warnings.warn(msg, FutureWarning, stacklevel=find_stack_level()) + warnings.warn( + msg, + FutureWarning, # pdlint: ignore[warning_class] + stacklevel=find_stack_level(), + ) return True return False diff --git a/pandas/core/arrays/string_.py b/pandas/core/arrays/string_.py index a52a729a0dce4..561daedff9043 100644 --- a/pandas/core/arrays/string_.py +++ b/pandas/core/arrays/string_.py @@ -176,7 +176,7 @@ def __init__( "'pd.options.future.infer_string = True' option globally and use " 'the "str" alias as a shorthand notation to specify a dtype ' '(instead of "string[pyarrow_numpy]").', - FutureWarning, + FutureWarning, # pdlint: ignore[warning_class] stacklevel=find_stack_level(), ) storage = "pyarrow" diff --git a/pandas/core/arrays/string_arrow.py b/pandas/core/arrays/string_arrow.py index bca7224ffc2f5..6e29848171ace 100644 --- a/pandas/core/arrays/string_arrow.py +++ b/pandas/core/arrays/string_arrow.py @@ -245,7 +245,7 @@ def _convert_bool_result(self, values, na=lib.no_default, method_name=None): warnings.warn( f"Allowing a non-bool 'na' in obj.str.{method_name} is deprecated " "and will raise in a future version.", - FutureWarning, + FutureWarning, # pdlint: ignore[warning_class] stacklevel=find_stack_level(), ) na = bool(na) diff --git a/pandas/core/dtypes/dtypes.py b/pandas/core/dtypes/dtypes.py index eb5c7739e5132..74f95cc7f52b4 100644 --- a/pandas/core/dtypes/dtypes.py +++ b/pandas/core/dtypes/dtypes.py @@ -1053,7 +1053,7 @@ def __new__(cls, freq) -> PeriodDtype: # noqa: PYI034 warnings.warn( "PeriodDtype[B] is deprecated and will be removed in a future " "version. Use a DatetimeIndex with freq='B' instead", - FutureWarning, + FutureWarning, # pdlint: ignore[warning_class] stacklevel=find_stack_level(), ) diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 0dcb15738c276..217aad35a80a7 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -13262,7 +13262,7 @@ def idxmin( f"The behavior of {type(self).__name__}.idxmin with all-NA " "values, or any-NA and skipna=False, is deprecated. In a future " "version this will raise ValueError", - FutureWarning, + FutureWarning, # pdlint: ignore[warning_class] stacklevel=find_stack_level(), ) @@ -13369,7 +13369,7 @@ def idxmax( f"The behavior of {type(self).__name__}.idxmax with all-NA " "values, or any-NA and skipna=False, is deprecated. In a future " "version this will raise ValueError", - FutureWarning, + FutureWarning, # pdlint: ignore[warning_class] stacklevel=find_stack_level(), ) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 5864cbae4c4a8..427e9594c3a24 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -9139,7 +9139,7 @@ def resample( "deprecated and will be removed in a future version. " "Explicitly cast PeriodIndex to DatetimeIndex before resampling " "instead.", - FutureWarning, # pdlint: ignore + FutureWarning, # pdlint: ignore[warning_class] stacklevel=find_stack_level(), ) else: diff --git a/pandas/core/groupby/groupby.py b/pandas/core/groupby/groupby.py index 2a3c433c54461..c81d4839fae11 100644 --- a/pandas/core/groupby/groupby.py +++ b/pandas/core/groupby/groupby.py @@ -557,7 +557,7 @@ def groups(self) -> dict[Hashable, Index]: "and will be removed. In a future version `groups` by one element " "list will return tuple. Use ``df.groupby(by='a').groups`` " "instead of ``df.groupby(by=['a']).groups`` to avoid this warning", - FutureWarning, # pdlint: ignore + FutureWarning, # pdlint: ignore[warning_class] stacklevel=find_stack_level(), ) return self._grouper.groups diff --git a/pandas/core/resample.py b/pandas/core/resample.py index c4035ee941fbe..fc54a07c1396b 100644 --- a/pandas/core/resample.py +++ b/pandas/core/resample.py @@ -1949,7 +1949,7 @@ def _resampler_for_grouping(self): warnings.warn( "Resampling a groupby with a PeriodIndex is deprecated. " "Cast to DatetimeIndex before resampling instead.", - FutureWarning, + FutureWarning, # pdlint: ignore[warning_class] stacklevel=find_stack_level(), ) return PeriodIndexResamplerGroupby @@ -2297,7 +2297,7 @@ def _get_resampler(self, obj: NDFrame) -> Resampler: warnings.warn( "Resampling with a PeriodIndex is deprecated. " "Cast index to DatetimeIndex before resampling instead.", - FutureWarning, + FutureWarning, # pdlint: ignore[warning_class] stacklevel=find_stack_level(), ) return PeriodIndexResampler( diff --git a/pandas/core/strings/object_array.py b/pandas/core/strings/object_array.py index a77db5710852b..55baff1dbdb64 100644 --- a/pandas/core/strings/object_array.py +++ b/pandas/core/strings/object_array.py @@ -163,7 +163,7 @@ def _str_contains( warnings.warn( "Allowing a non-bool 'na' in obj.str.contains is deprecated " "and will raise in a future version.", - FutureWarning, # pdlint: ignore + FutureWarning, # pdlint: ignore[warning_class] stacklevel=find_stack_level(), ) return self._str_map(f, na, dtype=np.dtype("bool")) @@ -175,7 +175,7 @@ def _str_startswith(self, pat, na=lib.no_default): warnings.warn( "Allowing a non-bool 'na' in obj.str.startswith is deprecated " "and will raise in a future version.", - FutureWarning, # pdlint: ignore + FutureWarning, # pdlint: ignore[warning_class] stacklevel=find_stack_level(), ) return self._str_map(f, na_value=na, dtype=np.dtype(bool)) @@ -187,7 +187,7 @@ def _str_endswith(self, pat, na=lib.no_default): warnings.warn( "Allowing a non-bool 'na' in obj.str.endswith is deprecated " "and will raise in a future version.", - FutureWarning, # pdlint: ignore + FutureWarning, # pdlint: ignore[warning_class] stacklevel=find_stack_level(), ) return self._str_map(f, na_value=na, dtype=np.dtype(bool)) diff --git a/pandas/tests/util/test_assert_produces_warning.py b/pandas/tests/util/test_assert_produces_warning.py index 5b917dbbe7ba7..9316f1452477c 100644 --- a/pandas/tests/util/test_assert_produces_warning.py +++ b/pandas/tests/util/test_assert_produces_warning.py @@ -38,7 +38,7 @@ def pair_different_warnings(request): def f(): - warnings.warn("f1", FutureWarning) + warnings.warn("f1", FutureWarning) # pdlint: ignore[warning_class] warnings.warn("f2", RuntimeWarning) @@ -175,7 +175,7 @@ def test_match_multiple_warnings(): # https://github.com/pandas-dev/pandas/issues/47829 category = (FutureWarning, UserWarning) with tm.assert_produces_warning(category, match=r"^Match this"): - warnings.warn("Match this", FutureWarning) + warnings.warn("Match this", FutureWarning) # pdlint: ignore[warning_class] warnings.warn("Match this too", UserWarning) @@ -185,7 +185,7 @@ def test_must_match_multiple_warnings(): msg = "Did not see expected warning of class 'UserWarning'" with pytest.raises(AssertionError, match=msg): with tm.assert_produces_warning(category, match=r"^Match this"): - warnings.warn("Match this", FutureWarning) + warnings.warn("Match this", FutureWarning) # pdlint: ignore[warning_class] def test_must_match_multiple_warnings_messages(): @@ -194,7 +194,7 @@ def test_must_match_multiple_warnings_messages(): msg = r"The emitted warning messages are \[UserWarning\('Not this'\)\]" with pytest.raises(AssertionError, match=msg): with tm.assert_produces_warning(category, match=r"^Match this"): - warnings.warn("Match this", FutureWarning) + warnings.warn("Match this", FutureWarning) # pdlint: ignore[warning_class] warnings.warn("Not this", UserWarning) @@ -204,7 +204,7 @@ def test_allow_partial_match_for_multiple_warnings(): with tm.assert_produces_warning( category, match=r"^Match this", must_find_all_warnings=False ): - warnings.warn("Match this", FutureWarning) + warnings.warn("Match this", FutureWarning) # pdlint: ignore[warning_class] def test_allow_partial_match_for_multiple_warnings_messages(): @@ -213,7 +213,7 @@ def test_allow_partial_match_for_multiple_warnings_messages(): with tm.assert_produces_warning( category, match=r"^Match this", must_find_all_warnings=False ): - warnings.warn("Match this", FutureWarning) + warnings.warn("Match this", FutureWarning) # pdlint: ignore[warning_class] warnings.warn("Not this", UserWarning) @@ -250,13 +250,17 @@ def test_raises_during_exception(): with pytest.raises(AssertionError, match=msg): with tm.assert_produces_warning(UserWarning): - warnings.warn("FutureWarning", FutureWarning) + warnings.warn( + "FutureWarning", FutureWarning + ) # pdlint: ignore[warning_class] raise IndexError msg = "Caused unexpected warning" with pytest.raises(AssertionError, match=msg): with tm.assert_produces_warning(None): - warnings.warn("FutureWarning", FutureWarning) + warnings.warn( + "FutureWarning", FutureWarning + ) # pdlint: ignore[warning_class] raise SystemError @@ -267,5 +271,7 @@ def test_passes_during_exception(): with pytest.raises(ValueError, match="Error"): with tm.assert_produces_warning(FutureWarning, match="FutureWarning"): - warnings.warn("FutureWarning", FutureWarning) + warnings.warn( + "FutureWarning", FutureWarning + ) # pdlint: ignore[warning_class] raise ValueError("Error") diff --git a/pandas/tests/util/test_rewrite_warning.py b/pandas/tests/util/test_rewrite_warning.py index f847a06d8ea8d..3db5e44d4fcea 100644 --- a/pandas/tests/util/test_rewrite_warning.py +++ b/pandas/tests/util/test_rewrite_warning.py @@ -36,4 +36,7 @@ def test_rewrite_warning(target_category, target_message, hit, new_category): with rewrite_warning( target_message, target_category, new_message, new_category ): - warnings.warn(message="Target message", category=FutureWarning) + warnings.warn( + message="Target message", + category=FutureWarning, # pdlint: ignore[warning_class] + )