Skip to content

Commit d44083e

Browse files
DanielNoordcdce8p
andauthored
Fix col_offset attribute for nodes involving with on PyPy (#1520)
Co-authored-by: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
1 parent d38d7e9 commit d44083e

File tree

3 files changed

+56
-12
lines changed

3 files changed

+56
-12
lines changed

ChangeLog

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ What's New in astroid 2.11.4?
1313
=============================
1414
Release date: TBA
1515

16+
* Fix ``col_offset`` attribute for nodes involving ``with`` on ``PyPy``.
1617

1718

1819
What's New in astroid 2.11.3?

astroid/rebuilder.py

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@
66
order to get a single Astroid representation
77
"""
88

9+
import ast
910
import sys
1011
import token
1112
import tokenize
1213
from io import StringIO
1314
from tokenize import TokenInfo, generate_tokens
1415
from typing import (
15-
TYPE_CHECKING,
1616
Callable,
1717
Dict,
1818
Generator,
@@ -39,9 +39,6 @@
3939
else:
4040
from typing_extensions import Final
4141

42-
if TYPE_CHECKING:
43-
import ast
44-
4542

4643
REDIRECT: Final[Dict[str, str]] = {
4744
"arguments": "Arguments",
@@ -1185,9 +1182,14 @@ def _visit_for(
11851182
self, cls: Type[T_For], node: Union["ast.For", "ast.AsyncFor"], parent: NodeNG
11861183
) -> T_For:
11871184
"""visit a For node by returning a fresh instance of it"""
1185+
col_offset = node.col_offset
1186+
if IS_PYPY and not PY39_PLUS and isinstance(node, ast.AsyncFor) and self._data:
1187+
# pylint: disable-next=unsubscriptable-object
1188+
col_offset = self._data[node.lineno - 1].index("async")
1189+
11881190
newnode = cls(
11891191
lineno=node.lineno,
1190-
col_offset=node.col_offset,
1192+
col_offset=col_offset,
11911193
# end_lineno and end_col_offset added in 3.8
11921194
end_lineno=getattr(node, "end_lineno", None),
11931195
end_col_offset=getattr(node, "end_col_offset", None),
@@ -1292,6 +1294,10 @@ def _visit_functiondef(
12921294
position=self._get_position_info(node, newnode),
12931295
doc_node=self.visit(doc_ast_node, newnode),
12941296
)
1297+
if IS_PYPY and PY36 and newnode.position:
1298+
# PyPy: col_offset in Python 3.6 doesn't include 'async',
1299+
# use position.col_offset instead.
1300+
newnode.col_offset = newnode.position.col_offset
12951301
self._fix_doc_node_position(newnode)
12961302
self._global_names.pop()
12971303
return newnode
@@ -1903,9 +1909,14 @@ def _visit_with(
19031909
node: Union["ast.With", "ast.AsyncWith"],
19041910
parent: NodeNG,
19051911
) -> T_With:
1912+
col_offset = node.col_offset
1913+
if IS_PYPY and not PY39_PLUS and isinstance(node, ast.AsyncWith) and self._data:
1914+
# pylint: disable-next=unsubscriptable-object
1915+
col_offset = self._data[node.lineno - 1].index("async")
1916+
19061917
newnode = cls(
19071918
lineno=node.lineno,
1908-
col_offset=node.col_offset,
1919+
col_offset=col_offset,
19091920
# end_lineno and end_col_offset added in 3.8
19101921
end_lineno=getattr(node, "end_lineno", None),
19111922
end_col_offset=getattr(node, "end_col_offset", None),

tests/unittest_nodes.py

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1074,20 +1074,52 @@ def test(a): return a
10741074

10751075
class Python35AsyncTest(unittest.TestCase):
10761076
def test_async_await_keywords(self) -> None:
1077-
async_def, async_for, async_with, await_node = builder.extract_node(
1077+
(
1078+
async_def,
1079+
async_for,
1080+
async_with,
1081+
async_for2,
1082+
async_with2,
1083+
await_node,
1084+
) = builder.extract_node(
10781085
"""
10791086
async def func(): #@
10801087
async for i in range(10): #@
10811088
f = __(await i)
10821089
async with test(): #@
10831090
pass
1091+
async for i \
1092+
in range(10): #@
1093+
pass
1094+
async with test(), \
1095+
test2(): #@
1096+
pass
10841097
"""
10851098
)
1086-
self.assertIsInstance(async_def, nodes.AsyncFunctionDef)
1087-
self.assertIsInstance(async_for, nodes.AsyncFor)
1088-
self.assertIsInstance(async_with, nodes.AsyncWith)
1089-
self.assertIsInstance(await_node, nodes.Await)
1090-
self.assertIsInstance(await_node.value, nodes.Name)
1099+
assert isinstance(async_def, nodes.AsyncFunctionDef)
1100+
assert async_def.lineno == 2
1101+
assert async_def.col_offset == 0
1102+
1103+
assert isinstance(async_for, nodes.AsyncFor)
1104+
assert async_for.lineno == 3
1105+
assert async_for.col_offset == 4
1106+
1107+
assert isinstance(async_with, nodes.AsyncWith)
1108+
assert async_with.lineno == 5
1109+
assert async_with.col_offset == 4
1110+
1111+
assert isinstance(async_for2, nodes.AsyncFor)
1112+
assert async_for2.lineno == 7
1113+
assert async_for2.col_offset == 4
1114+
1115+
assert isinstance(async_with2, nodes.AsyncWith)
1116+
assert async_with2.lineno == 9
1117+
assert async_with2.col_offset == 4
1118+
1119+
assert isinstance(await_node, nodes.Await)
1120+
assert isinstance(await_node.value, nodes.Name)
1121+
assert await_node.lineno == 4
1122+
assert await_node.col_offset == 15
10911123

10921124
def _test_await_async_as_string(self, code: str) -> None:
10931125
ast_node = parse(code)

0 commit comments

Comments
 (0)