Skip to content

Commit 3d2680b

Browse files
committed
Fix type of pytest.warns, and fix check_untyped_defs in test_recwarn
The expected_warning is optional.
1 parent 0b60315 commit 3d2680b

File tree

2 files changed

+64
-60
lines changed

2 files changed

+64
-60
lines changed

src/_pytest/recwarn.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def deprecated_call(func=None, *args, **kwargs):
5757

5858
@overload
5959
def warns(
60-
expected_warning: Union["Type[Warning]", Tuple["Type[Warning]", ...]],
60+
expected_warning: Optional[Union["Type[Warning]", Tuple["Type[Warning]", ...]]],
6161
*,
6262
match: "Optional[Union[str, Pattern]]" = ...
6363
) -> "WarningsChecker":
@@ -66,7 +66,7 @@ def warns(
6666

6767
@overload # noqa: F811
6868
def warns( # noqa: F811
69-
expected_warning: Union["Type[Warning]", Tuple["Type[Warning]", ...]],
69+
expected_warning: Optional[Union["Type[Warning]", Tuple["Type[Warning]", ...]]],
7070
func: Callable,
7171
*args: Any,
7272
match: Optional[Union[str, "Pattern"]] = ...,
@@ -76,7 +76,7 @@ def warns( # noqa: F811
7676

7777

7878
def warns( # noqa: F811
79-
expected_warning: Union["Type[Warning]", Tuple["Type[Warning]", ...]],
79+
expected_warning: Optional[Union["Type[Warning]", Tuple["Type[Warning]", ...]]],
8080
*args: Any,
8181
match: Optional[Union[str, "Pattern"]] = None,
8282
**kwargs: Any

testing/test_recwarn.py

Lines changed: 61 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
import re
22
import warnings
3+
from typing import Optional
34

45
import pytest
6+
from _pytest.outcomes import Failed
57
from _pytest.recwarn import WarningsRecorder
68

79

8-
def test_recwarn_stacklevel(recwarn):
10+
def test_recwarn_stacklevel(recwarn: WarningsRecorder) -> None:
911
warnings.warn("hello")
1012
warn = recwarn.pop()
1113
assert warn.filename == __file__
1214

1315

14-
def test_recwarn_functional(testdir):
16+
def test_recwarn_functional(testdir) -> None:
1517
testdir.makepyfile(
1618
"""
1719
import warnings
@@ -26,7 +28,7 @@ def test_method(recwarn):
2628

2729

2830
class TestWarningsRecorderChecker:
29-
def test_recording(self):
31+
def test_recording(self) -> None:
3032
rec = WarningsRecorder()
3133
with rec:
3234
assert not rec.list
@@ -42,23 +44,23 @@ def test_recording(self):
4244
assert values is rec.list
4345
pytest.raises(AssertionError, rec.pop)
4446

45-
def test_warn_stacklevel(self):
47+
def test_warn_stacklevel(self) -> None:
4648
"""#4243"""
4749
rec = WarningsRecorder()
4850
with rec:
4951
warnings.warn("test", DeprecationWarning, 2)
5052

51-
def test_typechecking(self):
53+
def test_typechecking(self) -> None:
5254
from _pytest.recwarn import WarningsChecker
5355

5456
with pytest.raises(TypeError):
55-
WarningsChecker(5)
57+
WarningsChecker(5) # type: ignore
5658
with pytest.raises(TypeError):
57-
WarningsChecker(("hi", RuntimeWarning))
59+
WarningsChecker(("hi", RuntimeWarning)) # type: ignore
5860
with pytest.raises(TypeError):
59-
WarningsChecker([DeprecationWarning, RuntimeWarning])
61+
WarningsChecker([DeprecationWarning, RuntimeWarning]) # type: ignore
6062

61-
def test_invalid_enter_exit(self):
63+
def test_invalid_enter_exit(self) -> None:
6264
# wrap this test in WarningsRecorder to ensure warning state gets reset
6365
with WarningsRecorder():
6466
with pytest.raises(RuntimeError):
@@ -75,50 +77,52 @@ def test_invalid_enter_exit(self):
7577
class TestDeprecatedCall:
7678
"""test pytest.deprecated_call()"""
7779

78-
def dep(self, i, j=None):
80+
def dep(self, i: int, j: Optional[int] = None) -> int:
7981
if i == 0:
8082
warnings.warn("is deprecated", DeprecationWarning, stacklevel=1)
8183
return 42
8284

83-
def dep_explicit(self, i):
85+
def dep_explicit(self, i: int) -> None:
8486
if i == 0:
8587
warnings.warn_explicit(
8688
"dep_explicit", category=DeprecationWarning, filename="hello", lineno=3
8789
)
8890

89-
def test_deprecated_call_raises(self):
90-
with pytest.raises(pytest.fail.Exception, match="No warnings of type"):
91+
def test_deprecated_call_raises(self) -> None:
92+
with pytest.raises(Failed, match="No warnings of type"):
9193
pytest.deprecated_call(self.dep, 3, 5)
9294

93-
def test_deprecated_call(self):
95+
def test_deprecated_call(self) -> None:
9496
pytest.deprecated_call(self.dep, 0, 5)
9597

96-
def test_deprecated_call_ret(self):
98+
def test_deprecated_call_ret(self) -> None:
9799
ret = pytest.deprecated_call(self.dep, 0)
98100
assert ret == 42
99101

100-
def test_deprecated_call_preserves(self):
101-
onceregistry = warnings.onceregistry.copy()
102-
filters = warnings.filters[:]
102+
def test_deprecated_call_preserves(self) -> None:
103+
# Type ignored because `onceregistry` and `filters` are not
104+
# documented API.
105+
onceregistry = warnings.onceregistry.copy() # type: ignore
106+
filters = warnings.filters[:] # type: ignore
103107
warn = warnings.warn
104108
warn_explicit = warnings.warn_explicit
105109
self.test_deprecated_call_raises()
106110
self.test_deprecated_call()
107-
assert onceregistry == warnings.onceregistry
108-
assert filters == warnings.filters
111+
assert onceregistry == warnings.onceregistry # type: ignore
112+
assert filters == warnings.filters # type: ignore
109113
assert warn is warnings.warn
110114
assert warn_explicit is warnings.warn_explicit
111115

112-
def test_deprecated_explicit_call_raises(self):
113-
with pytest.raises(pytest.fail.Exception):
116+
def test_deprecated_explicit_call_raises(self) -> None:
117+
with pytest.raises(Failed):
114118
pytest.deprecated_call(self.dep_explicit, 3)
115119

116-
def test_deprecated_explicit_call(self):
120+
def test_deprecated_explicit_call(self) -> None:
117121
pytest.deprecated_call(self.dep_explicit, 0)
118122
pytest.deprecated_call(self.dep_explicit, 0)
119123

120124
@pytest.mark.parametrize("mode", ["context_manager", "call"])
121-
def test_deprecated_call_no_warning(self, mode):
125+
def test_deprecated_call_no_warning(self, mode) -> None:
122126
"""Ensure deprecated_call() raises the expected failure when its block/function does
123127
not raise a deprecation warning.
124128
"""
@@ -127,7 +131,7 @@ def f():
127131
pass
128132

129133
msg = "No warnings of type (.*DeprecationWarning.*, .*PendingDeprecationWarning.*)"
130-
with pytest.raises(pytest.fail.Exception, match=msg):
134+
with pytest.raises(Failed, match=msg):
131135
if mode == "call":
132136
pytest.deprecated_call(f)
133137
else:
@@ -140,7 +144,7 @@ def f():
140144
@pytest.mark.parametrize("mode", ["context_manager", "call"])
141145
@pytest.mark.parametrize("call_f_first", [True, False])
142146
@pytest.mark.filterwarnings("ignore")
143-
def test_deprecated_call_modes(self, warning_type, mode, call_f_first):
147+
def test_deprecated_call_modes(self, warning_type, mode, call_f_first) -> None:
144148
"""Ensure deprecated_call() captures a deprecation warning as expected inside its
145149
block/function.
146150
"""
@@ -159,7 +163,7 @@ def f():
159163
assert f() == 10
160164

161165
@pytest.mark.parametrize("mode", ["context_manager", "call"])
162-
def test_deprecated_call_exception_is_raised(self, mode):
166+
def test_deprecated_call_exception_is_raised(self, mode) -> None:
163167
"""If the block of the code being tested by deprecated_call() raises an exception,
164168
it must raise the exception undisturbed.
165169
"""
@@ -174,7 +178,7 @@ def f():
174178
with pytest.deprecated_call():
175179
f()
176180

177-
def test_deprecated_call_specificity(self):
181+
def test_deprecated_call_specificity(self) -> None:
178182
other_warnings = [
179183
Warning,
180184
UserWarning,
@@ -189,78 +193,78 @@ def test_deprecated_call_specificity(self):
189193
def f():
190194
warnings.warn(warning("hi"))
191195

192-
with pytest.raises(pytest.fail.Exception):
196+
with pytest.raises(Failed):
193197
pytest.deprecated_call(f)
194-
with pytest.raises(pytest.fail.Exception):
198+
with pytest.raises(Failed):
195199
with pytest.deprecated_call():
196200
f()
197201

198-
def test_deprecated_call_supports_match(self):
202+
def test_deprecated_call_supports_match(self) -> None:
199203
with pytest.deprecated_call(match=r"must be \d+$"):
200204
warnings.warn("value must be 42", DeprecationWarning)
201205

202-
with pytest.raises(pytest.fail.Exception):
206+
with pytest.raises(Failed):
203207
with pytest.deprecated_call(match=r"must be \d+$"):
204208
warnings.warn("this is not here", DeprecationWarning)
205209

206210

207211
class TestWarns:
208-
def test_check_callable(self):
212+
def test_check_callable(self) -> None:
209213
source = "warnings.warn('w1', RuntimeWarning)"
210214
with pytest.raises(TypeError, match=r".* must be callable"):
211-
pytest.warns(RuntimeWarning, source)
215+
pytest.warns(RuntimeWarning, source) # type: ignore
212216

213-
def test_several_messages(self):
217+
def test_several_messages(self) -> None:
214218
# different messages, b/c Python suppresses multiple identical warnings
215219
pytest.warns(RuntimeWarning, lambda: warnings.warn("w1", RuntimeWarning))
216-
with pytest.raises(pytest.fail.Exception):
220+
with pytest.raises(Failed):
217221
pytest.warns(UserWarning, lambda: warnings.warn("w2", RuntimeWarning))
218222
pytest.warns(RuntimeWarning, lambda: warnings.warn("w3", RuntimeWarning))
219223

220-
def test_function(self):
224+
def test_function(self) -> None:
221225
pytest.warns(
222226
SyntaxWarning, lambda msg: warnings.warn(msg, SyntaxWarning), "syntax"
223227
)
224228

225-
def test_warning_tuple(self):
229+
def test_warning_tuple(self) -> None:
226230
pytest.warns(
227231
(RuntimeWarning, SyntaxWarning), lambda: warnings.warn("w1", RuntimeWarning)
228232
)
229233
pytest.warns(
230234
(RuntimeWarning, SyntaxWarning), lambda: warnings.warn("w2", SyntaxWarning)
231235
)
232236
pytest.raises(
233-
pytest.fail.Exception,
237+
Failed,
234238
lambda: pytest.warns(
235239
(RuntimeWarning, SyntaxWarning),
236240
lambda: warnings.warn("w3", UserWarning),
237241
),
238242
)
239243

240-
def test_as_contextmanager(self):
244+
def test_as_contextmanager(self) -> None:
241245
with pytest.warns(RuntimeWarning):
242246
warnings.warn("runtime", RuntimeWarning)
243247

244248
with pytest.warns(UserWarning):
245249
warnings.warn("user", UserWarning)
246250

247-
with pytest.raises(pytest.fail.Exception) as excinfo:
251+
with pytest.raises(Failed) as excinfo:
248252
with pytest.warns(RuntimeWarning):
249253
warnings.warn("user", UserWarning)
250254
excinfo.match(
251255
r"DID NOT WARN. No warnings of type \(.+RuntimeWarning.+,\) was emitted. "
252256
r"The list of emitted warnings is: \[UserWarning\('user',?\)\]."
253257
)
254258

255-
with pytest.raises(pytest.fail.Exception) as excinfo:
259+
with pytest.raises(Failed) as excinfo:
256260
with pytest.warns(UserWarning):
257261
warnings.warn("runtime", RuntimeWarning)
258262
excinfo.match(
259263
r"DID NOT WARN. No warnings of type \(.+UserWarning.+,\) was emitted. "
260264
r"The list of emitted warnings is: \[RuntimeWarning\('runtime',?\)\]."
261265
)
262266

263-
with pytest.raises(pytest.fail.Exception) as excinfo:
267+
with pytest.raises(Failed) as excinfo:
264268
with pytest.warns(UserWarning):
265269
pass
266270
excinfo.match(
@@ -269,7 +273,7 @@ def test_as_contextmanager(self):
269273
)
270274

271275
warning_classes = (UserWarning, FutureWarning)
272-
with pytest.raises(pytest.fail.Exception) as excinfo:
276+
with pytest.raises(Failed) as excinfo:
273277
with pytest.warns(warning_classes) as warninfo:
274278
warnings.warn("runtime", RuntimeWarning)
275279
warnings.warn("import", ImportWarning)
@@ -286,14 +290,14 @@ def test_as_contextmanager(self):
286290
)
287291
)
288292

289-
def test_record(self):
293+
def test_record(self) -> None:
290294
with pytest.warns(UserWarning) as record:
291295
warnings.warn("user", UserWarning)
292296

293297
assert len(record) == 1
294298
assert str(record[0].message) == "user"
295299

296-
def test_record_only(self):
300+
def test_record_only(self) -> None:
297301
with pytest.warns(None) as record:
298302
warnings.warn("user", UserWarning)
299303
warnings.warn("runtime", RuntimeWarning)
@@ -302,7 +306,7 @@ def test_record_only(self):
302306
assert str(record[0].message) == "user"
303307
assert str(record[1].message) == "runtime"
304308

305-
def test_record_by_subclass(self):
309+
def test_record_by_subclass(self) -> None:
306310
with pytest.warns(Warning) as record:
307311
warnings.warn("user", UserWarning)
308312
warnings.warn("runtime", RuntimeWarning)
@@ -325,7 +329,7 @@ class MyRuntimeWarning(RuntimeWarning):
325329
assert str(record[0].message) == "user"
326330
assert str(record[1].message) == "runtime"
327331

328-
def test_double_test(self, testdir):
332+
def test_double_test(self, testdir) -> None:
329333
"""If a test is run again, the warning should still be raised"""
330334
testdir.makepyfile(
331335
"""
@@ -341,32 +345,32 @@ def test(run):
341345
result = testdir.runpytest()
342346
result.stdout.fnmatch_lines(["*2 passed in*"])
343347

344-
def test_match_regex(self):
348+
def test_match_regex(self) -> None:
345349
with pytest.warns(UserWarning, match=r"must be \d+$"):
346350
warnings.warn("value must be 42", UserWarning)
347351

348-
with pytest.raises(pytest.fail.Exception):
352+
with pytest.raises(Failed):
349353
with pytest.warns(UserWarning, match=r"must be \d+$"):
350354
warnings.warn("this is not here", UserWarning)
351355

352-
with pytest.raises(pytest.fail.Exception):
356+
with pytest.raises(Failed):
353357
with pytest.warns(FutureWarning, match=r"must be \d+$"):
354358
warnings.warn("value must be 42", UserWarning)
355359

356-
def test_one_from_multiple_warns(self):
360+
def test_one_from_multiple_warns(self) -> None:
357361
with pytest.warns(UserWarning, match=r"aaa"):
358362
warnings.warn("cccccccccc", UserWarning)
359363
warnings.warn("bbbbbbbbbb", UserWarning)
360364
warnings.warn("aaaaaaaaaa", UserWarning)
361365

362-
def test_none_of_multiple_warns(self):
363-
with pytest.raises(pytest.fail.Exception):
366+
def test_none_of_multiple_warns(self) -> None:
367+
with pytest.raises(Failed):
364368
with pytest.warns(UserWarning, match=r"aaa"):
365369
warnings.warn("bbbbbbbbbb", UserWarning)
366370
warnings.warn("cccccccccc", UserWarning)
367371

368372
@pytest.mark.filterwarnings("ignore")
369-
def test_can_capture_previously_warned(self):
373+
def test_can_capture_previously_warned(self) -> None:
370374
def f():
371375
warnings.warn(UserWarning("ohai"))
372376
return 10
@@ -375,8 +379,8 @@ def f():
375379
assert pytest.warns(UserWarning, f) == 10
376380
assert pytest.warns(UserWarning, f) == 10
377381

378-
def test_warns_context_manager_with_kwargs(self):
382+
def test_warns_context_manager_with_kwargs(self) -> None:
379383
with pytest.raises(TypeError) as excinfo:
380-
with pytest.warns(UserWarning, foo="bar"):
384+
with pytest.warns(UserWarning, foo="bar"): # type: ignore
381385
pass
382386
assert "Unexpected keyword arguments" in str(excinfo.value)

0 commit comments

Comments
 (0)