@@ -421,6 +421,7 @@ def visit_ClassDef(self, node):
421
421
422
422
def visit_Try (self , node ):
423
423
self .check_for_b012 (node )
424
+ self .check_for_b025 (node )
424
425
self .generic_visit (node )
425
426
426
427
def visit_Compare (self , node ):
@@ -837,6 +838,24 @@ def check_for_b022(self, node):
837
838
):
838
839
self .errors .append (B022 (node .lineno , node .col_offset ))
839
840
841
+ def check_for_b025 (self , node ):
842
+ seen = []
843
+ for handler in node .handlers :
844
+ if isinstance (handler .type , (ast .Name , ast .Attribute )):
845
+ name = "." .join (compose_call_path (handler .type ))
846
+ seen .append (name )
847
+ elif isinstance (handler .type , ast .Tuple ):
848
+ # to avoid checking the same as B014, remove duplicates per except
849
+ uniques = set ()
850
+ for entry in handler .type .elts :
851
+ name = "." .join (compose_call_path (entry ))
852
+ uniques .add (name )
853
+ seen .extend (uniques )
854
+ # sort to have a deterministic output
855
+ duplicates = sorted (set (x for x in seen if seen .count (x ) > 1 ))
856
+ for duplicate in duplicates :
857
+ self .errors .append (B025 (node .lineno , node .col_offset , vars = (duplicate ,)))
858
+
840
859
841
860
def compose_call_path (node ):
842
861
if isinstance (node , ast .Attribute ):
@@ -1178,6 +1197,12 @@ def visit_Lambda(self, node):
1178
1197
" decorators."
1179
1198
)
1180
1199
)
1200
+ B025 = Error (
1201
+ message = (
1202
+ "B025 Exception `{0}` has been caught multiple times. Only the first except"
1203
+ " will be considered and all other except catches can be safely removed."
1204
+ )
1205
+ )
1181
1206
1182
1207
# Warnings disabled by default.
1183
1208
B901 = Error (
0 commit comments