Skip to content

Commit

Permalink
Fix #5370: Emit redefined-outer-name when a nested except handler s…
Browse files Browse the repository at this point in the history
…hadows an outer one (#5630)

* Add regression test for #4434.

Co-authored-by: Pierre Sassoulas <pierre.sassoulas@gmail.com>
Co-authored-by: Daniël van Noord <13665637+DanielNoord@users.noreply.github.com>
  • Loading branch information
3 people committed Jan 3, 2022
1 parent fffde57 commit 619d853
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 0 deletions.
5 changes: 5 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,11 @@ Release date: TBA

Partially closes #1730

* Emit ``redefined-outer-name`` when a nested except handler shadows an outer one.

Closes #4434
Closes #5370

* Fatal errors now emit a score of 0.0 regardless of whether the linted module
contained any statements

Expand Down
5 changes: 5 additions & 0 deletions doc/whatsnew/2.13.rst
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,11 @@ Other Changes

* Fixed crash on uninferable decorators on Python 3.6 and 3.7

* Emit ``redefined-outer-name`` when a nested except handler shadows an outer one.

Closes #4434
Closes #5370

* Fatal errors now emit a score of 0.0 regardless of whether the linted module
contained any statements

Expand Down
26 changes: 26 additions & 0 deletions pylint/checkers/variables.py
Original file line number Diff line number Diff line change
Expand Up @@ -846,6 +846,10 @@ def __init__(self, linter=None):
self._checking_mod_attr = None
self._loop_variables = []
self._type_annotation_names = []
self._except_handler_names_queue: List[
Tuple[nodes.ExceptHandler, nodes.AssignName]
] = []
"""This is a queue, last in first out"""
self._postponed_evaluation_enabled = False

def open(self) -> None:
Expand Down Expand Up @@ -1130,6 +1134,28 @@ def visit_name(self, node: nodes.Name) -> None:
if self._is_undefined_loop_variable_enabled:
self._loopvar_name(node)

@utils.check_messages("redefined-outer-name")
def visit_excepthandler(self, node: nodes.ExceptHandler) -> None:
if not node.name or not isinstance(node.name, nodes.AssignName):
return

for outer_except, outer_except_assign_name in self._except_handler_names_queue:
if node.name.name == outer_except_assign_name.name:
self.add_message(
"redefined-outer-name",
args=(outer_except_assign_name.name, outer_except.fromlineno),
node=node,
)
break

self._except_handler_names_queue.append((node, node.name))

@utils.check_messages("redefined-outer-name")
def leave_excepthandler(self, node: nodes.ExceptHandler) -> None:
if not node.name or not isinstance(node.name, nodes.AssignName):
return
self._except_handler_names_queue.pop()

def _undefined_and_used_before_checker(
self, node: nodes.Name, stmt: nodes.NodeNG
) -> None:
Expand Down
72 changes: 72 additions & 0 deletions tests/functional/r/redefined_except_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
"""Tests for except handlers that shadow outer except handlers or exceptions.
See: https://github.com/PyCQA/pylint/issues/5370
"""

try:
pass
except ImportError as err:
try:
pass
except ImportError as err: # [redefined-outer-name]
pass
print(err)

try:
pass
except ImportError as err:
try:
pass
except ImportError as err2:
pass
print(err)

try:
try:
pass
except ImportError as err:
pass
except ImportError:
try:
pass
except ImportError as err:
pass
print(err)


try:
try:
pass
except ImportError as err:
pass
except ImportError as err:
try:
pass
except ImportError:
pass
print(err)

try:
pass
except ImportError as err:
try:
pass
except ImportError as err2:
try:
pass
except ImportError as err: # [redefined-outer-name]
pass
print(err)


class CustomException(Exception):
"""https://github.com/PyCQA/pylint/issues/4434"""


def func():
"""Override CustomException by except .. as .."""
try:
raise CustomException('Test') # [used-before-assignment]
# pylint:disable-next=invalid-name, unused-variable
except IOError as CustomException: # [redefined-outer-name]
pass
4 changes: 4 additions & 0 deletions tests/functional/r/redefined_except_handler.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
redefined-outer-name:11:4:12:12::Redefining name 'err' from outer scope (line 8):UNDEFINED
redefined-outer-name:57:8:58:16::Redefining name 'err' from outer scope (line 51):UNDEFINED
used-before-assignment:69:14:69:29:func:Using variable 'CustomException' before assignment:UNDEFINED
redefined-outer-name:71:4:72:12:func:Redefining name 'CustomException' from outer scope (line 62):UNDEFINED

0 comments on commit 619d853

Please sign in to comment.