Skip to content

Commit 4ec9ed8

Browse files
authored
pythongh-98461: Fix source location in comprehensions bytecode (pythonGH-98464)
1 parent c60b3b3 commit 4ec9ed8

File tree

3 files changed

+234
-60
lines changed

3 files changed

+234
-60
lines changed

Lib/test/test_compile.py

+159
Original file line numberDiff line numberDiff line change
@@ -1249,6 +1249,165 @@ def test_multiline_assert(self):
12491249
self.assertOpcodeSourcePositionIs(compiled_code, 'RAISE_VARARGS',
12501250
line=1, end_line=3, column=0, end_column=30, occurrence=1)
12511251

1252+
def test_multiline_generator_expression(self):
1253+
snippet = """\
1254+
((x,
1255+
2*x)
1256+
for x
1257+
in [1,2,3] if (x > 0
1258+
and x < 100
1259+
and x != 50))
1260+
"""
1261+
compiled_code, _ = self.check_positions_against_ast(snippet)
1262+
compiled_code = compiled_code.co_consts[0]
1263+
self.assertIsInstance(compiled_code, types.CodeType)
1264+
self.assertOpcodeSourcePositionIs(compiled_code, 'YIELD_VALUE',
1265+
line=1, end_line=2, column=1, end_column=8, occurrence=1)
1266+
self.assertOpcodeSourcePositionIs(compiled_code, 'JUMP_BACKWARD',
1267+
line=1, end_line=2, column=1, end_column=8, occurrence=1)
1268+
self.assertOpcodeSourcePositionIs(compiled_code, 'RETURN_VALUE',
1269+
line=1, end_line=6, column=0, end_column=32, occurrence=1)
1270+
1271+
def test_multiline_async_generator_expression(self):
1272+
snippet = """\
1273+
((x,
1274+
2*x)
1275+
async for x
1276+
in [1,2,3] if (x > 0
1277+
and x < 100
1278+
and x != 50))
1279+
"""
1280+
compiled_code, _ = self.check_positions_against_ast(snippet)
1281+
compiled_code = compiled_code.co_consts[0]
1282+
self.assertIsInstance(compiled_code, types.CodeType)
1283+
self.assertOpcodeSourcePositionIs(compiled_code, 'YIELD_VALUE',
1284+
line=1, end_line=2, column=1, end_column=8, occurrence=2)
1285+
self.assertOpcodeSourcePositionIs(compiled_code, 'RETURN_VALUE',
1286+
line=6, end_line=6, column=23, end_column=30, occurrence=1)
1287+
1288+
def test_multiline_list_comprehension(self):
1289+
snippet = """\
1290+
[(x,
1291+
2*x)
1292+
for x
1293+
in [1,2,3] if (x > 0
1294+
and x < 100
1295+
and x != 50)]
1296+
"""
1297+
compiled_code, _ = self.check_positions_against_ast(snippet)
1298+
compiled_code = compiled_code.co_consts[0]
1299+
self.assertIsInstance(compiled_code, types.CodeType)
1300+
self.assertOpcodeSourcePositionIs(compiled_code, 'LIST_APPEND',
1301+
line=1, end_line=2, column=1, end_column=8, occurrence=1)
1302+
self.assertOpcodeSourcePositionIs(compiled_code, 'JUMP_BACKWARD',
1303+
line=1, end_line=2, column=1, end_column=8, occurrence=1)
1304+
self.assertOpcodeSourcePositionIs(compiled_code, 'RETURN_VALUE',
1305+
line=1, end_line=6, column=0, end_column=32, occurrence=1)
1306+
1307+
def test_multiline_async_list_comprehension(self):
1308+
snippet = """\
1309+
async def f():
1310+
[(x,
1311+
2*x)
1312+
async for x
1313+
in [1,2,3] if (x > 0
1314+
and x < 100
1315+
and x != 50)]
1316+
"""
1317+
compiled_code, _ = self.check_positions_against_ast(snippet)
1318+
g = {}
1319+
eval(compiled_code, g)
1320+
compiled_code = g['f'].__code__.co_consts[1]
1321+
self.assertIsInstance(compiled_code, types.CodeType)
1322+
self.assertOpcodeSourcePositionIs(compiled_code, 'LIST_APPEND',
1323+
line=2, end_line=3, column=5, end_column=12, occurrence=1)
1324+
self.assertOpcodeSourcePositionIs(compiled_code, 'JUMP_BACKWARD',
1325+
line=2, end_line=3, column=5, end_column=12, occurrence=1)
1326+
self.assertOpcodeSourcePositionIs(compiled_code, 'RETURN_VALUE',
1327+
line=2, end_line=7, column=4, end_column=36, occurrence=1)
1328+
1329+
def test_multiline_set_comprehension(self):
1330+
snippet = """\
1331+
{(x,
1332+
2*x)
1333+
for x
1334+
in [1,2,3] if (x > 0
1335+
and x < 100
1336+
and x != 50)}
1337+
"""
1338+
compiled_code, _ = self.check_positions_against_ast(snippet)
1339+
compiled_code = compiled_code.co_consts[0]
1340+
self.assertIsInstance(compiled_code, types.CodeType)
1341+
self.assertOpcodeSourcePositionIs(compiled_code, 'SET_ADD',
1342+
line=1, end_line=2, column=1, end_column=8, occurrence=1)
1343+
self.assertOpcodeSourcePositionIs(compiled_code, 'JUMP_BACKWARD',
1344+
line=1, end_line=2, column=1, end_column=8, occurrence=1)
1345+
self.assertOpcodeSourcePositionIs(compiled_code, 'RETURN_VALUE',
1346+
line=1, end_line=6, column=0, end_column=32, occurrence=1)
1347+
1348+
def test_multiline_async_set_comprehension(self):
1349+
snippet = """\
1350+
async def f():
1351+
{(x,
1352+
2*x)
1353+
async for x
1354+
in [1,2,3] if (x > 0
1355+
and x < 100
1356+
and x != 50)}
1357+
"""
1358+
compiled_code, _ = self.check_positions_against_ast(snippet)
1359+
g = {}
1360+
eval(compiled_code, g)
1361+
compiled_code = g['f'].__code__.co_consts[1]
1362+
self.assertIsInstance(compiled_code, types.CodeType)
1363+
self.assertOpcodeSourcePositionIs(compiled_code, 'SET_ADD',
1364+
line=2, end_line=3, column=5, end_column=12, occurrence=1)
1365+
self.assertOpcodeSourcePositionIs(compiled_code, 'JUMP_BACKWARD',
1366+
line=2, end_line=3, column=5, end_column=12, occurrence=1)
1367+
self.assertOpcodeSourcePositionIs(compiled_code, 'RETURN_VALUE',
1368+
line=2, end_line=7, column=4, end_column=36, occurrence=1)
1369+
1370+
def test_multiline_dict_comprehension(self):
1371+
snippet = """\
1372+
{x:
1373+
2*x
1374+
for x
1375+
in [1,2,3] if (x > 0
1376+
and x < 100
1377+
and x != 50)}
1378+
"""
1379+
compiled_code, _ = self.check_positions_against_ast(snippet)
1380+
compiled_code = compiled_code.co_consts[0]
1381+
self.assertIsInstance(compiled_code, types.CodeType)
1382+
self.assertOpcodeSourcePositionIs(compiled_code, 'MAP_ADD',
1383+
line=1, end_line=2, column=1, end_column=7, occurrence=1)
1384+
self.assertOpcodeSourcePositionIs(compiled_code, 'JUMP_BACKWARD',
1385+
line=1, end_line=2, column=1, end_column=7, occurrence=1)
1386+
self.assertOpcodeSourcePositionIs(compiled_code, 'RETURN_VALUE',
1387+
line=1, end_line=6, column=0, end_column=32, occurrence=1)
1388+
1389+
def test_multiline_async_dict_comprehension(self):
1390+
snippet = """\
1391+
async def f():
1392+
{x:
1393+
2*x
1394+
async for x
1395+
in [1,2,3] if (x > 0
1396+
and x < 100
1397+
and x != 50)}
1398+
"""
1399+
compiled_code, _ = self.check_positions_against_ast(snippet)
1400+
g = {}
1401+
eval(compiled_code, g)
1402+
compiled_code = g['f'].__code__.co_consts[1]
1403+
self.assertIsInstance(compiled_code, types.CodeType)
1404+
self.assertOpcodeSourcePositionIs(compiled_code, 'MAP_ADD',
1405+
line=2, end_line=3, column=5, end_column=11, occurrence=1)
1406+
self.assertOpcodeSourcePositionIs(compiled_code, 'JUMP_BACKWARD',
1407+
line=2, end_line=3, column=5, end_column=11, occurrence=1)
1408+
self.assertOpcodeSourcePositionIs(compiled_code, 'RETURN_VALUE',
1409+
line=2, end_line=7, column=4, end_column=36, occurrence=1)
1410+
12521411
def test_very_long_line_end_offset(self):
12531412
# Make sure we get the correct column offset for offsets
12541413
# too large to store in a byte.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix source location in bytecode for list, set and dict comprehensions as well as generator expressions.

0 commit comments

Comments
 (0)