1 # -*- coding: utf-8 -*-
2 # There are tests here with unicode string literals and
3 # identifiers. There's a code in ast.c that was added because of a
4 # failure with a non-ascii-only expression. So, I have tests for
5 # that. There are workarounds that would let me run tests for that
6 # code without unicode identifiers and strings, but just using them
7 # directly seems like the easiest and therefore safest thing to do.
8 # Unicode identifiers in tests is allowed by PEP 3131.
9
10 import ast
11 import os
12 import re
13 import types
14 import decimal
15 import unittest
16 import warnings
17 from test import support
18 from test.support.os_helper import temp_cwd
19 from test.support.script_helper import assert_python_failure, assert_python_ok
20
21 a_global = 'global variable'
22
23 # You could argue that I'm too strict in looking for specific error
24 # values with assertRaisesRegex, but without it it's way too easy to
25 # make a syntax error in the test strings. Especially with all of the
26 # triple quotes, raw strings, backslashes, etc. I think it's a
27 # worthwhile tradeoff. When I switched to this method, I found many
28 # examples where I wasn't testing what I thought I was.
29
30 class ESC[4;38;5;81mTestCase(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
31 def assertAllRaise(self, exception_type, regex, error_strings):
32 for str in error_strings:
33 with self.subTest(str=str):
34 with self.assertRaisesRegex(exception_type, regex):
35 eval(str)
36
37 def test__format__lookup(self):
38 # Make sure __format__ is looked up on the type, not the instance.
39 class ESC[4;38;5;81mX:
40 def __format__(self, spec):
41 return 'class'
42
43 x = X()
44
45 # Add a bound __format__ method to the 'y' instance, but not
46 # the 'x' instance.
47 y = X()
48 y.__format__ = types.MethodType(lambda self, spec: 'instance', y)
49
50 self.assertEqual(f'{y}', format(y))
51 self.assertEqual(f'{y}', 'class')
52 self.assertEqual(format(x), format(y))
53
54 # __format__ is not called this way, but still make sure it
55 # returns what we expect (so we can make sure we're bypassing
56 # it).
57 self.assertEqual(x.__format__(''), 'class')
58 self.assertEqual(y.__format__(''), 'instance')
59
60 # This is how __format__ is actually called.
61 self.assertEqual(type(x).__format__(x, ''), 'class')
62 self.assertEqual(type(y).__format__(y, ''), 'class')
63
64 def test_ast(self):
65 # Inspired by http://bugs.python.org/issue24975
66 class ESC[4;38;5;81mX:
67 def __init__(self):
68 self.called = False
69 def __call__(self):
70 self.called = True
71 return 4
72 x = X()
73 expr = """
74 a = 10
75 f'{a * x()}'"""
76 t = ast.parse(expr)
77 c = compile(t, '', 'exec')
78
79 # Make sure x was not called.
80 self.assertFalse(x.called)
81
82 # Actually run the code.
83 exec(c)
84
85 # Make sure x was called.
86 self.assertTrue(x.called)
87
88 def test_ast_line_numbers(self):
89 expr = """
90 a = 10
91 f'{a * x()}'"""
92 t = ast.parse(expr)
93 self.assertEqual(type(t), ast.Module)
94 self.assertEqual(len(t.body), 2)
95 # check `a = 10`
96 self.assertEqual(type(t.body[0]), ast.Assign)
97 self.assertEqual(t.body[0].lineno, 2)
98 # check `f'...'`
99 self.assertEqual(type(t.body[1]), ast.Expr)
100 self.assertEqual(type(t.body[1].value), ast.JoinedStr)
101 self.assertEqual(len(t.body[1].value.values), 1)
102 self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)
103 self.assertEqual(t.body[1].lineno, 3)
104 self.assertEqual(t.body[1].value.lineno, 3)
105 self.assertEqual(t.body[1].value.values[0].lineno, 3)
106 # check the binop location
107 binop = t.body[1].value.values[0].value
108 self.assertEqual(type(binop), ast.BinOp)
109 self.assertEqual(type(binop.left), ast.Name)
110 self.assertEqual(type(binop.op), ast.Mult)
111 self.assertEqual(type(binop.right), ast.Call)
112 self.assertEqual(binop.lineno, 3)
113 self.assertEqual(binop.left.lineno, 3)
114 self.assertEqual(binop.right.lineno, 3)
115 self.assertEqual(binop.col_offset, 3)
116 self.assertEqual(binop.left.col_offset, 3)
117 self.assertEqual(binop.right.col_offset, 7)
118
119 def test_ast_line_numbers_multiple_formattedvalues(self):
120 expr = """
121 f'no formatted values'
122 f'eggs {a * x()} spam {b + y()}'"""
123 t = ast.parse(expr)
124 self.assertEqual(type(t), ast.Module)
125 self.assertEqual(len(t.body), 2)
126 # check `f'no formatted value'`
127 self.assertEqual(type(t.body[0]), ast.Expr)
128 self.assertEqual(type(t.body[0].value), ast.JoinedStr)
129 self.assertEqual(t.body[0].lineno, 2)
130 # check `f'...'`
131 self.assertEqual(type(t.body[1]), ast.Expr)
132 self.assertEqual(type(t.body[1].value), ast.JoinedStr)
133 self.assertEqual(len(t.body[1].value.values), 4)
134 self.assertEqual(type(t.body[1].value.values[0]), ast.Constant)
135 self.assertEqual(type(t.body[1].value.values[0].value), str)
136 self.assertEqual(type(t.body[1].value.values[1]), ast.FormattedValue)
137 self.assertEqual(type(t.body[1].value.values[2]), ast.Constant)
138 self.assertEqual(type(t.body[1].value.values[2].value), str)
139 self.assertEqual(type(t.body[1].value.values[3]), ast.FormattedValue)
140 self.assertEqual(t.body[1].lineno, 3)
141 self.assertEqual(t.body[1].value.lineno, 3)
142 self.assertEqual(t.body[1].value.values[0].lineno, 3)
143 self.assertEqual(t.body[1].value.values[1].lineno, 3)
144 self.assertEqual(t.body[1].value.values[2].lineno, 3)
145 self.assertEqual(t.body[1].value.values[3].lineno, 3)
146 # check the first binop location
147 binop1 = t.body[1].value.values[1].value
148 self.assertEqual(type(binop1), ast.BinOp)
149 self.assertEqual(type(binop1.left), ast.Name)
150 self.assertEqual(type(binop1.op), ast.Mult)
151 self.assertEqual(type(binop1.right), ast.Call)
152 self.assertEqual(binop1.lineno, 3)
153 self.assertEqual(binop1.left.lineno, 3)
154 self.assertEqual(binop1.right.lineno, 3)
155 self.assertEqual(binop1.col_offset, 8)
156 self.assertEqual(binop1.left.col_offset, 8)
157 self.assertEqual(binop1.right.col_offset, 12)
158 # check the second binop location
159 binop2 = t.body[1].value.values[3].value
160 self.assertEqual(type(binop2), ast.BinOp)
161 self.assertEqual(type(binop2.left), ast.Name)
162 self.assertEqual(type(binop2.op), ast.Add)
163 self.assertEqual(type(binop2.right), ast.Call)
164 self.assertEqual(binop2.lineno, 3)
165 self.assertEqual(binop2.left.lineno, 3)
166 self.assertEqual(binop2.right.lineno, 3)
167 self.assertEqual(binop2.col_offset, 23)
168 self.assertEqual(binop2.left.col_offset, 23)
169 self.assertEqual(binop2.right.col_offset, 27)
170
171 def test_ast_line_numbers_nested(self):
172 expr = """
173 a = 10
174 f'{a * f"-{x()}-"}'"""
175 t = ast.parse(expr)
176 self.assertEqual(type(t), ast.Module)
177 self.assertEqual(len(t.body), 2)
178 # check `a = 10`
179 self.assertEqual(type(t.body[0]), ast.Assign)
180 self.assertEqual(t.body[0].lineno, 2)
181 # check `f'...'`
182 self.assertEqual(type(t.body[1]), ast.Expr)
183 self.assertEqual(type(t.body[1].value), ast.JoinedStr)
184 self.assertEqual(len(t.body[1].value.values), 1)
185 self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)
186 self.assertEqual(t.body[1].lineno, 3)
187 self.assertEqual(t.body[1].value.lineno, 3)
188 self.assertEqual(t.body[1].value.values[0].lineno, 3)
189 # check the binop location
190 binop = t.body[1].value.values[0].value
191 self.assertEqual(type(binop), ast.BinOp)
192 self.assertEqual(type(binop.left), ast.Name)
193 self.assertEqual(type(binop.op), ast.Mult)
194 self.assertEqual(type(binop.right), ast.JoinedStr)
195 self.assertEqual(binop.lineno, 3)
196 self.assertEqual(binop.left.lineno, 3)
197 self.assertEqual(binop.right.lineno, 3)
198 self.assertEqual(binop.col_offset, 3)
199 self.assertEqual(binop.left.col_offset, 3)
200 self.assertEqual(binop.right.col_offset, 7)
201 # check the nested call location
202 self.assertEqual(len(binop.right.values), 3)
203 self.assertEqual(type(binop.right.values[0]), ast.Constant)
204 self.assertEqual(type(binop.right.values[0].value), str)
205 self.assertEqual(type(binop.right.values[1]), ast.FormattedValue)
206 self.assertEqual(type(binop.right.values[2]), ast.Constant)
207 self.assertEqual(type(binop.right.values[2].value), str)
208 self.assertEqual(binop.right.values[0].lineno, 3)
209 self.assertEqual(binop.right.values[1].lineno, 3)
210 self.assertEqual(binop.right.values[2].lineno, 3)
211 call = binop.right.values[1].value
212 self.assertEqual(type(call), ast.Call)
213 self.assertEqual(call.lineno, 3)
214 self.assertEqual(call.col_offset, 11)
215
216 def test_ast_line_numbers_duplicate_expression(self):
217 expr = """
218 a = 10
219 f'{a * x()} {a * x()} {a * x()}'
220 """
221 t = ast.parse(expr)
222 self.assertEqual(type(t), ast.Module)
223 self.assertEqual(len(t.body), 2)
224 # check `a = 10`
225 self.assertEqual(type(t.body[0]), ast.Assign)
226 self.assertEqual(t.body[0].lineno, 2)
227 # check `f'...'`
228 self.assertEqual(type(t.body[1]), ast.Expr)
229 self.assertEqual(type(t.body[1].value), ast.JoinedStr)
230 self.assertEqual(len(t.body[1].value.values), 5)
231 self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)
232 self.assertEqual(type(t.body[1].value.values[1]), ast.Constant)
233 self.assertEqual(type(t.body[1].value.values[1].value), str)
234 self.assertEqual(type(t.body[1].value.values[2]), ast.FormattedValue)
235 self.assertEqual(type(t.body[1].value.values[3]), ast.Constant)
236 self.assertEqual(type(t.body[1].value.values[3].value), str)
237 self.assertEqual(type(t.body[1].value.values[4]), ast.FormattedValue)
238 self.assertEqual(t.body[1].lineno, 3)
239 self.assertEqual(t.body[1].value.lineno, 3)
240 self.assertEqual(t.body[1].value.values[0].lineno, 3)
241 self.assertEqual(t.body[1].value.values[1].lineno, 3)
242 self.assertEqual(t.body[1].value.values[2].lineno, 3)
243 self.assertEqual(t.body[1].value.values[3].lineno, 3)
244 self.assertEqual(t.body[1].value.values[4].lineno, 3)
245 # check the first binop location
246 binop = t.body[1].value.values[0].value
247 self.assertEqual(type(binop), ast.BinOp)
248 self.assertEqual(type(binop.left), ast.Name)
249 self.assertEqual(type(binop.op), ast.Mult)
250 self.assertEqual(type(binop.right), ast.Call)
251 self.assertEqual(binop.lineno, 3)
252 self.assertEqual(binop.left.lineno, 3)
253 self.assertEqual(binop.right.lineno, 3)
254 self.assertEqual(binop.col_offset, 3)
255 self.assertEqual(binop.left.col_offset, 3)
256 self.assertEqual(binop.right.col_offset, 7)
257 # check the second binop location
258 binop = t.body[1].value.values[2].value
259 self.assertEqual(type(binop), ast.BinOp)
260 self.assertEqual(type(binop.left), ast.Name)
261 self.assertEqual(type(binop.op), ast.Mult)
262 self.assertEqual(type(binop.right), ast.Call)
263 self.assertEqual(binop.lineno, 3)
264 self.assertEqual(binop.left.lineno, 3)
265 self.assertEqual(binop.right.lineno, 3)
266 self.assertEqual(binop.col_offset, 13)
267 self.assertEqual(binop.left.col_offset, 13)
268 self.assertEqual(binop.right.col_offset, 17)
269 # check the third binop location
270 binop = t.body[1].value.values[4].value
271 self.assertEqual(type(binop), ast.BinOp)
272 self.assertEqual(type(binop.left), ast.Name)
273 self.assertEqual(type(binop.op), ast.Mult)
274 self.assertEqual(type(binop.right), ast.Call)
275 self.assertEqual(binop.lineno, 3)
276 self.assertEqual(binop.left.lineno, 3)
277 self.assertEqual(binop.right.lineno, 3)
278 self.assertEqual(binop.col_offset, 23)
279 self.assertEqual(binop.left.col_offset, 23)
280 self.assertEqual(binop.right.col_offset, 27)
281
282 def test_ast_numbers_fstring_with_formatting(self):
283
284 t = ast.parse('f"Here is that pesky {xxx:.3f} again"')
285 self.assertEqual(len(t.body), 1)
286 self.assertEqual(t.body[0].lineno, 1)
287
288 self.assertEqual(type(t.body[0]), ast.Expr)
289 self.assertEqual(type(t.body[0].value), ast.JoinedStr)
290 self.assertEqual(len(t.body[0].value.values), 3)
291
292 self.assertEqual(type(t.body[0].value.values[0]), ast.Constant)
293 self.assertEqual(type(t.body[0].value.values[1]), ast.FormattedValue)
294 self.assertEqual(type(t.body[0].value.values[2]), ast.Constant)
295
296 _, expr, _ = t.body[0].value.values
297
298 name = expr.value
299 self.assertEqual(type(name), ast.Name)
300 self.assertEqual(name.lineno, 1)
301 self.assertEqual(name.end_lineno, 1)
302 self.assertEqual(name.col_offset, 22)
303 self.assertEqual(name.end_col_offset, 25)
304
305 def test_ast_line_numbers_multiline_fstring(self):
306 # See bpo-30465 for details.
307 expr = """
308 a = 10
309 f'''
310 {a
311 *
312 x()}
313 non-important content
314 '''
315 """
316 t = ast.parse(expr)
317 self.assertEqual(type(t), ast.Module)
318 self.assertEqual(len(t.body), 2)
319 # check `a = 10`
320 self.assertEqual(type(t.body[0]), ast.Assign)
321 self.assertEqual(t.body[0].lineno, 2)
322 # check `f'...'`
323 self.assertEqual(type(t.body[1]), ast.Expr)
324 self.assertEqual(type(t.body[1].value), ast.JoinedStr)
325 self.assertEqual(len(t.body[1].value.values), 3)
326 self.assertEqual(type(t.body[1].value.values[0]), ast.Constant)
327 self.assertEqual(type(t.body[1].value.values[0].value), str)
328 self.assertEqual(type(t.body[1].value.values[1]), ast.FormattedValue)
329 self.assertEqual(type(t.body[1].value.values[2]), ast.Constant)
330 self.assertEqual(type(t.body[1].value.values[2].value), str)
331 self.assertEqual(t.body[1].lineno, 3)
332 self.assertEqual(t.body[1].value.lineno, 3)
333 self.assertEqual(t.body[1].value.values[0].lineno, 3)
334 self.assertEqual(t.body[1].value.values[1].lineno, 4)
335 self.assertEqual(t.body[1].value.values[2].lineno, 6)
336 self.assertEqual(t.body[1].col_offset, 0)
337 self.assertEqual(t.body[1].value.col_offset, 0)
338 self.assertEqual(t.body[1].value.values[0].col_offset, 4)
339 self.assertEqual(t.body[1].value.values[1].col_offset, 2)
340 self.assertEqual(t.body[1].value.values[2].col_offset, 11)
341 # NOTE: the following lineno information and col_offset is correct for
342 # expressions within FormattedValues.
343 binop = t.body[1].value.values[1].value
344 self.assertEqual(type(binop), ast.BinOp)
345 self.assertEqual(type(binop.left), ast.Name)
346 self.assertEqual(type(binop.op), ast.Mult)
347 self.assertEqual(type(binop.right), ast.Call)
348 self.assertEqual(binop.lineno, 4)
349 self.assertEqual(binop.left.lineno, 4)
350 self.assertEqual(binop.right.lineno, 6)
351 self.assertEqual(binop.col_offset, 3)
352 self.assertEqual(binop.left.col_offset, 3)
353 self.assertEqual(binop.right.col_offset, 7)
354
355 expr = """
356 a = f'''
357 {blech}
358 '''
359 """
360 t = ast.parse(expr)
361 self.assertEqual(type(t), ast.Module)
362 self.assertEqual(len(t.body), 1)
363 # Check f'...'
364 self.assertEqual(type(t.body[0]), ast.Assign)
365 self.assertEqual(type(t.body[0].value), ast.JoinedStr)
366 self.assertEqual(len(t.body[0].value.values), 3)
367 self.assertEqual(type(t.body[0].value.values[1]), ast.FormattedValue)
368 self.assertEqual(t.body[0].lineno, 2)
369 self.assertEqual(t.body[0].value.lineno, 2)
370 self.assertEqual(t.body[0].value.values[0].lineno, 2)
371 self.assertEqual(t.body[0].value.values[1].lineno, 3)
372 self.assertEqual(t.body[0].value.values[2].lineno, 3)
373 self.assertEqual(t.body[0].col_offset, 0)
374 self.assertEqual(t.body[0].value.col_offset, 4)
375 self.assertEqual(t.body[0].value.values[0].col_offset, 8)
376 self.assertEqual(t.body[0].value.values[1].col_offset, 10)
377 self.assertEqual(t.body[0].value.values[2].col_offset, 17)
378 # Check {blech}
379 self.assertEqual(t.body[0].value.values[1].value.lineno, 3)
380 self.assertEqual(t.body[0].value.values[1].value.end_lineno, 3)
381 self.assertEqual(t.body[0].value.values[1].value.col_offset, 11)
382 self.assertEqual(t.body[0].value.values[1].value.end_col_offset, 16)
383
384 def test_ast_line_numbers_with_parentheses(self):
385 expr = """
386 x = (
387 f" {test(t)}"
388 )"""
389 t = ast.parse(expr)
390 self.assertEqual(type(t), ast.Module)
391 self.assertEqual(len(t.body), 1)
392 # check the joinedstr location
393 joinedstr = t.body[0].value
394 self.assertEqual(type(joinedstr), ast.JoinedStr)
395 self.assertEqual(joinedstr.lineno, 3)
396 self.assertEqual(joinedstr.end_lineno, 3)
397 self.assertEqual(joinedstr.col_offset, 4)
398 self.assertEqual(joinedstr.end_col_offset, 17)
399 # check the formatted value location
400 fv = t.body[0].value.values[1]
401 self.assertEqual(type(fv), ast.FormattedValue)
402 self.assertEqual(fv.lineno, 3)
403 self.assertEqual(fv.end_lineno, 3)
404 self.assertEqual(fv.col_offset, 7)
405 self.assertEqual(fv.end_col_offset, 16)
406 # check the test(t) location
407 call = t.body[0].value.values[1].value
408 self.assertEqual(type(call), ast.Call)
409 self.assertEqual(call.lineno, 3)
410 self.assertEqual(call.end_lineno, 3)
411 self.assertEqual(call.col_offset, 8)
412 self.assertEqual(call.end_col_offset, 15)
413
414 expr = """
415 x = (
416 u'wat',
417 u"wat",
418 b'wat',
419 b"wat",
420 f'wat',
421 f"wat",
422 )
423
424 y = (
425 u'''wat''',
426 u\"\"\"wat\"\"\",
427 b'''wat''',
428 b\"\"\"wat\"\"\",
429 f'''wat''',
430 f\"\"\"wat\"\"\",
431 )
432 """
433 t = ast.parse(expr)
434 self.assertEqual(type(t), ast.Module)
435 self.assertEqual(len(t.body), 2)
436 x, y = t.body
437
438 # Check the single quoted string offsets first.
439 offsets = [
440 (elt.col_offset, elt.end_col_offset)
441 for elt in x.value.elts
442 ]
443 self.assertTrue(all(
444 offset == (4, 10)
445 for offset in offsets
446 ))
447
448 # Check the triple quoted string offsets.
449 offsets = [
450 (elt.col_offset, elt.end_col_offset)
451 for elt in y.value.elts
452 ]
453 self.assertTrue(all(
454 offset == (4, 14)
455 for offset in offsets
456 ))
457
458 expr = """
459 x = (
460 'PERL_MM_OPT', (
461 f'wat'
462 f'some_string={f(x)} '
463 f'wat'
464 ),
465 )
466 """
467 t = ast.parse(expr)
468 self.assertEqual(type(t), ast.Module)
469 self.assertEqual(len(t.body), 1)
470 # check the fstring
471 fstring = t.body[0].value.elts[1]
472 self.assertEqual(type(fstring), ast.JoinedStr)
473 self.assertEqual(len(fstring.values), 3)
474 wat1, middle, wat2 = fstring.values
475 # check the first wat
476 self.assertEqual(type(wat1), ast.Constant)
477 self.assertEqual(wat1.lineno, 4)
478 self.assertEqual(wat1.end_lineno, 5)
479 self.assertEqual(wat1.col_offset, 14)
480 self.assertEqual(wat1.end_col_offset, 26)
481 # check the call
482 call = middle.value
483 self.assertEqual(type(call), ast.Call)
484 self.assertEqual(call.lineno, 5)
485 self.assertEqual(call.end_lineno, 5)
486 self.assertEqual(call.col_offset, 27)
487 self.assertEqual(call.end_col_offset, 31)
488 # check the second wat
489 self.assertEqual(type(wat2), ast.Constant)
490 self.assertEqual(wat2.lineno, 5)
491 self.assertEqual(wat2.end_lineno, 6)
492 self.assertEqual(wat2.col_offset, 32)
493 # wat ends at the offset 17, but the whole f-string
494 # ends at the offset 18 (since the quote is part of the
495 # f-string but not the wat string)
496 self.assertEqual(wat2.end_col_offset, 17)
497 self.assertEqual(fstring.end_col_offset, 18)
498
499 def test_ast_fstring_empty_format_spec(self):
500 expr = "f'{expr:}'"
501
502 mod = ast.parse(expr)
503 self.assertEqual(type(mod), ast.Module)
504 self.assertEqual(len(mod.body), 1)
505
506 fstring = mod.body[0].value
507 self.assertEqual(type(fstring), ast.JoinedStr)
508 self.assertEqual(len(fstring.values), 1)
509
510 fv = fstring.values[0]
511 self.assertEqual(type(fv), ast.FormattedValue)
512
513 format_spec = fv.format_spec
514 self.assertEqual(type(format_spec), ast.JoinedStr)
515 self.assertEqual(len(format_spec.values), 0)
516
517 def test_docstring(self):
518 def f():
519 f'''Not a docstring'''
520 self.assertIsNone(f.__doc__)
521 def g():
522 '''Not a docstring''' \
523 f''
524 self.assertIsNone(g.__doc__)
525
526 def test_literal_eval(self):
527 with self.assertRaisesRegex(ValueError, 'malformed node or string'):
528 ast.literal_eval("f'x'")
529
530 def test_ast_compile_time_concat(self):
531 x = ['']
532
533 expr = """x[0] = 'foo' f'{3}'"""
534 t = ast.parse(expr)
535 c = compile(t, '', 'exec')
536 exec(c)
537 self.assertEqual(x[0], 'foo3')
538
539 def test_compile_time_concat_errors(self):
540 self.assertAllRaise(SyntaxError,
541 'cannot mix bytes and nonbytes literals',
542 [r"""f'' b''""",
543 r"""b'' f''""",
544 ])
545
546 def test_literal(self):
547 self.assertEqual(f'', '')
548 self.assertEqual(f'a', 'a')
549 self.assertEqual(f' ', ' ')
550
551 def test_unterminated_string(self):
552 self.assertAllRaise(SyntaxError, 'unterminated string',
553 [r"""f'{"x'""",
554 r"""f'{"x}'""",
555 r"""f'{("x'""",
556 r"""f'{("x}'""",
557 ])
558
559 @unittest.skipIf(support.is_wasi, "exhausts limited stack on WASI")
560 def test_mismatched_parens(self):
561 self.assertAllRaise(SyntaxError, r"closing parenthesis '\}' "
562 r"does not match opening parenthesis '\('",
563 ["f'{((}'",
564 ])
565 self.assertAllRaise(SyntaxError, r"closing parenthesis '\)' "
566 r"does not match opening parenthesis '\['",
567 ["f'{a[4)}'",
568 ])
569 self.assertAllRaise(SyntaxError, r"closing parenthesis '\]' "
570 r"does not match opening parenthesis '\('",
571 ["f'{a(4]}'",
572 ])
573 self.assertAllRaise(SyntaxError, r"closing parenthesis '\}' "
574 r"does not match opening parenthesis '\['",
575 ["f'{a[4}'",
576 ])
577 self.assertAllRaise(SyntaxError, r"closing parenthesis '\}' "
578 r"does not match opening parenthesis '\('",
579 ["f'{a(4}'",
580 ])
581 self.assertRaises(SyntaxError, eval, "f'{" + "("*500 + "}'")
582
583 @unittest.skipIf(support.is_wasi, "exhausts limited stack on WASI")
584 def test_fstring_nested_too_deeply(self):
585 self.assertAllRaise(SyntaxError,
586 "f-string: expressions nested too deeply",
587 ['f"{1+2:{1+2:{1+1:{1}}}}"'])
588
589 def create_nested_fstring(n):
590 if n == 0:
591 return "1+1"
592 prev = create_nested_fstring(n-1)
593 return f'f"{{{prev}}}"'
594
595 self.assertAllRaise(SyntaxError,
596 "too many nested f-strings",
597 [create_nested_fstring(160)])
598
599 def test_syntax_error_in_nested_fstring(self):
600 # See gh-104016 for more information on this crash
601 self.assertAllRaise(SyntaxError,
602 "invalid syntax",
603 ['f"{1 1:' + ('{f"1:' * 199)])
604
605 def test_double_braces(self):
606 self.assertEqual(f'{{', '{')
607 self.assertEqual(f'a{{', 'a{')
608 self.assertEqual(f'{{b', '{b')
609 self.assertEqual(f'a{{b', 'a{b')
610 self.assertEqual(f'}}', '}')
611 self.assertEqual(f'a}}', 'a}')
612 self.assertEqual(f'}}b', '}b')
613 self.assertEqual(f'a}}b', 'a}b')
614 self.assertEqual(f'{{}}', '{}')
615 self.assertEqual(f'a{{}}', 'a{}')
616 self.assertEqual(f'{{b}}', '{b}')
617 self.assertEqual(f'{{}}c', '{}c')
618 self.assertEqual(f'a{{b}}', 'a{b}')
619 self.assertEqual(f'a{{}}c', 'a{}c')
620 self.assertEqual(f'{{b}}c', '{b}c')
621 self.assertEqual(f'a{{b}}c', 'a{b}c')
622
623 self.assertEqual(f'{{{10}', '{10')
624 self.assertEqual(f'}}{10}', '}10')
625 self.assertEqual(f'}}{{{10}', '}{10')
626 self.assertEqual(f'}}a{{{10}', '}a{10')
627
628 self.assertEqual(f'{10}{{', '10{')
629 self.assertEqual(f'{10}}}', '10}')
630 self.assertEqual(f'{10}}}{{', '10}{')
631 self.assertEqual(f'{10}}}a{{' '}', '10}a{}')
632
633 # Inside of strings, don't interpret doubled brackets.
634 self.assertEqual(f'{"{{}}"}', '{{}}')
635
636 self.assertAllRaise(TypeError, 'unhashable type',
637 ["f'{ {{}} }'", # dict in a set
638 ])
639
640 def test_compile_time_concat(self):
641 x = 'def'
642 self.assertEqual('abc' f'## {x}ghi', 'abc## defghi')
643 self.assertEqual('abc' f'{x}' 'ghi', 'abcdefghi')
644 self.assertEqual('abc' f'{x}' 'gh' f'i{x:4}', 'abcdefghidef ')
645 self.assertEqual('{x}' f'{x}', '{x}def')
646 self.assertEqual('{x' f'{x}', '{xdef')
647 self.assertEqual('{x}' f'{x}', '{x}def')
648 self.assertEqual('{{x}}' f'{x}', '{{x}}def')
649 self.assertEqual('{{x' f'{x}', '{{xdef')
650 self.assertEqual('x}}' f'{x}', 'x}}def')
651 self.assertEqual(f'{x}' 'x}}', 'defx}}')
652 self.assertEqual(f'{x}' '', 'def')
653 self.assertEqual('' f'{x}' '', 'def')
654 self.assertEqual('' f'{x}', 'def')
655 self.assertEqual(f'{x}' '2', 'def2')
656 self.assertEqual('1' f'{x}' '2', '1def2')
657 self.assertEqual('1' f'{x}', '1def')
658 self.assertEqual(f'{x}' f'-{x}', 'def-def')
659 self.assertEqual('' f'', '')
660 self.assertEqual('' f'' '', '')
661 self.assertEqual('' f'' '' f'', '')
662 self.assertEqual(f'', '')
663 self.assertEqual(f'' '', '')
664 self.assertEqual(f'' '' f'', '')
665 self.assertEqual(f'' '' f'' '', '')
666
667 # This is not really [f'{'] + [f'}'] since we treat the inside
668 # of braces as a purely new context, so it is actually f'{ and
669 # then eval(' f') (a valid expression) and then }' which would
670 # constitute a valid f-string.
671 self.assertEqual(f'{' f'}', ' f')
672
673 self.assertAllRaise(SyntaxError, "expecting '}'",
674 ['''f'{3' f"}"''', # can't concat to get a valid f-string
675 ])
676
677 def test_comments(self):
678 # These aren't comments, since they're in strings.
679 d = {'#': 'hash'}
680 self.assertEqual(f'{"#"}', '#')
681 self.assertEqual(f'{d["#"]}', 'hash')
682
683 self.assertAllRaise(SyntaxError, "'{' was never closed",
684 ["f'{1#}'", # error because everything after '#' is a comment
685 "f'{#}'",
686 "f'one: {1#}'",
687 "f'{1# one} {2 this is a comment still#}'",
688 ])
689 self.assertAllRaise(SyntaxError, r"f-string: unmatched '\)'",
690 ["f'{)#}'", # When wrapped in parens, this becomes
691 # '()#)'. Make sure that doesn't compile.
692 ])
693 self.assertEqual(f'''A complex trick: {
694 2 # two
695 }''', 'A complex trick: 2')
696 self.assertEqual(f'''
697 {
698 40 # fourty
699 + # plus
700 2 # two
701 }''', '\n42')
702 self.assertEqual(f'''
703 {
704 40 # fourty
705 + # plus
706 2 # two
707 }''', '\n42')
708
709 self.assertEqual(f'''
710 # this is not a comment
711 { # the following operation it's
712 3 # this is a number
713 * 2}''', '\n# this is not a comment\n6')
714 self.assertEqual(f'''
715 {# f'a {comment}'
716 86 # constant
717 # nothing more
718 }''', '\n86')
719
720 self.assertAllRaise(SyntaxError, r"f-string: valid expression required before '}'",
721 ["""f'''
722 {
723 # only a comment
724 }'''
725 """, # this is equivalent to f'{}'
726 ])
727
728 def test_many_expressions(self):
729 # Create a string with many expressions in it. Note that
730 # because we have a space in here as a literal, we're actually
731 # going to use twice as many ast nodes: one for each literal
732 # plus one for each expression.
733 def build_fstr(n, extra=''):
734 return "f'" + ('{x} ' * n) + extra + "'"
735
736 x = 'X'
737 width = 1
738
739 # Test around 256.
740 for i in range(250, 260):
741 self.assertEqual(eval(build_fstr(i)), (x+' ')*i)
742
743 # Test concatenating 2 largs fstrings.
744 self.assertEqual(eval(build_fstr(255)*256), (x+' ')*(255*256))
745
746 s = build_fstr(253, '{x:{width}} ')
747 self.assertEqual(eval(s), (x+' ')*254)
748
749 # Test lots of expressions and constants, concatenated.
750 s = "f'{1}' 'x' 'y'" * 1024
751 self.assertEqual(eval(s), '1xy' * 1024)
752
753 def test_format_specifier_expressions(self):
754 width = 10
755 precision = 4
756 value = decimal.Decimal('12.34567')
757 self.assertEqual(f'result: {value:{width}.{precision}}', 'result: 12.35')
758 self.assertEqual(f'result: {value:{width!r}.{precision}}', 'result: 12.35')
759 self.assertEqual(f'result: {value:{width:0}.{precision:1}}', 'result: 12.35')
760 self.assertEqual(f'result: {value:{1}{0:0}.{precision:1}}', 'result: 12.35')
761 self.assertEqual(f'result: {value:{ 1}{ 0:0}.{ precision:1}}', 'result: 12.35')
762 self.assertEqual(f'{10:#{1}0x}', ' 0xa')
763 self.assertEqual(f'{10:{"#"}1{0}{"x"}}', ' 0xa')
764 self.assertEqual(f'{-10:-{"#"}1{0}x}', ' -0xa')
765 self.assertEqual(f'{-10:{"-"}#{1}0{"x"}}', ' -0xa')
766 self.assertEqual(f'{10:#{3 != {4:5} and width}x}', ' 0xa')
767 self.assertEqual(f'result: {value:{width:{0}}.{precision:1}}', 'result: 12.35')
768
769 self.assertAllRaise(SyntaxError, "f-string: expecting ':' or '}'",
770 ["""f'{"s"!r{":10"}}'""",
771 # This looks like a nested format spec.
772 ])
773
774 self.assertAllRaise(SyntaxError,
775 "f-string: expecting a valid expression after '{'",
776 [# Invalid syntax inside a nested spec.
777 "f'{4:{/5}}'",
778 ])
779
780 self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character',
781 [# No expansion inside conversion or for
782 # the : or ! itself.
783 """f'{"s"!{"r"}}'""",
784 ])
785
786 def test_custom_format_specifier(self):
787 class ESC[4;38;5;81mCustomFormat:
788 def __format__(self, format_spec):
789 return format_spec
790
791 self.assertEqual(f'{CustomFormat():\n}', '\n')
792 self.assertEqual(f'{CustomFormat():\u2603}', '☃')
793 with self.assertWarns(SyntaxWarning):
794 exec(r'f"{F():¯\_(ツ)_/¯}"', {'F': CustomFormat})
795
796 def test_side_effect_order(self):
797 class ESC[4;38;5;81mX:
798 def __init__(self):
799 self.i = 0
800 def __format__(self, spec):
801 self.i += 1
802 return str(self.i)
803
804 x = X()
805 self.assertEqual(f'{x} {x}', '1 2')
806
807 def test_missing_expression(self):
808 self.assertAllRaise(SyntaxError,
809 "f-string: valid expression required before '}'",
810 ["f'{}'",
811 "f'{ }'"
812 "f' {} '",
813 "f'{10:{ }}'",
814 "f' { } '",
815
816 # The Python parser ignores also the following
817 # whitespace characters in additional to a space.
818 "f'''{\t\f\r\n}'''",
819 ])
820
821 self.assertAllRaise(SyntaxError,
822 "f-string: valid expression required before '!'",
823 ["f'{!r}'",
824 "f'{ !r}'",
825 "f'{!}'",
826 "f'''{\t\f\r\n!a}'''",
827
828 # Catch empty expression before the
829 # missing closing brace.
830 "f'{!'",
831 "f'{!s:'",
832
833 # Catch empty expression before the
834 # invalid conversion.
835 "f'{!x}'",
836 "f'{ !xr}'",
837 "f'{!x:}'",
838 "f'{!x:a}'",
839 "f'{ !xr:}'",
840 "f'{ !xr:a}'",
841 ])
842
843 self.assertAllRaise(SyntaxError,
844 "f-string: valid expression required before ':'",
845 ["f'{:}'",
846 "f'{ :!}'",
847 "f'{:2}'",
848 "f'''{\t\f\r\n:a}'''",
849 "f'{:'",
850 ])
851
852 self.assertAllRaise(SyntaxError,
853 "f-string: valid expression required before '='",
854 ["f'{=}'",
855 "f'{ =}'",
856 "f'{ =:}'",
857 "f'{ =!}'",
858 "f'''{\t\f\r\n=}'''",
859 "f'{='",
860 ])
861
862 # Different error message is raised for other whitespace characters.
863 self.assertAllRaise(SyntaxError, r"invalid non-printable character U\+00A0",
864 ["f'''{\xa0}'''",
865 "\xa0",
866 ])
867
868 def test_parens_in_expressions(self):
869 self.assertEqual(f'{3,}', '(3,)')
870
871 self.assertAllRaise(SyntaxError,
872 "f-string: expecting a valid expression after '{'",
873 ["f'{,}'",
874 ])
875
876 self.assertAllRaise(SyntaxError, r"f-string: unmatched '\)'",
877 ["f'{3)+(4}'",
878 ])
879
880 def test_newlines_before_syntax_error(self):
881 self.assertAllRaise(SyntaxError,
882 "f-string: expecting a valid expression after '{'",
883 ["f'{.}'", "\nf'{.}'", "\n\nf'{.}'"])
884
885 def test_backslashes_in_string_part(self):
886 self.assertEqual(f'\t', '\t')
887 self.assertEqual(r'\t', '\\t')
888 self.assertEqual(rf'\t', '\\t')
889 self.assertEqual(f'{2}\t', '2\t')
890 self.assertEqual(f'{2}\t{3}', '2\t3')
891 self.assertEqual(f'\t{3}', '\t3')
892
893 self.assertEqual(f'\u0394', '\u0394')
894 self.assertEqual(r'\u0394', '\\u0394')
895 self.assertEqual(rf'\u0394', '\\u0394')
896 self.assertEqual(f'{2}\u0394', '2\u0394')
897 self.assertEqual(f'{2}\u0394{3}', '2\u03943')
898 self.assertEqual(f'\u0394{3}', '\u03943')
899
900 self.assertEqual(f'\U00000394', '\u0394')
901 self.assertEqual(r'\U00000394', '\\U00000394')
902 self.assertEqual(rf'\U00000394', '\\U00000394')
903 self.assertEqual(f'{2}\U00000394', '2\u0394')
904 self.assertEqual(f'{2}\U00000394{3}', '2\u03943')
905 self.assertEqual(f'\U00000394{3}', '\u03943')
906
907 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}', '\u0394')
908 self.assertEqual(f'{2}\N{GREEK CAPITAL LETTER DELTA}', '2\u0394')
909 self.assertEqual(f'{2}\N{GREEK CAPITAL LETTER DELTA}{3}', '2\u03943')
910 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}{3}', '\u03943')
911 self.assertEqual(f'2\N{GREEK CAPITAL LETTER DELTA}', '2\u0394')
912 self.assertEqual(f'2\N{GREEK CAPITAL LETTER DELTA}3', '2\u03943')
913 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}3', '\u03943')
914
915 self.assertEqual(f'\x20', ' ')
916 self.assertEqual(r'\x20', '\\x20')
917 self.assertEqual(rf'\x20', '\\x20')
918 self.assertEqual(f'{2}\x20', '2 ')
919 self.assertEqual(f'{2}\x20{3}', '2 3')
920 self.assertEqual(f'\x20{3}', ' 3')
921
922 self.assertEqual(f'2\x20', '2 ')
923 self.assertEqual(f'2\x203', '2 3')
924 self.assertEqual(f'\x203', ' 3')
925
926 with self.assertWarns(SyntaxWarning): # invalid escape sequence
927 value = eval(r"f'\{6*7}'")
928 self.assertEqual(value, '\\42')
929 with self.assertWarns(SyntaxWarning): # invalid escape sequence
930 value = eval(r"f'\g'")
931 self.assertEqual(value, '\\g')
932 self.assertEqual(f'\\{6*7}', '\\42')
933 self.assertEqual(fr'\{6*7}', '\\42')
934
935 AMPERSAND = 'spam'
936 # Get the right unicode character (&), or pick up local variable
937 # depending on the number of backslashes.
938 self.assertEqual(f'\N{AMPERSAND}', '&')
939 self.assertEqual(f'\\N{AMPERSAND}', '\\Nspam')
940 self.assertEqual(fr'\N{AMPERSAND}', '\\Nspam')
941 self.assertEqual(f'\\\N{AMPERSAND}', '\\&')
942
943 def test_misformed_unicode_character_name(self):
944 # These test are needed because unicode names are parsed
945 # differently inside f-strings.
946 self.assertAllRaise(SyntaxError, r"\(unicode error\) 'unicodeescape' codec can't decode bytes in position .*: malformed \\N character escape",
947 [r"f'\N'",
948 r"f'\N '",
949 r"f'\N '", # See bpo-46503.
950 r"f'\N{'",
951 r"f'\N{GREEK CAPITAL LETTER DELTA'",
952
953 # Here are the non-f-string versions,
954 # which should give the same errors.
955 r"'\N'",
956 r"'\N '",
957 r"'\N '",
958 r"'\N{'",
959 r"'\N{GREEK CAPITAL LETTER DELTA'",
960 ])
961
962 def test_backslashes_in_expression_part(self):
963 self.assertEqual(f"{(
964 1 +
965 2
966 )}", "3")
967
968 self.assertEqual("\N{LEFT CURLY BRACKET}", '{')
969 self.assertEqual(f'{"\N{LEFT CURLY BRACKET}"}', '{')
970 self.assertEqual(rf'{"\N{LEFT CURLY BRACKET}"}', '{')
971
972 self.assertAllRaise(SyntaxError,
973 "f-string: valid expression required before '}'",
974 ["f'{\n}'",
975 ])
976
977 def test_invalid_backslashes_inside_fstring_context(self):
978 # All of these variations are invalid python syntax,
979 # so they are also invalid in f-strings as well.
980 cases = [
981 formatting.format(expr=expr)
982 for formatting in [
983 "{expr}",
984 "f'{{{expr}}}'",
985 "rf'{{{expr}}}'",
986 ]
987 for expr in [
988 r"\'a\'",
989 r"\t3",
990 r"\\"[0],
991 ]
992 ]
993 self.assertAllRaise(SyntaxError, 'unexpected character after line continuation',
994 cases)
995
996 def test_no_escapes_for_braces(self):
997 """
998 Only literal curly braces begin an expression.
999 """
1000 # \x7b is '{'.
1001 self.assertEqual(f'\x7b1+1}}', '{1+1}')
1002 self.assertEqual(f'\x7b1+1', '{1+1')
1003 self.assertEqual(f'\u007b1+1', '{1+1')
1004 self.assertEqual(f'\N{LEFT CURLY BRACKET}1+1\N{RIGHT CURLY BRACKET}', '{1+1}')
1005
1006 def test_newlines_in_expressions(self):
1007 self.assertEqual(f'{0}', '0')
1008 self.assertEqual(rf'''{3+
1009 4}''', '7')
1010
1011 def test_lambda(self):
1012 x = 5
1013 self.assertEqual(f'{(lambda y:x*y)("8")!r}', "'88888'")
1014 self.assertEqual(f'{(lambda y:x*y)("8")!r:10}', "'88888' ")
1015 self.assertEqual(f'{(lambda y:x*y)("8"):10}', "88888 ")
1016
1017 # lambda doesn't work without parens, because the colon
1018 # makes the parser think it's a format_spec
1019 # emit warning if we can match a format_spec
1020 self.assertAllRaise(SyntaxError,
1021 "f-string: lambda expressions are not allowed "
1022 "without parentheses",
1023 ["f'{lambda x:x}'",
1024 "f'{lambda :x}'",
1025 "f'{lambda *arg, :x}'",
1026 "f'{1, lambda:x}'",
1027 "f'{lambda x:}'",
1028 "f'{lambda :}'",
1029 ])
1030 # Ensure the detection of invalid lambdas doesn't trigger detection
1031 # for valid lambdas in the second error pass
1032 with self.assertRaisesRegex(SyntaxError, "invalid syntax"):
1033 compile("lambda name_3=f'{name_4}': {name_3}\n1 $ 1", "<string>", "exec")
1034
1035 # but don't emit the paren warning in general cases
1036 with self.assertRaisesRegex(SyntaxError, "f-string: expecting a valid expression after '{'"):
1037 eval("f'{+ lambda:None}'")
1038
1039 def test_valid_prefixes(self):
1040 self.assertEqual(F'{1}', "1")
1041 self.assertEqual(FR'{2}', "2")
1042 self.assertEqual(fR'{3}', "3")
1043
1044 def test_roundtrip_raw_quotes(self):
1045 self.assertEqual(fr"\'", "\\'")
1046 self.assertEqual(fr'\"', '\\"')
1047 self.assertEqual(fr'\"\'', '\\"\\\'')
1048 self.assertEqual(fr'\'\"', '\\\'\\"')
1049 self.assertEqual(fr'\"\'\"', '\\"\\\'\\"')
1050 self.assertEqual(fr'\'\"\'', '\\\'\\"\\\'')
1051 self.assertEqual(fr'\"\'\"\'', '\\"\\\'\\"\\\'')
1052
1053 def test_fstring_backslash_before_double_bracket(self):
1054 deprecated_cases = [
1055 (r"f'\{{\}}'", '\\{\\}'),
1056 (r"f'\{{'", '\\{'),
1057 (r"f'\{{{1+1}'", '\\{2'),
1058 (r"f'\}}{1+1}'", '\\}2'),
1059 (r"f'{1+1}\}}'", '2\\}')
1060 ]
1061 for case, expected_result in deprecated_cases:
1062 with self.subTest(case=case, expected_result=expected_result):
1063 with self.assertWarns(SyntaxWarning):
1064 result = eval(case)
1065 self.assertEqual(result, expected_result)
1066 self.assertEqual(fr'\{{\}}', '\\{\\}')
1067 self.assertEqual(fr'\{{', '\\{')
1068 self.assertEqual(fr'\{{{1+1}', '\\{2')
1069 self.assertEqual(fr'\}}{1+1}', '\\}2')
1070 self.assertEqual(fr'{1+1}\}}', '2\\}')
1071
1072 def test_fstring_backslash_before_double_bracket_warns_once(self):
1073 with self.assertWarns(SyntaxWarning) as w:
1074 eval(r"f'\{{'")
1075 self.assertEqual(len(w.warnings), 1)
1076 self.assertEqual(w.warnings[0].category, SyntaxWarning)
1077
1078 def test_fstring_backslash_prefix_raw(self):
1079 self.assertEqual(f'\\', '\\')
1080 self.assertEqual(f'\\\\', '\\\\')
1081 self.assertEqual(fr'\\', r'\\')
1082 self.assertEqual(fr'\\\\', r'\\\\')
1083 self.assertEqual(rf'\\', r'\\')
1084 self.assertEqual(rf'\\\\', r'\\\\')
1085 self.assertEqual(Rf'\\', R'\\')
1086 self.assertEqual(Rf'\\\\', R'\\\\')
1087 self.assertEqual(fR'\\', R'\\')
1088 self.assertEqual(fR'\\\\', R'\\\\')
1089 self.assertEqual(FR'\\', R'\\')
1090 self.assertEqual(FR'\\\\', R'\\\\')
1091
1092 def test_fstring_format_spec_greedy_matching(self):
1093 self.assertEqual(f"{1:}}}", "1}")
1094 self.assertEqual(f"{1:>3{5}}}}", " 1}")
1095
1096 def test_yield(self):
1097 # Not terribly useful, but make sure the yield turns
1098 # a function into a generator
1099 def fn(y):
1100 f'y:{yield y*2}'
1101 f'{yield}'
1102
1103 g = fn(4)
1104 self.assertEqual(next(g), 8)
1105 self.assertEqual(next(g), None)
1106
1107 def test_yield_send(self):
1108 def fn(x):
1109 yield f'x:{yield (lambda i: x * i)}'
1110
1111 g = fn(10)
1112 the_lambda = next(g)
1113 self.assertEqual(the_lambda(4), 40)
1114 self.assertEqual(g.send('string'), 'x:string')
1115
1116 def test_expressions_with_triple_quoted_strings(self):
1117 self.assertEqual(f"{'''x'''}", 'x')
1118 self.assertEqual(f"{'''eric's'''}", "eric's")
1119
1120 # Test concatenation within an expression
1121 self.assertEqual(f'{"x" """eric"s""" "y"}', 'xeric"sy')
1122 self.assertEqual(f'{"x" """eric"s"""}', 'xeric"s')
1123 self.assertEqual(f'{"""eric"s""" "y"}', 'eric"sy')
1124 self.assertEqual(f'{"""x""" """eric"s""" "y"}', 'xeric"sy')
1125 self.assertEqual(f'{"""x""" """eric"s""" """y"""}', 'xeric"sy')
1126 self.assertEqual(f'{r"""x""" """eric"s""" """y"""}', 'xeric"sy')
1127
1128 def test_multiple_vars(self):
1129 x = 98
1130 y = 'abc'
1131 self.assertEqual(f'{x}{y}', '98abc')
1132
1133 self.assertEqual(f'X{x}{y}', 'X98abc')
1134 self.assertEqual(f'{x}X{y}', '98Xabc')
1135 self.assertEqual(f'{x}{y}X', '98abcX')
1136
1137 self.assertEqual(f'X{x}Y{y}', 'X98Yabc')
1138 self.assertEqual(f'X{x}{y}Y', 'X98abcY')
1139 self.assertEqual(f'{x}X{y}Y', '98XabcY')
1140
1141 self.assertEqual(f'X{x}Y{y}Z', 'X98YabcZ')
1142
1143 def test_closure(self):
1144 def outer(x):
1145 def inner():
1146 return f'x:{x}'
1147 return inner
1148
1149 self.assertEqual(outer('987')(), 'x:987')
1150 self.assertEqual(outer(7)(), 'x:7')
1151
1152 def test_arguments(self):
1153 y = 2
1154 def f(x, width):
1155 return f'x={x*y:{width}}'
1156
1157 self.assertEqual(f('foo', 10), 'x=foofoo ')
1158 x = 'bar'
1159 self.assertEqual(f(10, 10), 'x= 20')
1160
1161 def test_locals(self):
1162 value = 123
1163 self.assertEqual(f'v:{value}', 'v:123')
1164
1165 def test_missing_variable(self):
1166 with self.assertRaises(NameError):
1167 f'v:{value}'
1168
1169 def test_missing_format_spec(self):
1170 class ESC[4;38;5;81mO:
1171 def __format__(self, spec):
1172 if not spec:
1173 return '*'
1174 return spec
1175
1176 self.assertEqual(f'{O():x}', 'x')
1177 self.assertEqual(f'{O()}', '*')
1178 self.assertEqual(f'{O():}', '*')
1179
1180 self.assertEqual(f'{3:}', '3')
1181 self.assertEqual(f'{3!s:}', '3')
1182
1183 def test_global(self):
1184 self.assertEqual(f'g:{a_global}', 'g:global variable')
1185 self.assertEqual(f'g:{a_global!r}', "g:'global variable'")
1186
1187 a_local = 'local variable'
1188 self.assertEqual(f'g:{a_global} l:{a_local}',
1189 'g:global variable l:local variable')
1190 self.assertEqual(f'g:{a_global!r}',
1191 "g:'global variable'")
1192 self.assertEqual(f'g:{a_global} l:{a_local!r}',
1193 "g:global variable l:'local variable'")
1194
1195 self.assertIn("module 'unittest' from", f'{unittest}')
1196
1197 def test_shadowed_global(self):
1198 a_global = 'really a local'
1199 self.assertEqual(f'g:{a_global}', 'g:really a local')
1200 self.assertEqual(f'g:{a_global!r}', "g:'really a local'")
1201
1202 a_local = 'local variable'
1203 self.assertEqual(f'g:{a_global} l:{a_local}',
1204 'g:really a local l:local variable')
1205 self.assertEqual(f'g:{a_global!r}',
1206 "g:'really a local'")
1207 self.assertEqual(f'g:{a_global} l:{a_local!r}',
1208 "g:really a local l:'local variable'")
1209
1210 def test_call(self):
1211 def foo(x):
1212 return 'x=' + str(x)
1213
1214 self.assertEqual(f'{foo(10)}', 'x=10')
1215
1216 def test_nested_fstrings(self):
1217 y = 5
1218 self.assertEqual(f'{f"{0}"*3}', '000')
1219 self.assertEqual(f'{f"{y}"*3}', '555')
1220
1221 def test_invalid_string_prefixes(self):
1222 single_quote_cases = ["fu''",
1223 "uf''",
1224 "Fu''",
1225 "fU''",
1226 "Uf''",
1227 "uF''",
1228 "ufr''",
1229 "urf''",
1230 "fur''",
1231 "fru''",
1232 "rfu''",
1233 "ruf''",
1234 "FUR''",
1235 "Fur''",
1236 "fb''",
1237 "fB''",
1238 "Fb''",
1239 "FB''",
1240 "bf''",
1241 "bF''",
1242 "Bf''",
1243 "BF''",]
1244 double_quote_cases = [case.replace("'", '"') for case in single_quote_cases]
1245 self.assertAllRaise(SyntaxError, 'invalid syntax',
1246 single_quote_cases + double_quote_cases)
1247
1248 def test_leading_trailing_spaces(self):
1249 self.assertEqual(f'{ 3}', '3')
1250 self.assertEqual(f'{ 3}', '3')
1251 self.assertEqual(f'{3 }', '3')
1252 self.assertEqual(f'{3 }', '3')
1253
1254 self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]}}',
1255 'expr={1: 2}')
1256 self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]} }',
1257 'expr={1: 2}')
1258
1259 def test_not_equal(self):
1260 # There's a special test for this because there's a special
1261 # case in the f-string parser to look for != as not ending an
1262 # expression. Normally it would, while looking for !s or !r.
1263
1264 self.assertEqual(f'{3!=4}', 'True')
1265 self.assertEqual(f'{3!=4:}', 'True')
1266 self.assertEqual(f'{3!=4!s}', 'True')
1267 self.assertEqual(f'{3!=4!s:.3}', 'Tru')
1268
1269 def test_equal_equal(self):
1270 # Because an expression ending in = has special meaning,
1271 # there's a special test for ==. Make sure it works.
1272
1273 self.assertEqual(f'{0==1}', 'False')
1274
1275 def test_conversions(self):
1276 self.assertEqual(f'{3.14:10.10}', ' 3.14')
1277 self.assertEqual(f'{3.14!s:10.10}', '3.14 ')
1278 self.assertEqual(f'{3.14!r:10.10}', '3.14 ')
1279 self.assertEqual(f'{3.14!a:10.10}', '3.14 ')
1280
1281 self.assertEqual(f'{"a"}', 'a')
1282 self.assertEqual(f'{"a"!r}', "'a'")
1283 self.assertEqual(f'{"a"!a}', "'a'")
1284
1285 # Conversions can have trailing whitespace after them since it
1286 # does not provide any significance
1287 self.assertEqual(f"{3!s }", "3")
1288 self.assertEqual(f'{3.14!s :10.10}', '3.14 ')
1289
1290 # Not a conversion.
1291 self.assertEqual(f'{"a!r"}', "a!r")
1292
1293 # Not a conversion, but show that ! is allowed in a format spec.
1294 self.assertEqual(f'{3.14:!<10.10}', '3.14!!!!!!')
1295
1296 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
1297 ["f'{3!'",
1298 "f'{3!s'",
1299 "f'{3!g'",
1300 ])
1301
1302 self.assertAllRaise(SyntaxError, 'f-string: missing conversion character',
1303 ["f'{3!}'",
1304 "f'{3!:'",
1305 "f'{3!:}'",
1306 ])
1307
1308 for conv_identifier in 'g', 'A', 'G', 'ä', 'ɐ':
1309 self.assertAllRaise(SyntaxError,
1310 "f-string: invalid conversion character %r: "
1311 "expected 's', 'r', or 'a'" % conv_identifier,
1312 ["f'{3!" + conv_identifier + "}'"])
1313
1314 for conv_non_identifier in '3', '!':
1315 self.assertAllRaise(SyntaxError,
1316 "f-string: invalid conversion character",
1317 ["f'{3!" + conv_non_identifier + "}'"])
1318
1319 for conv in ' s', ' s ':
1320 self.assertAllRaise(SyntaxError,
1321 "f-string: conversion type must come right after the"
1322 " exclamanation mark",
1323 ["f'{3!" + conv + "}'"])
1324
1325 self.assertAllRaise(SyntaxError,
1326 "f-string: invalid conversion character 'ss': "
1327 "expected 's', 'r', or 'a'",
1328 ["f'{3!ss}'",
1329 "f'{3!ss:}'",
1330 "f'{3!ss:s}'",
1331 ])
1332
1333 def test_assignment(self):
1334 self.assertAllRaise(SyntaxError, r'invalid syntax',
1335 ["f'' = 3",
1336 "f'{0}' = x",
1337 "f'{x}' = x",
1338 ])
1339
1340 def test_del(self):
1341 self.assertAllRaise(SyntaxError, 'invalid syntax',
1342 ["del f''",
1343 "del '' f''",
1344 ])
1345
1346 def test_mismatched_braces(self):
1347 self.assertAllRaise(SyntaxError, "f-string: single '}' is not allowed",
1348 ["f'{{}'",
1349 "f'{{}}}'",
1350 "f'}'",
1351 "f'x}'",
1352 "f'x}x'",
1353 r"f'\u007b}'",
1354
1355 # Can't have { or } in a format spec.
1356 "f'{3:}>10}'",
1357 "f'{3:}}>10}'",
1358 ])
1359
1360 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
1361 ["f'{3'",
1362 "f'{3!'",
1363 "f'{3:'",
1364 "f'{3!s'",
1365 "f'{3!s:'",
1366 "f'{3!s:3'",
1367 "f'x{'",
1368 "f'x{x'",
1369 "f'{x'",
1370 "f'{3:s'",
1371 "f'{{{'",
1372 "f'{{}}{'",
1373 "f'{'",
1374 "f'{i='", # See gh-93418.
1375 ])
1376
1377 self.assertAllRaise(SyntaxError,
1378 "f-string: expecting a valid expression after '{'",
1379 ["f'{3:{{>10}'",
1380 ])
1381
1382 # But these are just normal strings.
1383 self.assertEqual(f'{"{"}', '{')
1384 self.assertEqual(f'{"}"}', '}')
1385 self.assertEqual(f'{3:{"}"}>10}', '}}}}}}}}}3')
1386 self.assertEqual(f'{2:{"{"}>10}', '{{{{{{{{{2')
1387
1388 def test_if_conditional(self):
1389 # There's special logic in compile.c to test if the
1390 # conditional for an if (and while) are constants. Exercise
1391 # that code.
1392
1393 def test_fstring(x, expected):
1394 flag = 0
1395 if f'{x}':
1396 flag = 1
1397 else:
1398 flag = 2
1399 self.assertEqual(flag, expected)
1400
1401 def test_concat_empty(x, expected):
1402 flag = 0
1403 if '' f'{x}':
1404 flag = 1
1405 else:
1406 flag = 2
1407 self.assertEqual(flag, expected)
1408
1409 def test_concat_non_empty(x, expected):
1410 flag = 0
1411 if ' ' f'{x}':
1412 flag = 1
1413 else:
1414 flag = 2
1415 self.assertEqual(flag, expected)
1416
1417 test_fstring('', 2)
1418 test_fstring(' ', 1)
1419
1420 test_concat_empty('', 2)
1421 test_concat_empty(' ', 1)
1422
1423 test_concat_non_empty('', 1)
1424 test_concat_non_empty(' ', 1)
1425
1426 def test_empty_format_specifier(self):
1427 x = 'test'
1428 self.assertEqual(f'{x}', 'test')
1429 self.assertEqual(f'{x:}', 'test')
1430 self.assertEqual(f'{x!s:}', 'test')
1431 self.assertEqual(f'{x!r:}', "'test'")
1432
1433 def test_str_format_differences(self):
1434 d = {'a': 'string',
1435 0: 'integer',
1436 }
1437 a = 0
1438 self.assertEqual(f'{d[0]}', 'integer')
1439 self.assertEqual(f'{d["a"]}', 'string')
1440 self.assertEqual(f'{d[a]}', 'integer')
1441 self.assertEqual('{d[a]}'.format(d=d), 'string')
1442 self.assertEqual('{d[0]}'.format(d=d), 'integer')
1443
1444 def test_errors(self):
1445 # see issue 26287
1446 self.assertAllRaise(TypeError, 'unsupported',
1447 [r"f'{(lambda: 0):x}'",
1448 r"f'{(0,):x}'",
1449 ])
1450 self.assertAllRaise(ValueError, 'Unknown format code',
1451 [r"f'{1000:j}'",
1452 r"f'{1000:j}'",
1453 ])
1454
1455 def test_filename_in_syntaxerror(self):
1456 # see issue 38964
1457 with temp_cwd() as cwd:
1458 file_path = os.path.join(cwd, 't.py')
1459 with open(file_path, 'w', encoding="utf-8") as f:
1460 f.write('f"{a b}"') # This generates a SyntaxError
1461 _, _, stderr = assert_python_failure(file_path,
1462 PYTHONIOENCODING='ascii')
1463 self.assertIn(file_path.encode('ascii', 'backslashreplace'), stderr)
1464
1465 def test_loop(self):
1466 for i in range(1000):
1467 self.assertEqual(f'i:{i}', 'i:' + str(i))
1468
1469 def test_dict(self):
1470 d = {'"': 'dquote',
1471 "'": 'squote',
1472 'foo': 'bar',
1473 }
1474 self.assertEqual(f'''{d["'"]}''', 'squote')
1475 self.assertEqual(f"""{d['"']}""", 'dquote')
1476
1477 self.assertEqual(f'{d["foo"]}', 'bar')
1478 self.assertEqual(f"{d['foo']}", 'bar')
1479
1480 def test_backslash_char(self):
1481 # Check eval of a backslash followed by a control char.
1482 # See bpo-30682: this used to raise an assert in pydebug mode.
1483 self.assertEqual(eval('f"\\\n"'), '')
1484 self.assertEqual(eval('f"\\\r"'), '')
1485
1486 def test_debug_conversion(self):
1487 x = 'A string'
1488 self.assertEqual(f'{x=}', 'x=' + repr(x))
1489 self.assertEqual(f'{x =}', 'x =' + repr(x))
1490 self.assertEqual(f'{x=!s}', 'x=' + str(x))
1491 self.assertEqual(f'{x=!r}', 'x=' + repr(x))
1492 self.assertEqual(f'{x=!a}', 'x=' + ascii(x))
1493
1494 x = 2.71828
1495 self.assertEqual(f'{x=:.2f}', 'x=' + format(x, '.2f'))
1496 self.assertEqual(f'{x=:}', 'x=' + format(x, ''))
1497 self.assertEqual(f'{x=!r:^20}', 'x=' + format(repr(x), '^20'))
1498 self.assertEqual(f'{x=!s:^20}', 'x=' + format(str(x), '^20'))
1499 self.assertEqual(f'{x=!a:^20}', 'x=' + format(ascii(x), '^20'))
1500
1501 x = 9
1502 self.assertEqual(f'{3*x+15=}', '3*x+15=42')
1503
1504 # There is code in ast.c that deals with non-ascii expression values. So,
1505 # use a unicode identifier to trigger that.
1506 tenπ = 31.4
1507 self.assertEqual(f'{tenπ=:.2f}', 'tenπ=31.40')
1508
1509 # Also test with Unicode in non-identifiers.
1510 self.assertEqual(f'{"Σ"=}', '"Σ"=\'Σ\'')
1511
1512 # Make sure nested fstrings still work.
1513 self.assertEqual(f'{f"{3.1415=:.1f}":*^20}', '*****3.1415=3.1*****')
1514
1515 # Make sure text before and after an expression with = works
1516 # correctly.
1517 pi = 'π'
1518 self.assertEqual(f'alpha α {pi=} ω omega', "alpha α pi='π' ω omega")
1519
1520 # Check multi-line expressions.
1521 self.assertEqual(f'''{
1522 3
1523 =}''', '\n3\n=3')
1524
1525 # Since = is handled specially, make sure all existing uses of
1526 # it still work.
1527
1528 self.assertEqual(f'{0==1}', 'False')
1529 self.assertEqual(f'{0!=1}', 'True')
1530 self.assertEqual(f'{0<=1}', 'True')
1531 self.assertEqual(f'{0>=1}', 'False')
1532 self.assertEqual(f'{(x:="5")}', '5')
1533 self.assertEqual(x, '5')
1534 self.assertEqual(f'{(x:=5)}', '5')
1535 self.assertEqual(x, 5)
1536 self.assertEqual(f'{"="}', '=')
1537
1538 x = 20
1539 # This isn't an assignment expression, it's 'x', with a format
1540 # spec of '=10'. See test_walrus: you need to use parens.
1541 self.assertEqual(f'{x:=10}', ' 20')
1542
1543 # Test named function parameters, to make sure '=' parsing works
1544 # there.
1545 def f(a):
1546 nonlocal x
1547 oldx = x
1548 x = a
1549 return oldx
1550 x = 0
1551 self.assertEqual(f'{f(a="3=")}', '0')
1552 self.assertEqual(x, '3=')
1553 self.assertEqual(f'{f(a=4)}', '3=')
1554 self.assertEqual(x, 4)
1555
1556 # Make sure __format__ is being called.
1557 class ESC[4;38;5;81mC:
1558 def __format__(self, s):
1559 return f'FORMAT-{s}'
1560 def __repr__(self):
1561 return 'REPR'
1562
1563 self.assertEqual(f'{C()=}', 'C()=REPR')
1564 self.assertEqual(f'{C()=!r}', 'C()=REPR')
1565 self.assertEqual(f'{C()=:}', 'C()=FORMAT-')
1566 self.assertEqual(f'{C()=: }', 'C()=FORMAT- ')
1567 self.assertEqual(f'{C()=:x}', 'C()=FORMAT-x')
1568 self.assertEqual(f'{C()=!r:*^20}', 'C()=********REPR********')
1569
1570 self.assertRaises(SyntaxError, eval, "f'{C=]'")
1571
1572 # Make sure leading and following text works.
1573 x = 'foo'
1574 self.assertEqual(f'X{x=}Y', 'Xx='+repr(x)+'Y')
1575
1576 # Make sure whitespace around the = works.
1577 self.assertEqual(f'X{x =}Y', 'Xx ='+repr(x)+'Y')
1578 self.assertEqual(f'X{x= }Y', 'Xx= '+repr(x)+'Y')
1579 self.assertEqual(f'X{x = }Y', 'Xx = '+repr(x)+'Y')
1580 self.assertEqual(f"sadsd {1 + 1 = :{1 + 1:1d}f}", "sadsd 1 + 1 = 2.000000")
1581
1582 # These next lines contains tabs. Backslash escapes don't
1583 # work in f-strings.
1584 # patchcheck doesn't like these tabs. So the only way to test
1585 # this will be to dynamically created and exec the f-strings. But
1586 # that's such a hassle I'll save it for another day. For now, convert
1587 # the tabs to spaces just to shut up patchcheck.
1588 #self.assertEqual(f'X{x =}Y', 'Xx\t='+repr(x)+'Y')
1589 #self.assertEqual(f'X{x = }Y', 'Xx\t=\t'+repr(x)+'Y')
1590
1591 def test_walrus(self):
1592 x = 20
1593 # This isn't an assignment expression, it's 'x', with a format
1594 # spec of '=10'.
1595 self.assertEqual(f'{x:=10}', ' 20')
1596
1597 # This is an assignment expression, which requires parens.
1598 self.assertEqual(f'{(x:=10)}', '10')
1599 self.assertEqual(x, 10)
1600
1601 def test_invalid_syntax_error_message(self):
1602 with self.assertRaisesRegex(SyntaxError,
1603 "f-string: expecting '=', or '!', or ':', or '}'"):
1604 compile("f'{a $ b}'", "?", "exec")
1605
1606 def test_with_two_commas_in_format_specifier(self):
1607 error_msg = re.escape("Cannot specify ',' with ','.")
1608 with self.assertRaisesRegex(ValueError, error_msg):
1609 f'{1:,,}'
1610
1611 def test_with_two_underscore_in_format_specifier(self):
1612 error_msg = re.escape("Cannot specify '_' with '_'.")
1613 with self.assertRaisesRegex(ValueError, error_msg):
1614 f'{1:__}'
1615
1616 def test_with_a_commas_and_an_underscore_in_format_specifier(self):
1617 error_msg = re.escape("Cannot specify both ',' and '_'.")
1618 with self.assertRaisesRegex(ValueError, error_msg):
1619 f'{1:,_}'
1620
1621 def test_with_an_underscore_and_a_comma_in_format_specifier(self):
1622 error_msg = re.escape("Cannot specify both ',' and '_'.")
1623 with self.assertRaisesRegex(ValueError, error_msg):
1624 f'{1:_,}'
1625
1626 def test_syntax_error_for_starred_expressions(self):
1627 with self.assertRaisesRegex(SyntaxError, "can't use starred expression here"):
1628 compile("f'{*a}'", "?", "exec")
1629
1630 with self.assertRaisesRegex(SyntaxError,
1631 "f-string: expecting a valid expression after '{'"):
1632 compile("f'{**a}'", "?", "exec")
1633
1634 def test_not_closing_quotes(self):
1635 self.assertAllRaise(SyntaxError, "unterminated f-string literal", ['f"', "f'"])
1636 self.assertAllRaise(SyntaxError, "unterminated triple-quoted f-string literal",
1637 ['f"""', "f'''"])
1638 # Ensure that the errors are reported at the correct line number.
1639 data = '''\
1640 x = 1 + 1
1641 y = 2 + 2
1642 z = f"""
1643 sdfjnsdfjsdf
1644 sdfsdfs{1+
1645 2} dfigdf {3+
1646 4}sdufsd""
1647 '''
1648 try:
1649 compile(data, "?", "exec")
1650 except SyntaxError as e:
1651 self.assertEqual(e.text, 'z = f"""')
1652 self.assertEqual(e.lineno, 3)
1653 def test_syntax_error_after_debug(self):
1654 self.assertAllRaise(SyntaxError, "f-string: expecting a valid expression after '{'",
1655 [
1656 "f'{1=}{;'",
1657 "f'{1=}{+;'",
1658 "f'{1=}{2}{;'",
1659 "f'{1=}{3}{;'",
1660 ])
1661 self.assertAllRaise(SyntaxError, "f-string: expecting '=', or '!', or ':', or '}'",
1662 [
1663 "f'{1=}{1;'",
1664 "f'{1=}{1;}'",
1665 ])
1666
1667 def test_debug_in_file(self):
1668 with temp_cwd():
1669 script = 'script.py'
1670 with open('script.py', 'w') as f:
1671 f.write(f"""\
1672 print(f'''{{
1673 3
1674 =}}''')""")
1675
1676 _, stdout, _ = assert_python_ok(script)
1677 self.assertEqual(stdout.decode('utf-8').strip().replace('\r\n', '\n').replace('\r', '\n'),
1678 "3\n=3")
1679
1680 def test_syntax_warning_infinite_recursion_in_file(self):
1681 with temp_cwd():
1682 script = 'script.py'
1683 with open(script, 'w') as f:
1684 f.write(r"print(f'\{1}')")
1685
1686 _, stdout, stderr = assert_python_ok(script)
1687 self.assertIn(rb'\1', stdout)
1688 self.assertEqual(len(stderr.strip().splitlines()), 2)
1689
1690 if __name__ == '__main__':
1691 unittest.main()