Skip to content

Commit 815ab98

Browse files
authored
Add B017 support for pytest.raises (#317)
* Add B017 support for pytest.raises * Add pytest as optional dev dependency
1 parent fef1edc commit 815ab98

File tree

4 files changed

+35
-9
lines changed

4 files changed

+35
-9
lines changed

bugbear.py

+19-7
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,7 @@ def check_for_b016(self, node):
514514

515515
def check_for_b017(self, node):
516516
"""Checks for use of the evil syntax 'with assertRaises(Exception):'
517+
or 'with pytest.raises(Exception)'.
517518
518519
This form of assertRaises will catch everything that subclasses
519520
Exception, which happens to be the vast majority of Python internal
@@ -523,10 +524,21 @@ def check_for_b017(self, node):
523524
"""
524525
item = node.items[0]
525526
item_context = item.context_expr
527+
526528
if (
527529
hasattr(item_context, "func")
528-
and hasattr(item_context.func, "attr")
529-
and item_context.func.attr == "assertRaises"
530+
and (
531+
(
532+
hasattr(item_context.func, "attr")
533+
and item_context.func.attr == "assertRaises"
534+
)
535+
or (
536+
isinstance(item_context.func, ast.Attribute)
537+
and item_context.func.attr == "raises"
538+
and isinstance(item_context.func.value, ast.Name)
539+
and item_context.func.value.id == "pytest"
540+
)
541+
)
530542
and len(item_context.args) == 1
531543
and isinstance(item_context.args[0], ast.Name)
532544
and item_context.args[0].id == "Exception"
@@ -1282,11 +1294,11 @@ def visit_Lambda(self, node):
12821294
)
12831295
B017 = Error(
12841296
message=(
1285-
"B017 assertRaises(Exception): should be considered evil. "
1286-
"It can lead to your test passing even if the code being tested is "
1287-
"never executed due to a typo. Either assert for a more specific "
1288-
"exception (builtin or custom), use assertRaisesRegex, or use the "
1289-
"context manager form of assertRaises."
1297+
"B017 assertRaises(Exception): or pytest.raises(Exception) should "
1298+
"be considered evil. It can lead to your test passing even if the "
1299+
"code being tested is never executed due to a typo. Either assert "
1300+
"for a more specific exception (builtin or custom), use "
1301+
"assertRaisesRegex, or use the context manager form of assertRaises."
12901302
)
12911303
)
12921304
B018 = Error(

pyproject.toml

+2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ classifiers = [
3030
"Programming Language :: Python :: 3.8",
3131
"Programming Language :: Python :: 3.9",
3232
"Programming Language :: Python :: 3.10",
33+
"Programming Language :: Python :: 3.11",
3334
"Programming Language :: Python :: 3 :: Only",
3435
"Topic :: Software Development :: Libraries :: Python Modules",
3536
"Topic :: Software Development :: Quality Assurance",
@@ -52,6 +53,7 @@ dev = [
5253
"hypothesis",
5354
"hypothesmith>=0.2",
5455
"pre-commit",
56+
"pytest",
5557
]
5658

5759
[tool.setuptools]

tests/b017.py

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
"""
22
Should emit:
3-
B017 - on lines 20
3+
B017 - on lines 24 and 26.
44
"""
55
import asyncio
66
import unittest
77

8+
import pytest
9+
810
CONSTANT = True
911

1012

@@ -21,16 +23,26 @@ class Foobar(unittest.TestCase):
2123
def evil_raises(self) -> None:
2224
with self.assertRaises(Exception):
2325
raise Exception("Evil I say!")
26+
with pytest.raises(Exception):
27+
raise Exception("Evil I say!")
2428

2529
def context_manager_raises(self) -> None:
2630
with self.assertRaises(Exception) as ex:
2731
raise Exception("Context manager is good")
32+
with pytest.raises(Exception) as pyt_ex:
33+
raise Exception("Context manager is good")
34+
2835
self.assertEqual("Context manager is good", str(ex.exception))
36+
self.assertEqual("Context manager is good", str(pyt_ex.exception))
2937

3038
def regex_raises(self) -> None:
3139
with self.assertRaisesRegex(Exception, "Regex is good"):
3240
raise Exception("Regex is good")
41+
with pytest.raises(Exception, "Regex is good"):
42+
raise Exception("Regex is good")
3343

3444
def raises_with_absolute_reference(self):
3545
with self.assertRaises(asyncio.CancelledError):
3646
Foo()
47+
with pytest.raises(asyncio.CancelledError):
48+
Foo()

tests/test_bugbear.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ def test_b017(self):
252252
filename = Path(__file__).absolute().parent / "b017.py"
253253
bbc = BugBearChecker(filename=str(filename))
254254
errors = list(bbc.run())
255-
expected = self.errors(B017(22, 8))
255+
expected = self.errors(B017(24, 8), B017(26, 8))
256256
self.assertEqual(errors, expected)
257257

258258
def test_b018_functions(self):

0 commit comments

Comments
 (0)