Skip to content

Commit e7bbdeb

Browse files
authoredFeb 2, 2023
Add a lint checking for warnings.warn stacklevel argument (#342)
* Add a lint checking for warnings.warn stacklevel argument * Fix formatting * Update lint descriptions * Move function to bottom
1 parent a2e0c95 commit e7bbdeb

File tree

4 files changed

+44
-0
lines changed

4 files changed

+44
-0
lines changed
 

‎README.rst

+5
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,10 @@ limitations make it difficult.
173173

174174
**B027**: Empty method in abstract base class, but has no abstract decorator. Consider adding @abstractmethod.
175175

176+
**BO28**: No explicit stacklevel keyword argument found. The warn method from the warnings module uses a
177+
stacklevel of 1 by default. This will only show a stack trace for the line on which the warn method is called.
178+
It is therefore recommended to use a stacklevel of 2 or greater to provide more information to the user.
179+
176180
Opinionated warnings
177181
~~~~~~~~~~~~~~~~~~~~
178182

@@ -320,6 +324,7 @@ Future
320324
``ast.Str`` nodes are all deprecated, but may still be used by some codebases in
321325
order to maintain backwards compatibility with Python 3.7.
322326
* B016: Warn when raising f-strings.
327+
* Add B028: Check for an explicit stacklevel keyword argument on the warn method from the warnings module.
323328

324329
23.1.20
325330
~~~~~~~~~

‎bugbear.py

+20
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,7 @@ def visit_Call(self, node):
360360
self.check_for_b026(node)
361361

362362
self.check_for_b905(node)
363+
self.check_for_b028(node)
363364
self.generic_visit(node)
364365

365366
def visit_Module(self, node):
@@ -1146,6 +1147,16 @@ def myunparse(node: ast.AST) -> str: # pragma: no cover
11461147
# if no pre-mark or variable detected, reset state
11471148
current_mark = variable = None
11481149

1150+
def check_for_b028(self, node):
1151+
if (
1152+
isinstance(node.func, ast.Attribute)
1153+
and node.func.attr == "warn"
1154+
and isinstance(node.func.value, ast.Name)
1155+
and node.func.value.id == "warnings"
1156+
and not any(kw.arg == "stacklevel" for kw in node.keywords)
1157+
):
1158+
self.errors.append(B028(node.lineno, node.col_offset))
1159+
11491160

11501161
def compose_call_path(node):
11511162
if isinstance(node, ast.Attribute):
@@ -1510,6 +1521,15 @@ def visit_Lambda(self, node):
15101521
" decorator. Consider adding @abstractmethod."
15111522
)
15121523
)
1524+
B028 = Error(
1525+
message=(
1526+
"B028 No explicit stacklevel keyword argument found. The warn method from the"
1527+
" warnings module uses a stacklevel of 1 by default. This will only show a"
1528+
" stack trace for the line on which the warn method is called."
1529+
" It is therefore recommended to use a stacklevel of 2 or"
1530+
" greater to provide more information to the user."
1531+
)
1532+
)
15131533

15141534
# Warnings disabled by default.
15151535
B901 = Error(

‎tests/b028.py

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import warnings
2+
3+
"""
4+
Should emit:
5+
B028 - on lines 8 and 9
6+
"""
7+
8+
warnings.warn(DeprecationWarning("test"))
9+
warnings.warn(DeprecationWarning("test"), source=None)
10+
warnings.warn(DeprecationWarning("test"), source=None, stacklevel=2)
11+
warnings.warn(DeprecationWarning("test"), stacklevel=1)

‎tests/test_bugbear.py

+8
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
B025,
4040
B026,
4141
B027,
42+
B028,
4243
B901,
4344
B902,
4445
B903,
@@ -430,6 +431,13 @@ def test_b027(self):
430431
)
431432
self.assertEqual(errors, expected)
432433

434+
def test_b028(self):
435+
filename = Path(__file__).absolute().parent / "b028.py"
436+
bbc = BugBearChecker(filename=str(filename))
437+
errors = list(bbc.run())
438+
expected = self.errors(B028(8, 0), B028(9, 0))
439+
self.assertEqual(errors, expected)
440+
433441
@unittest.skipIf(sys.version_info < (3, 8), "not implemented for <3.8")
434442
def test_b907(self):
435443
filename = Path(__file__).absolute().parent / "b907.py"

0 commit comments

Comments
 (0)
Please sign in to comment.