Skip to content

Commit 1da8c9b

Browse files
committed
Add Try node
1 parent e596315 commit 1da8c9b

File tree

6 files changed

+200
-24
lines changed

6 files changed

+200
-24
lines changed

astroid/node_classes.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
Slice,
7171
Starred,
7272
Subscript,
73+
Try,
7374
TryExcept,
7475
TryFinally,
7576
Tuple,

astroid/nodes/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@
9292
Starred,
9393
Statement,
9494
Subscript,
95+
Try,
9596
TryExcept,
9697
TryFinally,
9798
Tuple,
@@ -204,6 +205,7 @@
204205
Slice,
205206
Starred,
206207
Subscript,
208+
Try,
207209
TryExcept,
208210
TryFinally,
209211
Tuple,

astroid/nodes/node_classes.py

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3953,6 +3953,129 @@ def get_children(self):
39533953
yield self.slice
39543954

39553955

3956+
class Try(mixins.MultiLineBlockMixin, mixins.BlockRangeMixIn, Statement):
3957+
"""Class representing a :class:`ast.Try` node.
3958+
3959+
Replaces the `TryExcept` and `TryFinally` nodes.
3960+
Requires ``tree_rev >= 2``.
3961+
3962+
>>> import astroid
3963+
>>> node = astroid.extract_node('''
3964+
try:
3965+
do_something()
3966+
except Exception as error:
3967+
print("Error!")
3968+
''')
3969+
>>> node
3970+
<Try l.2 at 0x7f23b2e41d68>
3971+
"""
3972+
3973+
_astroid_fields = ("body", "handlers", "orelse", "finalbody")
3974+
_multi_line_block_fields = ("body", "handlers", "orelse", "finalbody")
3975+
3976+
def __init__(
3977+
self,
3978+
*,
3979+
lineno: Optional[int] = None,
3980+
col_offset: Optional[int] = None,
3981+
end_lineno: Optional[int] = None,
3982+
end_col_offset: Optional[int] = None,
3983+
parent: Optional[NodeNG] = None,
3984+
) -> None:
3985+
"""
3986+
:param lineno: The line that this node appears on in the source code.
3987+
3988+
:param col_offset: The column that this node appears on in the
3989+
source code.
3990+
3991+
:param parent: The parent node in the syntax tree.
3992+
3993+
:param end_lineno: The last line this node appears on in the source code.
3994+
3995+
:param end_col_offset: The end column this node appears on in the
3996+
source code. Note: This is after the last symbol.
3997+
"""
3998+
self.body: typing.List[NodeNG] = []
3999+
"""The contents of the block to catch exceptions from."""
4000+
4001+
self.handlers: typing.List[ExceptHandler] = []
4002+
"""The exception handlers."""
4003+
4004+
self.orelse: typing.List[NodeNG] = []
4005+
"""The contents of the ``else`` block."""
4006+
4007+
self.finalbody: typing.List[NodeNG] = []
4008+
"""The contents of the ``finally`` block."""
4009+
4010+
super().__init__(
4011+
lineno=lineno,
4012+
col_offset=col_offset,
4013+
end_lineno=end_lineno,
4014+
end_col_offset=end_col_offset,
4015+
parent=parent,
4016+
)
4017+
4018+
def postinit(
4019+
self,
4020+
*,
4021+
body: Optional[typing.List[NodeNG]] = None,
4022+
handlers: Optional[typing.List[ExceptHandler]] = None,
4023+
orelse: Optional[typing.List[NodeNG]] = None,
4024+
finalbody: Optional[typing.List[NodeNG]] = None,
4025+
) -> None:
4026+
"""Do some setup after initialisation.
4027+
4028+
:param body: The contents of the block to catch exceptions from.
4029+
4030+
:param handlers: The exception handlers.
4031+
4032+
:param orelse: The contents of the ``else`` block.
4033+
4034+
:param finalbody: The contents of the ``finally`` block.
4035+
"""
4036+
if body:
4037+
self.body = body
4038+
if handlers:
4039+
self.handlers = handlers
4040+
if orelse:
4041+
self.orelse = orelse
4042+
if finalbody:
4043+
self.finalbody = finalbody
4044+
4045+
def _infer_name(self, frame, name):
4046+
return name
4047+
4048+
def block_range(self, lineno: int) -> typing.Tuple[int, int]:
4049+
"""Get a range from a given line number to where this node ends."""
4050+
if lineno == self.fromlineno:
4051+
return lineno, lineno
4052+
if self.body and self.body[0].fromlineno <= lineno <= self.body[-1].tolineno:
4053+
# Inside try body - return from lineno till end of try body
4054+
return lineno, self.body[-1].tolineno
4055+
for exhandler in self.handlers:
4056+
if exhandler.type and lineno == exhandler.type.fromlineno:
4057+
return lineno, lineno
4058+
if exhandler.body[0].fromlineno <= lineno <= exhandler.body[-1].tolineno:
4059+
return lineno, exhandler.body[-1].tolineno
4060+
if self.orelse:
4061+
if self.orelse[0].fromlineno - 1 == lineno:
4062+
return lineno, lineno
4063+
if self.orelse[0].fromlineno <= lineno <= self.orelse[-1].tolineno:
4064+
return lineno, self.orelse[-1].tolineno
4065+
if self.finalbody:
4066+
if self.finalbody[0].fromlineno - 1 == lineno:
4067+
return lineno, lineno
4068+
if self.finalbody[0].fromlineno <= lineno <= self.finalbody[-1].tolineno:
4069+
return lineno, self.finalbody[-1].tolineno
4070+
return lineno, self.tolineno
4071+
4072+
def get_children(self):
4073+
yield from self.body
4074+
yield from self.handlers
4075+
yield from self.orelse
4076+
yield from self.finalbody
4077+
4078+
39564079
class TryExcept(mixins.MultiLineBlockMixin, mixins.BlockRangeMixIn, Statement):
39574080
"""Class representing an :class:`ast.TryExcept` node.
39584081

astroid/nodes/node_ng.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -587,7 +587,7 @@ def _get_yield_nodes_skip_lambdas(self):
587587
yield from ()
588588

589589
def _infer_name(self, frame, name):
590-
# overridden for ImportFrom, Import, Global, TryExcept and Arguments
590+
# overridden for ImportFrom, Import, Global, Try, TryExcept and Arguments
591591
pass
592592

593593
def _infer(self, context=None):

astroid/rebuilder.py

Lines changed: 40 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -547,7 +547,7 @@ def visit(self, node: "ast.Starred", parent: NodeNG) -> nodes.Starred:
547547
@overload
548548
def visit(
549549
self, node: "ast.Try", parent: NodeNG
550-
) -> Union[nodes.TryExcept, nodes.TryFinally]:
550+
) -> Union[nodes.Try, nodes.TryExcept, nodes.TryFinally]:
551551
...
552552

553553
@overload
@@ -893,7 +893,7 @@ def visit(self, node: "ast.Starred", parent: NodeNG) -> nodes.Starred:
893893
@overload
894894
def visit(
895895
self, node: "ast.Try", parent: NodeNG
896-
) -> Union[nodes.TryExcept, nodes.TryFinally]:
896+
) -> Union[nodes.Try, nodes.TryExcept, nodes.TryFinally]:
897897
...
898898

899899
@overload
@@ -2134,28 +2134,45 @@ def visit_tryexcept(self, node: "ast.Try", parent: NodeNG) -> nodes.TryExcept:
21342134

21352135
def visit_try(
21362136
self, node: "ast.Try", parent: NodeNG
2137-
) -> Union[nodes.TryExcept, nodes.TryFinally, None]:
2138-
# python 3.3 introduce a new Try node replacing
2139-
# TryFinally/TryExcept nodes
2140-
if node.finalbody:
2141-
newnode = nodes.TryFinally(
2142-
lineno=node.lineno,
2143-
col_offset=node.col_offset,
2144-
# end_lineno and end_col_offset added in 3.8
2145-
end_lineno=getattr(node, "end_lineno", None),
2146-
end_col_offset=getattr(node, "end_col_offset", None),
2147-
parent=parent,
2148-
)
2149-
body: List[Union[NodeNG, nodes.TryExcept]]
2137+
) -> Union[nodes.Try, nodes.TryExcept, nodes.TryFinally, None]:
2138+
newnode: Union[nodes.Try, nodes.TryExcept, nodes.TryFinally]
2139+
if self._tree_rev == 1:
2140+
# python 3.3 introduce a new Try node replacing
2141+
# TryFinally/TryExcept nodes
2142+
if node.finalbody:
2143+
newnode = nodes.TryFinally(
2144+
lineno=node.lineno,
2145+
col_offset=node.col_offset,
2146+
# end_lineno and end_col_offset added in 3.8
2147+
end_lineno=getattr(node, "end_lineno", None),
2148+
end_col_offset=getattr(node, "end_col_offset", None),
2149+
parent=parent,
2150+
)
2151+
body: List[Union[NodeNG, nodes.TryExcept]]
2152+
if node.handlers:
2153+
body = [self.visit_tryexcept(node, newnode)]
2154+
else:
2155+
body = [self.visit(child, newnode) for child in node.body]
2156+
newnode.postinit(body, [self.visit(n, newnode) for n in node.finalbody])
2157+
return newnode
21502158
if node.handlers:
2151-
body = [self.visit_tryexcept(node, newnode)]
2152-
else:
2153-
body = [self.visit(child, newnode) for child in node.body]
2154-
newnode.postinit(body, [self.visit(n, newnode) for n in node.finalbody])
2155-
return newnode
2156-
if node.handlers:
2157-
return self.visit_tryexcept(node, parent)
2158-
return None
2159+
return self.visit_tryexcept(node, parent)
2160+
return None
2161+
2162+
newnode = nodes.Try(
2163+
lineno=node.lineno,
2164+
col_offset=node.col_offset,
2165+
end_lineno=getattr(node, "end_lineno", None),
2166+
end_col_offset=getattr(node, "end_col_offset", None),
2167+
parent=parent,
2168+
)
2169+
newnode.postinit(
2170+
body=[self.visit(child, newnode) for child in node.body],
2171+
handlers=[self.visit(child, newnode) for child in node.handlers],
2172+
orelse=[self.visit(child, newnode) for child in node.orelse],
2173+
finalbody=[self.visit(child, newnode) for child in node.finalbody],
2174+
)
2175+
return newnode
21592176

21602177
def visit_tuple(self, node: "ast.Tuple", parent: NodeNG) -> nodes.Tuple:
21612178
"""visit a Tuple node by returning a fresh instance of it"""

tests/unittest_nodes.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
ParentMissingError,
6262
StatementMissing,
6363
)
64+
from astroid.manager import AstroidManager
6465
from astroid.nodes.node_classes import (
6566
AssignAttr,
6667
AssignName,
@@ -438,6 +439,38 @@ def test_if_typing_guard() -> None:
438439
assert code[3].is_typing_guard() is False
439440

440441

442+
class TryNodeTest(_NodeTest):
443+
CODE = """
444+
try: # L2
445+
print("Hello")
446+
except IOError:
447+
pass
448+
except UnicodeError:
449+
pass
450+
else:
451+
print()
452+
finally:
453+
print()
454+
"""
455+
456+
def test_block_range(self) -> None:
457+
AstroidManager.tree_rev = 2
458+
try:
459+
assert self.astroid.body[0].block_range(1) == (1, 11)
460+
assert self.astroid.body[0].block_range(2) == (2, 2)
461+
assert self.astroid.body[0].block_range(3) == (3, 3)
462+
assert self.astroid.body[0].block_range(4) == (4, 4)
463+
assert self.astroid.body[0].block_range(5) == (5, 5)
464+
assert self.astroid.body[0].block_range(6) == (6, 6)
465+
assert self.astroid.body[0].block_range(7) == (7, 7)
466+
assert self.astroid.body[0].block_range(8) == (8, 8)
467+
assert self.astroid.body[0].block_range(9) == (9, 9)
468+
assert self.astroid.body[0].block_range(10) == (10, 10)
469+
assert self.astroid.body[0].block_range(11) == (11, 11)
470+
finally:
471+
AstroidManager.tree_rev = 1
472+
473+
441474
class TryExceptNodeTest(_NodeTest):
442475
CODE = """
443476
try:

0 commit comments

Comments
 (0)