@@ -70,6 +70,253 @@ def __call__(self):
7070 # Make sure x was called.
7171 self .assertTrue (x .called )
7272
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+
73320 def test_docstring (self ):
74321 def f ():
75322 f'''Not a docstring'''
@@ -786,5 +1033,6 @@ def test_backslash_char(self):
7861033 self .assertEqual (eval ('f"\\ \n "' ), '' )
7871034 self .assertEqual (eval ('f"\\ \r "' ), '' )
7881035
1036+
7891037if __name__ == '__main__' :
7901038 unittest .main ()
0 commit comments