diff --git a/doc/whatsnew/fragments/9288.false_positive b/doc/whatsnew/fragments/9288.false_positive new file mode 100644 index 0000000000..470c308c69 --- /dev/null +++ b/doc/whatsnew/fragments/9288.false_positive @@ -0,0 +1,4 @@ +Fix false positive for ``invalid-exception-operation`` when concatenating tuples +of exception types. + +Closes #9288 diff --git a/pylint/checkers/exceptions.py b/pylint/checkers/exceptions.py index ce3f9367f0..2a193a6ce8 100644 --- a/pylint/checkers/exceptions.py +++ b/pylint/checkers/exceptions.py @@ -533,8 +533,19 @@ def gather_exceptions_from_handler( @utils.only_required_for_messages("wrong-exception-operation") def visit_binop(self, node: nodes.BinOp) -> None: if isinstance(node.parent, nodes.ExceptHandler): + both_sides_tuple_or_uninferable = isinstance( + utils.safe_infer(node.left), (nodes.Tuple, util.UninferableBase) + ) and isinstance( + utils.safe_infer(node.right), (nodes.Tuple, util.UninferableBase) + ) + # Tuple concatenation allowed + if both_sides_tuple_or_uninferable: + if node.op == "+": + return + suggestion = f"Did you mean '({node.left.as_string()} + {node.right.as_string()})' instead?" # except (V | A) - suggestion = f"Did you mean '({node.left.as_string()}, {node.right.as_string()})' instead?" + else: + suggestion = f"Did you mean '({node.left.as_string()}, {node.right.as_string()})' instead?" self.add_message("wrong-exception-operation", node=node, args=(suggestion,)) @utils.only_required_for_messages("wrong-exception-operation") diff --git a/tests/functional/w/wrong_exception_operation.py b/tests/functional/w/wrong_exception_operation.py index 8078573c41..141251fed9 100644 --- a/tests/functional/w/wrong_exception_operation.py +++ b/tests/functional/w/wrong_exception_operation.py @@ -16,3 +16,24 @@ 1/0 except (ValueError < TypeError): # [wrong-exception-operation] pass + + +# Concatenation of exception type tuples +DIVISION_BY_ZERO = (ZeroDivisionError,) +VALUE_ERROR = (ValueError,) +UNINFERABLE = DIVISION_BY_ZERO | VALUE_ERROR + +try: + 1/0 +except (ValueError, ) + DIVISION_BY_ZERO: + pass + +try: + 1/0 +except (ValueError, ) | DIVISION_BY_ZERO: # [wrong-exception-operation] + pass + +try: + 1/0 +except (ValueError, ) + UNINFERABLE: + pass diff --git a/tests/functional/w/wrong_exception_operation.txt b/tests/functional/w/wrong_exception_operation.txt index dc3c213462..d7eae8134f 100644 --- a/tests/functional/w/wrong_exception_operation.txt +++ b/tests/functional/w/wrong_exception_operation.txt @@ -2,3 +2,4 @@ catching-non-exception:6:8:6:30::"Catching an exception which doesn't inherit fr wrong-exception-operation:6:8:6:30::Invalid exception operation. Did you mean '(ValueError, TypeError)' instead?:UNDEFINED wrong-exception-operation:11:8:11:30::Invalid exception operation. Did you mean '(ValueError, TypeError)' instead?:UNDEFINED wrong-exception-operation:17:8:17:30::Invalid exception operation. Did you mean '(ValueError, TypeError)' instead?:UNDEFINED +wrong-exception-operation:33:7:33:40::Invalid exception operation. Did you mean '((ValueError, ) + DIVISION_BY_ZERO)' instead?:UNDEFINED