@@ -243,7 +243,7 @@ def _flatten_excepthandler(node: ast.expr | None) -> Iterator[ast.expr | None]:
243
243
yield expr
244
244
245
245
246
- def _check_redundant_excepthandlers (names : Sequence [str ], node ):
246
+ def _check_redundant_excepthandlers (names : Sequence [str ], node , in_trystar ):
247
247
# See if any of the given exception names could be removed, e.g. from:
248
248
# (MyError, MyError) # duplicate names
249
249
# (MyError, BaseException) # everything derives from the Base
@@ -275,7 +275,7 @@ def _check_redundant_excepthandlers(names: Sequence[str], node):
275
275
return B014 (
276
276
node .lineno ,
277
277
node .col_offset ,
278
- vars = (", " .join (names ), as_ , desc ),
278
+ vars = (", " .join (names ), as_ , desc , in_trystar ),
279
279
)
280
280
return None
281
281
@@ -388,6 +388,9 @@ class BugBearVisitor(ast.NodeVisitor):
388
388
_b023_seen : set [error ] = attr .ib (factory = set , init = False )
389
389
_b005_imports : set [str ] = attr .ib (factory = set , init = False )
390
390
391
+ # set to "*" when inside a try/except*, for correctly printing errors
392
+ in_trystar : str = attr .ib (default = "" )
393
+
391
394
if False :
392
395
# Useful for tracing what the hell is going on.
393
396
@@ -457,7 +460,7 @@ def visit_ExceptHandler(self, node: ast.ExceptHandler) -> None:
457
460
else :
458
461
self .b040_caught_exception = B040CaughtException (node .name , False )
459
462
460
- names = self .check_for_b013_b029_b030 (node )
463
+ names = self .check_for_b013_b014_b029_b030 (node )
461
464
462
465
if (
463
466
"BaseException" in names
@@ -605,6 +608,12 @@ def visit_Try(self, node) -> None:
605
608
self .check_for_b025 (node )
606
609
self .generic_visit (node )
607
610
611
+ def visit_TryStar (self , node ) -> None :
612
+ outer_trystar = self .in_trystar
613
+ self .in_trystar = "*"
614
+ self .visit_Try (node )
615
+ self .in_trystar = outer_trystar
616
+
608
617
def visit_Compare (self , node ) -> None :
609
618
self .check_for_b015 (node )
610
619
self .generic_visit (node )
@@ -763,15 +772,17 @@ def _loop(node, bad_node_types) -> None:
763
772
bad_node_types = (ast .Return ,)
764
773
765
774
elif isinstance (node , bad_node_types ):
766
- self .errors .append (B012 (node .lineno , node .col_offset ))
775
+ self .errors .append (
776
+ B012 (node .lineno , node .col_offset , vars = (self .in_trystar ,))
777
+ )
767
778
768
779
for child in ast .iter_child_nodes (node ):
769
780
_loop (child , bad_node_types )
770
781
771
782
for child in node .finalbody :
772
783
_loop (child , (ast .Return , ast .Continue , ast .Break ))
773
784
774
- def check_for_b013_b029_b030 (self , node : ast .ExceptHandler ) -> list [str ]:
785
+ def check_for_b013_b014_b029_b030 (self , node : ast .ExceptHandler ) -> list [str ]:
775
786
handlers : Iterable [ast .expr | None ] = _flatten_excepthandler (node .type )
776
787
names : list [str ] = []
777
788
bad_handlers : list [object ] = []
@@ -791,16 +802,27 @@ def check_for_b013_b029_b030(self, node: ast.ExceptHandler) -> list[str]:
791
802
if bad_handlers :
792
803
self .errors .append (B030 (node .lineno , node .col_offset ))
793
804
if len (names ) == 0 and not bad_handlers and not ignored_handlers :
794
- self .errors .append (B029 (node .lineno , node .col_offset ))
805
+ self .errors .append (
806
+ B029 (node .lineno , node .col_offset , vars = (self .in_trystar ,))
807
+ )
795
808
elif (
796
809
len (names ) == 1
797
810
and not bad_handlers
798
811
and not ignored_handlers
799
812
and isinstance (node .type , ast .Tuple )
800
813
):
801
- self .errors .append (B013 (node .lineno , node .col_offset , vars = names ))
814
+ self .errors .append (
815
+ B013 (
816
+ node .lineno ,
817
+ node .col_offset ,
818
+ vars = (
819
+ * names ,
820
+ self .in_trystar ,
821
+ ),
822
+ )
823
+ )
802
824
else :
803
- maybe_error = _check_redundant_excepthandlers (names , node )
825
+ maybe_error = _check_redundant_excepthandlers (names , node , self . in_trystar )
804
826
if maybe_error is not None :
805
827
self .errors .append (maybe_error )
806
828
return names
@@ -1216,7 +1238,9 @@ def check_for_b904(self, node) -> None:
1216
1238
and not (isinstance (node .exc , ast .Name ) and node .exc .id .islower ())
1217
1239
and any (isinstance (n , ast .ExceptHandler ) for n in self .node_stack )
1218
1240
):
1219
- self .errors .append (B904 (node .lineno , node .col_offset ))
1241
+ self .errors .append (
1242
+ B904 (node .lineno , node .col_offset , vars = (self .in_trystar ,))
1243
+ )
1220
1244
1221
1245
def walk_function_body (self , node ):
1222
1246
def _loop (parent , node ):
@@ -1480,7 +1504,9 @@ def check_for_b025(self, node) -> None:
1480
1504
# sort to have a deterministic output
1481
1505
duplicates = sorted ({x for x in seen if seen .count (x ) > 1 })
1482
1506
for duplicate in duplicates :
1483
- self .errors .append (B025 (node .lineno , node .col_offset , vars = (duplicate ,)))
1507
+ self .errors .append (
1508
+ B025 (node .lineno , node .col_offset , vars = (duplicate , self .in_trystar ))
1509
+ )
1484
1510
1485
1511
@staticmethod
1486
1512
def _is_infinite_iterator (node : ast .expr ) -> bool :
@@ -2073,6 +2099,7 @@ def visit_Lambda(self, node) -> None:
2073
2099
error = namedtuple ("error" , "lineno col message type vars" )
2074
2100
Error = partial (partial , error , type = BugBearChecker , vars = ())
2075
2101
2102
+ # note: bare except* is a syntax error, so B001 does not need to handle it
2076
2103
B001 = Error (
2077
2104
message = (
2078
2105
"B001 Do not use bare `except:`, it also catches unexpected "
@@ -2194,20 +2221,20 @@ def visit_Lambda(self, node) -> None:
2194
2221
B012 = Error (
2195
2222
message = (
2196
2223
"B012 return/continue/break inside finally blocks cause exceptions "
2197
- "to be silenced. Exceptions should be silenced in except blocks. Control "
2224
+ "to be silenced. Exceptions should be silenced in except{0} blocks. Control "
2198
2225
"statements can be moved outside the finally block."
2199
2226
)
2200
2227
)
2201
2228
B013 = Error (
2202
2229
message = (
2203
2230
"B013 A length-one tuple literal is redundant. "
2204
- "Write `except {0}:` instead of `except ({0},):`."
2231
+ "Write `except{1} {0}:` instead of `except{1} ({0},):`."
2205
2232
)
2206
2233
)
2207
2234
B014 = Error (
2208
2235
message = (
2209
- "B014 Redundant exception types in `except ({0}){1}:`. "
2210
- "Write `except {2}{1}:`, which catches exactly the same exceptions."
2236
+ "B014 Redundant exception types in `except{3} ({0}){1}:`. "
2237
+ "Write `except{3} {2}{1}:`, which catches exactly the same exceptions."
2211
2238
)
2212
2239
)
2213
2240
B014_REDUNDANT_EXCEPTIONS = {
@@ -2296,8 +2323,8 @@ def visit_Lambda(self, node) -> None:
2296
2323
)
2297
2324
B025 = Error (
2298
2325
message = (
2299
- "B025 Exception `{0}` has been caught multiple times. Only the first except"
2300
- " will be considered and all other except catches can be safely removed."
2326
+ "B025 Exception `{0}` has been caught multiple times. Only the first except{0} "
2327
+ " will be considered and all other except{0} catches can be safely removed."
2301
2328
)
2302
2329
)
2303
2330
B026 = Error (
@@ -2325,7 +2352,7 @@ def visit_Lambda(self, node) -> None:
2325
2352
)
2326
2353
B029 = Error (
2327
2354
message = (
2328
- "B029 Using `except ():` with an empty tuple does not handle/catch "
2355
+ "B029 Using `except{0} ():` with an empty tuple does not handle/catch "
2329
2356
"anything. Add exceptions to handle."
2330
2357
)
2331
2358
)
@@ -2414,7 +2441,7 @@ def visit_Lambda(self, node) -> None:
2414
2441
2415
2442
B904 = Error (
2416
2443
message = (
2417
- "B904 Within an `except` clause, raise exceptions with `raise ... from err` or"
2444
+ "B904 Within an `except{0} ` clause, raise exceptions with `raise ... from err` or"
2418
2445
" `raise ... from None` to distinguish them from errors in exception handling. "
2419
2446
" See https://docs.python.org/3/tutorial/errors.html#exception-chaining for"
2420
2447
" details."
0 commit comments