Skip to content

Commit

Permalink
Add Try node (pylint-dev#1867)
Browse files Browse the repository at this point in the history
Co-authored-by: Jacob Walls <jacobtylerwalls@gmail.com>
  • Loading branch information
cdce8p and jacobtylerwalls committed Jul 8, 2023
1 parent a7ab088 commit e91a3b5
Show file tree
Hide file tree
Showing 12 changed files with 135 additions and 187 deletions.
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 @@ def are_exclusive(stmt1, stmt2, exceptions: list[str] | None = None) -> bool:
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 @@ def are_exclusive(stmt1, stmt2, exceptions: list[str] | None = None) -> bool:
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 @@ def infer_lhs(self, context: InferenceContext | None = None, **kwargs: Any):
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 @@ def __init__(
: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 __init__(

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

: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

0 comments on commit e91a3b5

Please sign in to comment.