bpo-30465: Fix lineno and col_offset in fstring AST nodes (GH-1800) (… · python/cpython@aa1afc7 (original) (raw)
`@@ -70,6 +70,253 @@ def call(self):
`
70
70
`# Make sure x was called.
`
71
71
`self.assertTrue(x.called)
`
72
72
``
``
73
`+
def test_ast_line_numbers(self):
`
``
74
`+
expr = """
`
``
75
`+
a = 10
`
``
76
`+
f'{a * x()}'"""
`
``
77
`+
t = ast.parse(expr)
`
``
78
`+
self.assertEqual(type(t), ast.Module)
`
``
79
`+
self.assertEqual(len(t.body), 2)
`
``
80
`` +
check a = 10
``
``
81
`+
self.assertEqual(type(t.body[0]), ast.Assign)
`
``
82
`+
self.assertEqual(t.body[0].lineno, 2)
`
``
83
`` +
check f'...'
``
``
84
`+
self.assertEqual(type(t.body[1]), ast.Expr)
`
``
85
`+
self.assertEqual(type(t.body[1].value), ast.JoinedStr)
`
``
86
`+
self.assertEqual(len(t.body[1].value.values), 1)
`
``
87
`+
self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)
`
``
88
`+
self.assertEqual(t.body[1].lineno, 3)
`
``
89
`+
self.assertEqual(t.body[1].value.lineno, 3)
`
``
90
`+
self.assertEqual(t.body[1].value.values[0].lineno, 3)
`
``
91
`+
check the binop location
`
``
92
`+
binop = t.body[1].value.values[0].value
`
``
93
`+
self.assertEqual(type(binop), ast.BinOp)
`
``
94
`+
self.assertEqual(type(binop.left), ast.Name)
`
``
95
`+
self.assertEqual(type(binop.op), ast.Mult)
`
``
96
`+
self.assertEqual(type(binop.right), ast.Call)
`
``
97
`+
self.assertEqual(binop.lineno, 3)
`
``
98
`+
self.assertEqual(binop.left.lineno, 3)
`
``
99
`+
self.assertEqual(binop.right.lineno, 3)
`
``
100
`+
self.assertEqual(binop.col_offset, 3)
`
``
101
`+
self.assertEqual(binop.left.col_offset, 3)
`
``
102
`+
self.assertEqual(binop.right.col_offset, 7)
`
``
103
+
``
104
`+
def test_ast_line_numbers_multiple_formattedvalues(self):
`
``
105
`+
expr = """
`
``
106
`+
f'no formatted values'
`
``
107
`+
f'eggs {a * x()} spam {b + y()}'"""
`
``
108
`+
t = ast.parse(expr)
`
``
109
`+
self.assertEqual(type(t), ast.Module)
`
``
110
`+
self.assertEqual(len(t.body), 2)
`
``
111
`` +
check f'no formatted value'
``
``
112
`+
self.assertEqual(type(t.body[0]), ast.Expr)
`
``
113
`+
self.assertEqual(type(t.body[0].value), ast.JoinedStr)
`
``
114
`+
self.assertEqual(t.body[0].lineno, 2)
`
``
115
`` +
check f'...'
``
``
116
`+
self.assertEqual(type(t.body[1]), ast.Expr)
`
``
117
`+
self.assertEqual(type(t.body[1].value), ast.JoinedStr)
`
``
118
`+
self.assertEqual(len(t.body[1].value.values), 4)
`
``
119
`+
self.assertEqual(type(t.body[1].value.values[0]), ast.Str)
`
``
120
`+
self.assertEqual(type(t.body[1].value.values[1]), ast.FormattedValue)
`
``
121
`+
self.assertEqual(type(t.body[1].value.values[2]), ast.Str)
`
``
122
`+
self.assertEqual(type(t.body[1].value.values[3]), ast.FormattedValue)
`
``
123
`+
self.assertEqual(t.body[1].lineno, 3)
`
``
124
`+
self.assertEqual(t.body[1].value.lineno, 3)
`
``
125
`+
self.assertEqual(t.body[1].value.values[0].lineno, 3)
`
``
126
`+
self.assertEqual(t.body[1].value.values[1].lineno, 3)
`
``
127
`+
self.assertEqual(t.body[1].value.values[2].lineno, 3)
`
``
128
`+
self.assertEqual(t.body[1].value.values[3].lineno, 3)
`
``
129
`+
check the first binop location
`
``
130
`+
binop1 = t.body[1].value.values[1].value
`
``
131
`+
self.assertEqual(type(binop1), ast.BinOp)
`
``
132
`+
self.assertEqual(type(binop1.left), ast.Name)
`
``
133
`+
self.assertEqual(type(binop1.op), ast.Mult)
`
``
134
`+
self.assertEqual(type(binop1.right), ast.Call)
`
``
135
`+
self.assertEqual(binop1.lineno, 3)
`
``
136
`+
self.assertEqual(binop1.left.lineno, 3)
`
``
137
`+
self.assertEqual(binop1.right.lineno, 3)
`
``
138
`+
self.assertEqual(binop1.col_offset, 8)
`
``
139
`+
self.assertEqual(binop1.left.col_offset, 8)
`
``
140
`+
self.assertEqual(binop1.right.col_offset, 12)
`
``
141
`+
check the second binop location
`
``
142
`+
binop2 = t.body[1].value.values[3].value
`
``
143
`+
self.assertEqual(type(binop2), ast.BinOp)
`
``
144
`+
self.assertEqual(type(binop2.left), ast.Name)
`
``
145
`+
self.assertEqual(type(binop2.op), ast.Add)
`
``
146
`+
self.assertEqual(type(binop2.right), ast.Call)
`
``
147
`+
self.assertEqual(binop2.lineno, 3)
`
``
148
`+
self.assertEqual(binop2.left.lineno, 3)
`
``
149
`+
self.assertEqual(binop2.right.lineno, 3)
`
``
150
`+
self.assertEqual(binop2.col_offset, 23)
`
``
151
`+
self.assertEqual(binop2.left.col_offset, 23)
`
``
152
`+
self.assertEqual(binop2.right.col_offset, 27)
`
``
153
+
``
154
`+
def test_ast_line_numbers_nested(self):
`
``
155
`+
expr = """
`
``
156
`+
a = 10
`
``
157
`+
f'{a * f"-{x()}-"}'"""
`
``
158
`+
t = ast.parse(expr)
`
``
159
`+
self.assertEqual(type(t), ast.Module)
`
``
160
`+
self.assertEqual(len(t.body), 2)
`
``
161
`` +
check a = 10
``
``
162
`+
self.assertEqual(type(t.body[0]), ast.Assign)
`
``
163
`+
self.assertEqual(t.body[0].lineno, 2)
`
``
164
`` +
check f'...'
``
``
165
`+
self.assertEqual(type(t.body[1]), ast.Expr)
`
``
166
`+
self.assertEqual(type(t.body[1].value), ast.JoinedStr)
`
``
167
`+
self.assertEqual(len(t.body[1].value.values), 1)
`
``
168
`+
self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)
`
``
169
`+
self.assertEqual(t.body[1].lineno, 3)
`
``
170
`+
self.assertEqual(t.body[1].value.lineno, 3)
`
``
171
`+
self.assertEqual(t.body[1].value.values[0].lineno, 3)
`
``
172
`+
check the binop location
`
``
173
`+
binop = t.body[1].value.values[0].value
`
``
174
`+
self.assertEqual(type(binop), ast.BinOp)
`
``
175
`+
self.assertEqual(type(binop.left), ast.Name)
`
``
176
`+
self.assertEqual(type(binop.op), ast.Mult)
`
``
177
`+
self.assertEqual(type(binop.right), ast.JoinedStr)
`
``
178
`+
self.assertEqual(binop.lineno, 3)
`
``
179
`+
self.assertEqual(binop.left.lineno, 3)
`
``
180
`+
self.assertEqual(binop.right.lineno, 3)
`
``
181
`+
self.assertEqual(binop.col_offset, 3)
`
``
182
`+
self.assertEqual(binop.left.col_offset, 3)
`
``
183
`+
self.assertEqual(binop.right.col_offset, 7)
`
``
184
`+
check the nested call location
`
``
185
`+
self.assertEqual(len(binop.right.values), 3)
`
``
186
`+
self.assertEqual(type(binop.right.values[0]), ast.Str)
`
``
187
`+
self.assertEqual(type(binop.right.values[1]), ast.FormattedValue)
`
``
188
`+
self.assertEqual(type(binop.right.values[2]), ast.Str)
`
``
189
`+
self.assertEqual(binop.right.values[0].lineno, 3)
`
``
190
`+
self.assertEqual(binop.right.values[1].lineno, 3)
`
``
191
`+
self.assertEqual(binop.right.values[2].lineno, 3)
`
``
192
`+
call = binop.right.values[1].value
`
``
193
`+
self.assertEqual(type(call), ast.Call)
`
``
194
`+
self.assertEqual(call.lineno, 3)
`
``
195
`+
self.assertEqual(call.col_offset, 11)
`
``
196
+
``
197
`+
def test_ast_line_numbers_duplicate_expression(self):
`
``
198
`+
"""Duplicate expression
`
``
199
+
``
200
`+
NOTE: this is currently broken, always sets location of the first
`
``
201
`+
expression.
`
``
202
`+
"""
`
``
203
`+
expr = """
`
``
204
`+
a = 10
`
``
205
`+
f'{a * x()} {a * x()} {a * x()}'
`
``
206
`+
"""
`
``
207
`+
t = ast.parse(expr)
`
``
208
`+
self.assertEqual(type(t), ast.Module)
`
``
209
`+
self.assertEqual(len(t.body), 2)
`
``
210
`` +
check a = 10
``
``
211
`+
self.assertEqual(type(t.body[0]), ast.Assign)
`
``
212
`+
self.assertEqual(t.body[0].lineno, 2)
`
``
213
`` +
check f'...'
``
``
214
`+
self.assertEqual(type(t.body[1]), ast.Expr)
`
``
215
`+
self.assertEqual(type(t.body[1].value), ast.JoinedStr)
`
``
216
`+
self.assertEqual(len(t.body[1].value.values), 5)
`
``
217
`+
self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)
`
``
218
`+
self.assertEqual(type(t.body[1].value.values[1]), ast.Str)
`
``
219
`+
self.assertEqual(type(t.body[1].value.values[2]), ast.FormattedValue)
`
``
220
`+
self.assertEqual(type(t.body[1].value.values[3]), ast.Str)
`
``
221
`+
self.assertEqual(type(t.body[1].value.values[4]), ast.FormattedValue)
`
``
222
`+
self.assertEqual(t.body[1].lineno, 3)
`
``
223
`+
self.assertEqual(t.body[1].value.lineno, 3)
`
``
224
`+
self.assertEqual(t.body[1].value.values[0].lineno, 3)
`
``
225
`+
self.assertEqual(t.body[1].value.values[1].lineno, 3)
`
``
226
`+
self.assertEqual(t.body[1].value.values[2].lineno, 3)
`
``
227
`+
self.assertEqual(t.body[1].value.values[3].lineno, 3)
`
``
228
`+
self.assertEqual(t.body[1].value.values[4].lineno, 3)
`
``
229
`+
check the first binop location
`
``
230
`+
binop = t.body[1].value.values[0].value
`
``
231
`+
self.assertEqual(type(binop), ast.BinOp)
`
``
232
`+
self.assertEqual(type(binop.left), ast.Name)
`
``
233
`+
self.assertEqual(type(binop.op), ast.Mult)
`
``
234
`+
self.assertEqual(type(binop.right), ast.Call)
`
``
235
`+
self.assertEqual(binop.lineno, 3)
`
``
236
`+
self.assertEqual(binop.left.lineno, 3)
`
``
237
`+
self.assertEqual(binop.right.lineno, 3)
`
``
238
`+
self.assertEqual(binop.col_offset, 3)
`
``
239
`+
self.assertEqual(binop.left.col_offset, 3)
`
``
240
`+
self.assertEqual(binop.right.col_offset, 7)
`
``
241
`+
check the second binop location
`
``
242
`+
binop = t.body[1].value.values[2].value
`
``
243
`+
self.assertEqual(type(binop), ast.BinOp)
`
``
244
`+
self.assertEqual(type(binop.left), ast.Name)
`
``
245
`+
self.assertEqual(type(binop.op), ast.Mult)
`
``
246
`+
self.assertEqual(type(binop.right), ast.Call)
`
``
247
`+
self.assertEqual(binop.lineno, 3)
`
``
248
`+
self.assertEqual(binop.left.lineno, 3)
`
``
249
`+
self.assertEqual(binop.right.lineno, 3)
`
``
250
`+
self.assertEqual(binop.col_offset, 3) # FIXME: this is wrong
`
``
251
`+
self.assertEqual(binop.left.col_offset, 3) # FIXME: this is wrong
`
``
252
`+
self.assertEqual(binop.right.col_offset, 7) # FIXME: this is wrong
`
``
253
`+
check the third binop location
`
``
254
`+
binop = t.body[1].value.values[4].value
`
``
255
`+
self.assertEqual(type(binop), ast.BinOp)
`
``
256
`+
self.assertEqual(type(binop.left), ast.Name)
`
``
257
`+
self.assertEqual(type(binop.op), ast.Mult)
`
``
258
`+
self.assertEqual(type(binop.right), ast.Call)
`
``
259
`+
self.assertEqual(binop.lineno, 3)
`
``
260
`+
self.assertEqual(binop.left.lineno, 3)
`
``
261
`+
self.assertEqual(binop.right.lineno, 3)
`
``
262
`+
self.assertEqual(binop.col_offset, 3) # FIXME: this is wrong
`
``
263
`+
self.assertEqual(binop.left.col_offset, 3) # FIXME: this is wrong
`
``
264
`+
self.assertEqual(binop.right.col_offset, 7) # FIXME: this is wrong
`
``
265
+
``
266
`+
def test_ast_line_numbers_multiline_fstring(self):
`
``
267
`+
FIXME: This test demonstrates invalid behavior due to JoinedStr's
`
``
268
`+
immediate child nodes containing the wrong lineno. The enclosed
`
``
269
`+
expressions have valid line information and column offsets.
`
``
270
`+
See bpo-16806 and bpo-30465 for details.
`
``
271
`+
expr = """
`
``
272
`+
a = 10
`
``
273
`+
f'''
`
``
274
`+
{a
`
``
275
`+
`
``
276
`+
x()}
`
``
277
`+
non-important content
`
``
278
`+
'''
`
``
279
`+
"""
`
``
280
`+
t = ast.parse(expr)
`
``
281
`+
self.assertEqual(type(t), ast.Module)
`
``
282
`+
self.assertEqual(len(t.body), 2)
`
``
283
`` +
check a = 10
``
``
284
`+
self.assertEqual(type(t.body[0]), ast.Assign)
`
``
285
`+
self.assertEqual(t.body[0].lineno, 2)
`
``
286
`` +
check f'...'
``
``
287
`+
self.assertEqual(type(t.body[1]), ast.Expr)
`
``
288
`+
self.assertEqual(type(t.body[1].value), ast.JoinedStr)
`
``
289
`+
self.assertEqual(len(t.body[1].value.values), 3)
`
``
290
`+
self.assertEqual(type(t.body[1].value.values[0]), ast.Str)
`
``
291
`+
self.assertEqual(type(t.body[1].value.values[1]), ast.FormattedValue)
`
``
292
`+
self.assertEqual(type(t.body[1].value.values[2]), ast.Str)
`
``
293
`+
NOTE: the following invalid behavior is described in bpo-16806.
`
``
294
`+
- line number should be the first line (3), not the last (8)
`
``
295
`+
- column offset should not be -1
`
``
296
`+
self.assertEqual(t.body[1].lineno, 8)
`
``
297
`+
self.assertEqual(t.body[1].value.lineno, 8)
`
``
298
`+
self.assertEqual(t.body[1].value.values[0].lineno, 8)
`
``
299
`+
self.assertEqual(t.body[1].value.values[1].lineno, 8)
`
``
300
`+
self.assertEqual(t.body[1].value.values[2].lineno, 8)
`
``
301
`+
self.assertEqual(t.body[1].col_offset, -1)
`
``
302
`+
self.assertEqual(t.body[1].value.col_offset, -1)
`
``
303
`+
self.assertEqual(t.body[1].value.values[0].col_offset, -1)
`
``
304
`+
self.assertEqual(t.body[1].value.values[1].col_offset, -1)
`
``
305
`+
self.assertEqual(t.body[1].value.values[2].col_offset, -1)
`
``
306
`+
NOTE: the following lineno information and col_offset is correct for
`
``
307
`+
expressions within FormattedValues.
`
``
308
`+
binop = t.body[1].value.values[1].value
`
``
309
`+
self.assertEqual(type(binop), ast.BinOp)
`
``
310
`+
self.assertEqual(type(binop.left), ast.Name)
`
``
311
`+
self.assertEqual(type(binop.op), ast.Mult)
`
``
312
`+
self.assertEqual(type(binop.right), ast.Call)
`
``
313
`+
self.assertEqual(binop.lineno, 4)
`
``
314
`+
self.assertEqual(binop.left.lineno, 4)
`
``
315
`+
self.assertEqual(binop.right.lineno, 6)
`
``
316
`+
self.assertEqual(binop.col_offset, 3)
`
``
317
`+
self.assertEqual(binop.left.col_offset, 3)
`
``
318
`+
self.assertEqual(binop.right.col_offset, 7)
`
``
319
+
73
320
`def test_docstring(self):
`
74
321
`def f():
`
75
322
`f'''Not a docstring'''
`
`@@ -786,5 +1033,6 @@ def test_backslash_char(self):
`
786
1033
`self.assertEqual(eval('f"\\n"'), '')
`
787
1034
`self.assertEqual(eval('f"\\r"'), '')
`
788
1035
``
``
1036
+
789
1037
`if name == 'main':
`
790
1038
`unittest.main()
`