Skip to content

Commit a4c63d6

Browse files
authored
CLN: Lint for usage of Deprecation/FutureWarning (#62178)
1 parent 5152088 commit a4c63d6

15 files changed

+166
-43
lines changed

.pre-commit-config.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,11 @@ repos:
264264
language: python
265265
entry: python scripts/validate_unwanted_patterns.py --validation-type="nodefault_used_not_only_for_typing"
266266
types: [python]
267+
- id: unwanted-patterns-doesnt-use-pandas-warnings
268+
name: Check that warning classes for deprecations use pandas' warning classes
269+
language: python
270+
entry: python scripts/validate_unwanted_patterns.py --validation-type="doesnt_use_pandas_warnings"
271+
types: [ python ]
267272
- id: no-return-exception
268273
name: Use raise instead of return for exceptions
269274
language: pygrep

pandas/_config/config.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373

7474
class DeprecatedOption(NamedTuple):
7575
key: str
76+
category: type[Warning]
7677
msg: str | None
7778
rkey: str | None
7879
removal_ver: str | None
@@ -589,6 +590,7 @@ def register_option(
589590

590591
def deprecate_option(
591592
key: str,
593+
category: type[Warning],
592594
msg: str | None = None,
593595
rkey: str | None = None,
594596
removal_ver: str | None = None,
@@ -608,6 +610,8 @@ def deprecate_option(
608610
key : str
609611
Name of the option to be deprecated.
610612
must be a fully-qualified option name (e.g "x.y.z.rkey").
613+
category : Warning
614+
Warning class for the deprecation.
611615
msg : str, optional
612616
Warning message to output when the key is referenced.
613617
if no message is given a default message will be emitted.
@@ -631,7 +635,7 @@ def deprecate_option(
631635
if key in _deprecated_options:
632636
raise OptionError(f"Option '{key}' has already been defined as deprecated.")
633637

634-
_deprecated_options[key] = DeprecatedOption(key, msg, rkey, removal_ver)
638+
_deprecated_options[key] = DeprecatedOption(key, category, msg, rkey, removal_ver)
635639

636640

637641
#
@@ -716,7 +720,7 @@ def _warn_if_deprecated(key: str) -> bool:
716720
if d.msg:
717721
warnings.warn(
718722
d.msg,
719-
FutureWarning,
723+
d.category,
720724
stacklevel=find_stack_level(),
721725
)
722726
else:
@@ -728,7 +732,11 @@ def _warn_if_deprecated(key: str) -> bool:
728732
else:
729733
msg += ", please refrain from using it."
730734

731-
warnings.warn(msg, FutureWarning, stacklevel=find_stack_level())
735+
warnings.warn(
736+
msg,
737+
d.category,
738+
stacklevel=find_stack_level(),
739+
)
732740
return True
733741
return False
734742

pandas/core/arrays/string_.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ def __init__(
177177
"'pd.options.future.infer_string = True' option globally and use "
178178
'the "str" alias as a shorthand notation to specify a dtype '
179179
'(instead of "string[pyarrow_numpy]").',
180-
FutureWarning,
180+
FutureWarning, # pdlint: ignore[warning_class]
181181
stacklevel=find_stack_level(),
182182
)
183183
storage = "pyarrow"

pandas/core/arrays/string_arrow.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ def _convert_bool_result(self, values, na=lib.no_default, method_name=None):
245245
warnings.warn(
246246
f"Allowing a non-bool 'na' in obj.str.{method_name} is deprecated "
247247
"and will raise in a future version.",
248-
FutureWarning,
248+
FutureWarning, # pdlint: ignore[warning_class]
249249
stacklevel=find_stack_level(),
250250
)
251251
na = bool(na)

pandas/core/dtypes/dtypes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1053,7 +1053,7 @@ def __new__(cls, freq) -> PeriodDtype: # noqa: PYI034
10531053
warnings.warn(
10541054
"PeriodDtype[B] is deprecated and will be removed in a future "
10551055
"version. Use a DatetimeIndex with freq='B' instead",
1056-
FutureWarning,
1056+
FutureWarning, # pdlint: ignore[warning_class]
10571057
stacklevel=find_stack_level(),
10581058
)
10591059

pandas/core/generic.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9139,7 +9139,7 @@ def resample(
91399139
"deprecated and will be removed in a future version. "
91409140
"Explicitly cast PeriodIndex to DatetimeIndex before resampling "
91419141
"instead.",
9142-
FutureWarning,
9142+
FutureWarning, # pdlint: ignore[warning_class]
91439143
stacklevel=find_stack_level(),
91449144
)
91459145
else:

pandas/core/groupby/groupby.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ class providing the base-class of operations.
6161
from pandas.errors import (
6262
AbstractMethodError,
6363
DataError,
64+
Pandas4Warning,
6465
)
6566
from pandas.util._decorators import (
6667
Appender,
@@ -557,7 +558,7 @@ def groups(self) -> dict[Hashable, Index]:
557558
"and will be removed. In a future version `groups` by one element "
558559
"list will return tuple. Use ``df.groupby(by='a').groups`` "
559560
"instead of ``df.groupby(by=['a']).groups`` to avoid this warning",
560-
FutureWarning,
561+
Pandas4Warning,
561562
stacklevel=find_stack_level(),
562563
)
563564
return self._grouper.groups

pandas/core/resample.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1949,7 +1949,7 @@ def _resampler_for_grouping(self):
19491949
warnings.warn(
19501950
"Resampling a groupby with a PeriodIndex is deprecated. "
19511951
"Cast to DatetimeIndex before resampling instead.",
1952-
FutureWarning,
1952+
FutureWarning, # pdlint: ignore[warning_class]
19531953
stacklevel=find_stack_level(),
19541954
)
19551955
return PeriodIndexResamplerGroupby
@@ -2293,11 +2293,12 @@ def _get_resampler(self, obj: NDFrame) -> Resampler:
22932293
)
22942294
elif isinstance(ax, PeriodIndex):
22952295
if isinstance(ax, PeriodIndex):
2296+
# TODO: Enforce in 3.0 (#53481)
22962297
# GH#53481
22972298
warnings.warn(
22982299
"Resampling with a PeriodIndex is deprecated. "
22992300
"Cast index to DatetimeIndex before resampling instead.",
2300-
FutureWarning,
2301+
FutureWarning, # pdlint: ignore[warning_class]
23012302
stacklevel=find_stack_level(),
23022303
)
23032304
return PeriodIndexResampler(

pandas/core/strings/object_array.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,35 +159,38 @@ def _str_contains(
159159
upper_pat = pat.upper()
160160
f = lambda x: upper_pat in x.upper()
161161
if na is not lib.no_default and not isna(na) and not isinstance(na, bool):
162+
# TODO: Enforce in 3.0 (#59615)
162163
# GH#59561
163164
warnings.warn(
164165
"Allowing a non-bool 'na' in obj.str.contains is deprecated "
165166
"and will raise in a future version.",
166-
FutureWarning,
167+
FutureWarning, # pdlint: ignore[warning_class]
167168
stacklevel=find_stack_level(),
168169
)
169170
return self._str_map(f, na, dtype=np.dtype("bool"))
170171

171172
def _str_startswith(self, pat, na=lib.no_default):
172173
f = lambda x: x.startswith(pat)
173174
if na is not lib.no_default and not isna(na) and not isinstance(na, bool):
175+
# TODO: Enforce in 3.0 (#59615)
174176
# GH#59561
175177
warnings.warn(
176178
"Allowing a non-bool 'na' in obj.str.startswith is deprecated "
177179
"and will raise in a future version.",
178-
FutureWarning,
180+
FutureWarning, # pdlint: ignore[warning_class]
179181
stacklevel=find_stack_level(),
180182
)
181183
return self._str_map(f, na_value=na, dtype=np.dtype(bool))
182184

183185
def _str_endswith(self, pat, na=lib.no_default):
184186
f = lambda x: x.endswith(pat)
185187
if na is not lib.no_default and not isna(na) and not isinstance(na, bool):
188+
# TODO: Enforce in 3.0 (#59615)
186189
# GH#59561
187190
warnings.warn(
188191
"Allowing a non-bool 'na' in obj.str.endswith is deprecated "
189192
"and will raise in a future version.",
190-
FutureWarning,
193+
FutureWarning, # pdlint: ignore[warning_class]
191194
stacklevel=find_stack_level(),
192195
)
193196
return self._str_map(f, na_value=na, dtype=np.dtype(bool))

pandas/tests/config/test_config.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,14 @@ def test_register_option(self):
7575
def test_describe_option(self):
7676
cf.register_option("a", 1, "doc")
7777
cf.register_option("b", 1, "doc2")
78-
cf.deprecate_option("b")
78+
cf.deprecate_option("b", FutureWarning)
7979

8080
cf.register_option("c.d.e1", 1, "doc3")
8181
cf.register_option("c.d.e2", 1, "doc4")
8282
cf.register_option("f", 1)
8383
cf.register_option("g.h", 1)
8484
cf.register_option("k", 2)
85-
cf.deprecate_option("g.h", rkey="k")
85+
cf.deprecate_option("g.h", FutureWarning, rkey="k")
8686
cf.register_option("l", "foo")
8787

8888
# non-existent keys raise KeyError
@@ -111,7 +111,8 @@ def test_describe_option(self):
111111
cf.set_option("l", "bar")
112112
assert "bar" in cf.describe_option("l", _print_desc=False)
113113

114-
def test_case_insensitive(self):
114+
@pytest.mark.parametrize("category", [DeprecationWarning, FutureWarning])
115+
def test_case_insensitive(self, category):
115116
cf.register_option("KanBAN", 1, "doc")
116117

117118
assert "doc" in cf.describe_option("kanbaN", _print_desc=False)
@@ -124,9 +125,9 @@ def test_case_insensitive(self):
124125
with pytest.raises(OptionError, match=msg):
125126
cf.get_option("no_such_option")
126127

127-
cf.deprecate_option("KanBan")
128+
cf.deprecate_option("KanBan", category)
128129
msg = "'kanban' is deprecated, please refrain from using it."
129-
with pytest.raises(FutureWarning, match=msg):
130+
with pytest.raises(category, match=msg):
130131
cf.get_option("kAnBaN")
131132

132133
def test_get_option(self):
@@ -285,7 +286,7 @@ def test_reset_option_all(self):
285286

286287
def test_deprecate_option(self):
287288
# we can deprecate non-existent options
288-
cf.deprecate_option("foo")
289+
cf.deprecate_option("foo", FutureWarning)
289290

290291
with tm.assert_produces_warning(FutureWarning, match="deprecated"):
291292
with pytest.raises(KeyError, match="No such keys.s.: 'foo'"):
@@ -295,15 +296,15 @@ def test_deprecate_option(self):
295296
cf.register_option("b.c", "hullo", "doc2")
296297
cf.register_option("foo", "hullo", "doc2")
297298

298-
cf.deprecate_option("a", removal_ver="nifty_ver")
299+
cf.deprecate_option("a", FutureWarning, removal_ver="nifty_ver")
299300
with tm.assert_produces_warning(FutureWarning, match="eprecated.*nifty_ver"):
300301
cf.get_option("a")
301302

302303
msg = "Option 'a' has already been defined as deprecated"
303304
with pytest.raises(OptionError, match=msg):
304-
cf.deprecate_option("a")
305+
cf.deprecate_option("a", FutureWarning)
305306

306-
cf.deprecate_option("b.c", "zounds!")
307+
cf.deprecate_option("b.c", FutureWarning, "zounds!")
307308
with tm.assert_produces_warning(FutureWarning, match="zounds!"):
308309
cf.get_option("b.c")
309310

@@ -313,7 +314,7 @@ def test_deprecate_option(self):
313314
assert cf.get_option("d.a") == "foo"
314315
assert cf.get_option("d.dep") == "bar"
315316

316-
cf.deprecate_option("d.dep", rkey="d.a") # reroute d.dep to d.a
317+
cf.deprecate_option("d.dep", FutureWarning, rkey="d.a") # reroute d.dep to d.a
317318
with tm.assert_produces_warning(FutureWarning, match="eprecated"):
318319
assert cf.get_option("d.dep") == "foo"
319320

0 commit comments

Comments
 (0)