Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #5370: Emit redefined-outer-name when a nested except handler shadows an outer one #5630

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
jacobtylerwalls marked this conversation as resolved.
Show resolved Hide resolved

* 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
jacobtylerwalls marked this conversation as resolved.
Show resolved Hide resolved

* 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]
jacobtylerwalls marked this conversation as resolved.
Show resolved Hide resolved
# 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