Repository: pylint-dev/pylint

Issue:
False positive `redefined-outer-name` for variables bound to the exception in except clauses
### Bug description

```python
# pylint: disable=missing-module-docstring, missing-function-docstring
def f():
    try:
        raise ValueError('inner')
    except ValueError as e:
        print(e)

try:
    raise ValueError('outer')
except ValueError as e:
    print(e)

# print(e)  # NameError: name 'e' is not defined
```


### Configuration

_No response_

### Command used

```shell
pylint a.py
```


### Pylint output

```python
************* Module a
a.py:5:4: W0621: Redefining name 'e' from outer scope (line 10) (redefined-outer-name)
```


### Expected behavior

Pylint must not complain about reusing variables bound to the exception in except clauses because they are in fact not defined outside the corresponding except clause (as can be seen by uncommenting the last line in the example).

### Pylint version

```shell
pylint 3.2.2
astroid 3.2.2
Python 3.12.3 (main, Apr 10 2024, 05:33:47) [GCC 13.2.0]
```


### OS / Environment

_No response_

### Additional dependencies

_No response_


Discussion:

Patch:
diff --git a/doc/whatsnew/fragments/9671.false_positive b/doc/whatsnew/fragments/9671.false_positive
new file mode 100644
index 0000000000..23dafff10f
--- /dev/null
+++ b/doc/whatsnew/fragments/9671.false_positive
@@ -0,0 +1,3 @@
+Fix a false positive for ``redefined-outer-name`` when there is a name defined in an exception-handling block which shares the same name as a local variable that has been defined in a function body.
+
+Closes #9671
diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py
index 6c33a05556..8447320b89 100644
--- a/pylint/checkers/variables.py
+++ b/pylint/checkers/variables.py
@@ -1519,6 +1519,15 @@ def visit_functiondef(self, node: nodes.FunctionDef) -> None:
                 ):
                     continue
 
+                # Suppress emitting the message if the outer name is in the
+                # scope of an exception assignment.
+                # For example: the `e` in `except ValueError as e`
+                global_node = globs[name][0]
+                if isinstance(global_node, nodes.AssignName) and isinstance(
+                    global_node.parent, nodes.ExceptHandler
+                ):
+                    continue
+
                 line = definition.fromlineno
                 if not self._is_name_ignored(stmt, name):
                     self.add_message(


Test patch:
diff --git a/tests/functional/r/redefined/redefined_except_handler.py b/tests/functional/r/redefined/redefined_except_handler.py
index b774e32f2f..d621496263 100644
--- a/tests/functional/r/redefined/redefined_except_handler.py
+++ b/tests/functional/r/redefined/redefined_except_handler.py
@@ -70,3 +70,25 @@ def func():
     # pylint:disable-next=invalid-name, unused-variable
     except IOError as CustomException:  # [redefined-outer-name]
         pass
+
+
+# https://github.com/pylint-dev/pylint/issues/9671
+def function_before_exception():
+    """The local variable `e` should not trigger `redefined-outer-name`
+       when `e` is also defined in the subsequent exception handling block.
+    """
+    e = 42
+    return e
+
+try:
+    raise ValueError('outer')
+except ValueError as e:
+    print(e)
+
+
+def function_after_exception():
+    """The local variable `e` should not trigger `redefined-outer-name`
+       when `e` is also defined in the preceding exception handling block.
+    """
+    e = 42
+    return e