@@ -1045,7 +1045,7 @@ def _except_handlers_ignores_exceptions(
1045
1045
1046
1046
1047
1047
def get_exception_handlers (
1048
- node : nodes .NodeNG , exception : type [Exception ] = Exception
1048
+ node : nodes .NodeNG , exception : type [Exception ] | str = Exception
1049
1049
) -> list [nodes .ExceptHandler ] | None :
1050
1050
"""Return the collections of handlers handling the exception in arguments.
1051
1051
@@ -1064,6 +1064,59 @@ def get_exception_handlers(
1064
1064
return []
1065
1065
1066
1066
1067
+ def get_contextlib_with_statements (node : nodes .NodeNG ) -> Iterator [nodes .With ]:
1068
+ """Get all contextlib.with statements in the ancestors of the given node."""
1069
+ for with_node in node .node_ancestors ():
1070
+ if isinstance (with_node , nodes .With ):
1071
+ yield with_node
1072
+
1073
+
1074
+ def _suppresses_exception (
1075
+ call : nodes .Call , exception : type [Exception ] | str = Exception
1076
+ ) -> bool :
1077
+ """Check if the given node suppresses the given exception."""
1078
+ if not isinstance (exception , str ):
1079
+ exception = exception .__name__
1080
+ for arg in call .args :
1081
+ inferred = safe_infer (arg )
1082
+ if isinstance (inferred , nodes .ClassDef ):
1083
+ if inferred .name == exception :
1084
+ return True
1085
+ elif isinstance (inferred , nodes .Tuple ):
1086
+ for elt in inferred .elts :
1087
+ inferred_elt = safe_infer (elt )
1088
+ if (
1089
+ isinstance (inferred_elt , nodes .ClassDef )
1090
+ and inferred_elt .name == exception
1091
+ ):
1092
+ return True
1093
+ return False
1094
+
1095
+
1096
+ def get_contextlib_suppressors (
1097
+ node : nodes .NodeNG , exception : type [Exception ] | str = Exception
1098
+ ) -> Iterator [nodes .With ]:
1099
+ """Return the contextlib suppressors handling the exception.
1100
+
1101
+ Args:
1102
+ node (nodes.NodeNG): A node that is potentially wrapped in a contextlib.suppress.
1103
+ exception (builtin.Exception): exception or name of the exception.
1104
+
1105
+ Yields:
1106
+ nodes.With: A with node that is suppressing the exception.
1107
+ """
1108
+ for with_node in get_contextlib_with_statements (node ):
1109
+ for item , _ in with_node .items :
1110
+ if isinstance (item , nodes .Call ):
1111
+ inferred = safe_infer (item .func )
1112
+ if (
1113
+ isinstance (inferred , nodes .ClassDef )
1114
+ and inferred .qname () == "contextlib.suppress"
1115
+ ):
1116
+ if _suppresses_exception (item , exception ):
1117
+ yield with_node
1118
+
1119
+
1067
1120
def is_node_inside_try_except (node : nodes .Raise ) -> bool :
1068
1121
"""Check if the node is directly under a Try/Except statement
1069
1122
(but not under an ExceptHandler!).
@@ -1079,17 +1132,17 @@ def is_node_inside_try_except(node: nodes.Raise) -> bool:
1079
1132
1080
1133
1081
1134
def node_ignores_exception (
1082
- node : nodes .NodeNG , exception : type [Exception ] = Exception
1135
+ node : nodes .NodeNG , exception : type [Exception ] | str = Exception
1083
1136
) -> bool :
1084
1137
"""Check if the node is in a TryExcept which handles the given exception.
1085
1138
1086
1139
If the exception is not given, the function is going to look for bare
1087
1140
excepts.
1088
1141
"""
1089
1142
managing_handlers = get_exception_handlers (node , exception )
1090
- if not managing_handlers :
1091
- return False
1092
- return any (managing_handlers )
1143
+ if managing_handlers :
1144
+ return True
1145
+ return any (get_contextlib_suppressors ( node , exception ) )
1093
1146
1094
1147
1095
1148
def class_is_abstract (node : nodes .ClassDef ) -> bool :
0 commit comments