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

Add Try node #1867

Merged
merged 3 commits into from
Jul 8, 2023
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
3 changes: 3 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,9 @@ Release date: TBA

Refs #2154

* Add new ``nodes.Try`` to better match Python AST. Replaces the ``TryExcept``
and ``TryFinally`` nodes which have been removed.

* Publicize ``NodeNG.repr_name()`` to facilitate finding a node's nice name.

Refs pylint-dev/pylint#8598
Expand Down
3 changes: 1 addition & 2 deletions astroid/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,7 @@
Slice,
Starred,
Subscript,
TryExcept,
TryFinally,
Try,
TryStar,
Tuple,
UnaryOp,
Expand Down
3 changes: 1 addition & 2 deletions astroid/node_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,7 @@
Slice,
Starred,
Subscript,
TryExcept,
TryFinally,
Try,
TryStar,
Tuple,
UnaryOp,
Expand Down
9 changes: 3 additions & 6 deletions astroid/nodes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,7 @@
Slice,
Starred,
Subscript,
TryExcept,
TryFinally,
Try,
TryStar,
Tuple,
TypeAlias,
Expand Down Expand Up @@ -188,8 +187,7 @@
Slice,
Starred,
Subscript,
TryExcept,
TryFinally,
Try,
TryStar,
Tuple,
TypeAlias,
Expand Down Expand Up @@ -283,8 +281,7 @@
"Slice",
"Starred",
"Subscript",
"TryExcept",
"TryFinally",
"Try",
"TryStar",
"Tuple",
"TypeAlias",
Expand Down
12 changes: 4 additions & 8 deletions astroid/nodes/as_string.py
Original file line number Diff line number Diff line change
Expand Up @@ -491,21 +491,17 @@ def visit_subscript(self, node) -> str:
idxstr = idxstr[1:-1]
return f"{self._precedence_parens(node, node.value)}[{idxstr}]"

def visit_tryexcept(self, node) -> str:
"""return an astroid.TryExcept node as string"""
def visit_try(self, node) -> str:
"""return an astroid.Try node as string"""
trys = [f"try:\n{self._stmt_list(node.body)}"]
for handler in node.handlers:
trys.append(handler.accept(self))
if node.orelse:
trys.append(f"else:\n{self._stmt_list(node.orelse)}")
if node.finalbody:
trys.append(f"finally:\n{self._stmt_list(node.finalbody)}")
return "\n".join(trys)

def visit_tryfinally(self, node) -> str:
"""return an astroid.TryFinally node as string"""
return "try:\n{}\nfinally:\n{}".format(
self._stmt_list(node.body), self._stmt_list(node.finalbody)
)

def visit_trystar(self, node) -> str:
"""return an astroid.TryStar node as string"""
trys = [f"try:\n{self._stmt_list(node.body)}"]
Expand Down
166 changes: 63 additions & 103 deletions astroid/nodes/node_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@
previous = stmt2
for node in stmt2.node_ancestors():
if node in stmt1_parents:
# if the common parent is a If or TryExcept statement, look if
# if the common parent is a If or Try statement, look if
# nodes are in exclusive branches
if isinstance(node, If) and exceptions is None:
c2attr, c2node = node.locate_child(previous)
Expand All @@ -155,7 +155,7 @@
if c1attr != c2attr:
# different `If` branches (`If.body` and `If.orelse`)
return True
elif isinstance(node, TryExcept):
elif isinstance(node, Try):
c2attr, c2node = node.locate_child(previous)
c1attr, c1node = node.locate_child(children[node])
if c1node is not c2node:
Expand Down Expand Up @@ -3720,97 +3720,33 @@
return self._infer_subscript(context, **kwargs)


class TryExcept(_base_nodes.MultiLineWithElseBlockNode, _base_nodes.Statement):
"""Class representing an :class:`ast.TryExcept` node.
class Try(_base_nodes.MultiLineWithElseBlockNode, _base_nodes.Statement):
"""Class representing a :class:`ast.Try` node.

>>> import astroid
>>> node = astroid.extract_node('''
try:
do_something()
except Exception as error:
print("Error!")
finally:
print("Cleanup!")
''')
>>> node
<TryExcept l.2 at 0x7f23b2e9d908>
<Try l.2 at 0x7f23b2e41d68>
"""

_astroid_fields = ("body", "handlers", "orelse")
_multi_line_block_fields = ("body", "handlers", "orelse")

body: list[NodeNG]
"""The contents of the block to catch exceptions from."""

handlers: list[ExceptHandler]
"""The exception handlers."""

orelse: list[NodeNG]
"""The contents of the ``else`` block."""

def postinit(
self,
body: list[NodeNG],
handlers: list[ExceptHandler],
orelse: list[NodeNG],
) -> None:
self.body = body
self.handlers = handlers
self.orelse = orelse

def _infer_name(self, frame, name):
return name

def block_range(self, lineno: int) -> tuple[int, int]:
"""Get a range from the given line number to where this node ends.

:param lineno: The line number to start the range at.

:returns: The range of line numbers that this node belongs to,
starting at the given line number.
"""
last = None
for exhandler in self.handlers:
if exhandler.type and lineno == exhandler.type.fromlineno:
return lineno, lineno
if exhandler.body[0].fromlineno <= lineno <= exhandler.body[-1].tolineno:
return lineno, exhandler.body[-1].tolineno
if last is None:
last = exhandler.body[0].fromlineno - 1
return self._elsed_block_range(lineno, self.orelse, last)

def get_children(self):
yield from self.body

yield from self.handlers or ()
yield from self.orelse or ()


class TryFinally(_base_nodes.MultiLineWithElseBlockNode, _base_nodes.Statement):
"""Class representing an :class:`ast.TryFinally` node.

>>> import astroid
>>> node = astroid.extract_node('''
try:
do_something()
except Exception as error:
print("Error!")
finally:
print("Cleanup!")
''')
>>> node
<TryFinally l.2 at 0x7f23b2e41d68>
"""

_astroid_fields = ("body", "finalbody")
_multi_line_block_fields = ("body", "finalbody")
_astroid_fields = ("body", "handlers", "orelse", "finalbody")
_multi_line_block_fields = ("body", "handlers", "orelse", "finalbody")

def __init__(
self,
lineno: int | None = None,
col_offset: int | None = None,
parent: NodeNG | None = None,
*,
end_lineno: int | None = None,
end_col_offset: int | None = None,
lineno: int,
col_offset: int,
end_lineno: int,
end_col_offset: int,
parent: NodeNG,
) -> None:
"""
:param lineno: The line that this node appears on in the source code.
Expand All @@ -3825,8 +3761,14 @@
:param end_col_offset: The end column this node appears on in the
source code. Note: This is after the last symbol.
"""
self.body: list[NodeNG | TryExcept] = []
"""The try-except that the finally is attached to."""
self.body: list[NodeNG] = []
"""The contents of the block to catch exceptions from."""

self.handlers: list[ExceptHandler] = []
"""The exception handlers."""

self.orelse: list[NodeNG] = []
"""The contents of the ``else`` block."""

self.finalbody: list[NodeNG] = []
"""The contents of the ``finally`` block."""
Expand All @@ -3841,40 +3783,58 @@

def postinit(
self,
body: list[NodeNG | TryExcept] | None = None,
finalbody: list[NodeNG] | None = None,
*,
body: list[NodeNG],
handlers: list[ExceptHandler],
orelse: list[NodeNG],
finalbody: list[NodeNG],
) -> None:
"""Do some setup after initialisation.

:param body: The try-except that the finally is attached to.
:param body: The contents of the block to catch exceptions from.

:param handlers: The exception handlers.

:param orelse: The contents of the ``else`` block.

:param finalbody: The contents of the ``finally`` block.
"""
if body is not None:
self.body = body
if finalbody is not None:
self.finalbody = finalbody

def block_range(self, lineno: int) -> tuple[int, int]:
"""Get a range from the given line number to where this node ends.
self.body = body
self.handlers = handlers
self.orelse = orelse
self.finalbody = finalbody

:param lineno: The line number to start the range at.
def _infer_name(self, frame, name):
return name

Check warning on line 3808 in astroid/nodes/node_classes.py

View check run for this annotation

Codecov / codecov/patch

astroid/nodes/node_classes.py#L3808

Added line #L3808 was not covered by tests
Pierre-Sassoulas marked this conversation as resolved.
Show resolved Hide resolved

:returns: The range of line numbers that this node belongs to,
starting at the given line number.
"""
child = self.body[0]
# py2.5 try: except: finally:
if (
isinstance(child, TryExcept)
and child.fromlineno == self.fromlineno
and child.tolineno >= lineno > self.fromlineno
):
return child.block_range(lineno)
return self._elsed_block_range(lineno, self.finalbody)
def block_range(self, lineno: int) -> tuple[int, int]:
"""Get a range from a given line number to where this node ends."""
if lineno == self.fromlineno:
return lineno, lineno
if self.body and self.body[0].fromlineno <= lineno <= self.body[-1].tolineno:
# Inside try body - return from lineno till end of try body
return lineno, self.body[-1].tolineno
for exhandler in self.handlers:
if exhandler.type and lineno == exhandler.type.fromlineno:
return lineno, lineno
if exhandler.body[0].fromlineno <= lineno <= exhandler.body[-1].tolineno:
return lineno, exhandler.body[-1].tolineno
if self.orelse:
if self.orelse[0].fromlineno - 1 == lineno:
return lineno, lineno
if self.orelse[0].fromlineno <= lineno <= self.orelse[-1].tolineno:
return lineno, self.orelse[-1].tolineno
if self.finalbody:
if self.finalbody[0].fromlineno - 1 == lineno:
return lineno, lineno
if self.finalbody[0].fromlineno <= lineno <= self.finalbody[-1].tolineno:
return lineno, self.finalbody[-1].tolineno
return lineno, self.tolineno

def get_children(self):
yield from self.body
yield from self.handlers
yield from self.orelse
yield from self.finalbody


Expand Down
2 changes: 1 addition & 1 deletion astroid/nodes/node_ng.py
Original file line number Diff line number Diff line change
Expand Up @@ -573,7 +573,7 @@ def _get_yield_nodes_skip_lambdas(self):
yield from ()

def _infer_name(self, frame, name):
# overridden for ImportFrom, Import, Global, TryExcept, TryStar and Arguments
# overridden for ImportFrom, Import, Global, Try, TryStar and Arguments
pass

def _infer(
Expand Down
2 changes: 1 addition & 1 deletion astroid/nodes/scoped_nodes/scoped_nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -1531,7 +1531,7 @@ def _infer(
# We also don't want to pass parent if the definition is within a Try node
if isinstance(
self.parent,
(node_classes.TryExcept, node_classes.TryFinally, node_classes.If),
(node_classes.Try, node_classes.If),
):
property_already_in_parent_locals = True

Expand Down
Loading
Loading