@@ -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
11501161def 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.
15151535B901 = Error (
0 commit comments