blob: e0812d13cdaf5c891aba2e76585737e3e45d7626 [file] [log] [blame]
Eric V. Smith9a4135e2019-05-08 16:28:48 -04001# -*- 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
Eric V. Smith235a6f02015-09-19 14:51:32 -040010import ast
Lysandros Nikolaou791a46e2020-05-26 04:24:31 +030011import os
Eric V. Smith235a6f02015-09-19 14:51:32 -040012import types
13import decimal
14import unittest
Lysandros Nikolaou791a46e2020-05-26 04:24:31 +030015from test.support import temp_cwd, use_old_parser
16from test.support.script_helper import assert_python_failure
Eric V. Smith235a6f02015-09-19 14:51:32 -040017
18a_global = 'global variable'
19
20# You could argue that I'm too strict in looking for specific error
21# values with assertRaisesRegex, but without it it's way too easy to
22# make a syntax error in the test strings. Especially with all of the
23# triple quotes, raw strings, backslashes, etc. I think it's a
24# worthwhile tradeoff. When I switched to this method, I found many
25# examples where I wasn't testing what I thought I was.
26
27class TestCase(unittest.TestCase):
28 def assertAllRaise(self, exception_type, regex, error_strings):
29 for str in error_strings:
30 with self.subTest(str=str):
31 with self.assertRaisesRegex(exception_type, regex):
32 eval(str)
33
34 def test__format__lookup(self):
35 # Make sure __format__ is looked up on the type, not the instance.
36 class X:
37 def __format__(self, spec):
38 return 'class'
39
40 x = X()
41
42 # Add a bound __format__ method to the 'y' instance, but not
43 # the 'x' instance.
44 y = X()
45 y.__format__ = types.MethodType(lambda self, spec: 'instance', y)
46
47 self.assertEqual(f'{y}', format(y))
48 self.assertEqual(f'{y}', 'class')
49 self.assertEqual(format(x), format(y))
50
51 # __format__ is not called this way, but still make sure it
52 # returns what we expect (so we can make sure we're bypassing
53 # it).
54 self.assertEqual(x.__format__(''), 'class')
55 self.assertEqual(y.__format__(''), 'instance')
56
57 # This is how __format__ is actually called.
58 self.assertEqual(type(x).__format__(x, ''), 'class')
59 self.assertEqual(type(y).__format__(y, ''), 'class')
60
61 def test_ast(self):
62 # Inspired by http://bugs.python.org/issue24975
63 class X:
64 def __init__(self):
65 self.called = False
66 def __call__(self):
67 self.called = True
68 return 4
69 x = X()
70 expr = """
71a = 10
72f'{a * x()}'"""
73 t = ast.parse(expr)
74 c = compile(t, '', 'exec')
75
76 # Make sure x was not called.
77 self.assertFalse(x.called)
78
79 # Actually run the code.
80 exec(c)
81
82 # Make sure x was called.
83 self.assertTrue(x.called)
84
Łukasz Langae7c566c2017-09-06 17:27:58 -070085 def test_ast_line_numbers(self):
86 expr = """
87a = 10
88f'{a * x()}'"""
89 t = ast.parse(expr)
90 self.assertEqual(type(t), ast.Module)
91 self.assertEqual(len(t.body), 2)
92 # check `a = 10`
93 self.assertEqual(type(t.body[0]), ast.Assign)
94 self.assertEqual(t.body[0].lineno, 2)
95 # check `f'...'`
96 self.assertEqual(type(t.body[1]), ast.Expr)
97 self.assertEqual(type(t.body[1].value), ast.JoinedStr)
98 self.assertEqual(len(t.body[1].value.values), 1)
99 self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)
100 self.assertEqual(t.body[1].lineno, 3)
101 self.assertEqual(t.body[1].value.lineno, 3)
102 self.assertEqual(t.body[1].value.values[0].lineno, 3)
103 # check the binop location
104 binop = t.body[1].value.values[0].value
105 self.assertEqual(type(binop), ast.BinOp)
106 self.assertEqual(type(binop.left), ast.Name)
107 self.assertEqual(type(binop.op), ast.Mult)
108 self.assertEqual(type(binop.right), ast.Call)
109 self.assertEqual(binop.lineno, 3)
110 self.assertEqual(binop.left.lineno, 3)
111 self.assertEqual(binop.right.lineno, 3)
112 self.assertEqual(binop.col_offset, 3)
113 self.assertEqual(binop.left.col_offset, 3)
114 self.assertEqual(binop.right.col_offset, 7)
115
116 def test_ast_line_numbers_multiple_formattedvalues(self):
117 expr = """
118f'no formatted values'
119f'eggs {a * x()} spam {b + y()}'"""
120 t = ast.parse(expr)
121 self.assertEqual(type(t), ast.Module)
122 self.assertEqual(len(t.body), 2)
123 # check `f'no formatted value'`
124 self.assertEqual(type(t.body[0]), ast.Expr)
125 self.assertEqual(type(t.body[0].value), ast.JoinedStr)
126 self.assertEqual(t.body[0].lineno, 2)
127 # check `f'...'`
128 self.assertEqual(type(t.body[1]), ast.Expr)
129 self.assertEqual(type(t.body[1].value), ast.JoinedStr)
130 self.assertEqual(len(t.body[1].value.values), 4)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300131 self.assertEqual(type(t.body[1].value.values[0]), ast.Constant)
132 self.assertEqual(type(t.body[1].value.values[0].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700133 self.assertEqual(type(t.body[1].value.values[1]), ast.FormattedValue)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300134 self.assertEqual(type(t.body[1].value.values[2]), ast.Constant)
135 self.assertEqual(type(t.body[1].value.values[2].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700136 self.assertEqual(type(t.body[1].value.values[3]), ast.FormattedValue)
137 self.assertEqual(t.body[1].lineno, 3)
138 self.assertEqual(t.body[1].value.lineno, 3)
139 self.assertEqual(t.body[1].value.values[0].lineno, 3)
140 self.assertEqual(t.body[1].value.values[1].lineno, 3)
141 self.assertEqual(t.body[1].value.values[2].lineno, 3)
142 self.assertEqual(t.body[1].value.values[3].lineno, 3)
143 # check the first binop location
144 binop1 = t.body[1].value.values[1].value
145 self.assertEqual(type(binop1), ast.BinOp)
146 self.assertEqual(type(binop1.left), ast.Name)
147 self.assertEqual(type(binop1.op), ast.Mult)
148 self.assertEqual(type(binop1.right), ast.Call)
149 self.assertEqual(binop1.lineno, 3)
150 self.assertEqual(binop1.left.lineno, 3)
151 self.assertEqual(binop1.right.lineno, 3)
152 self.assertEqual(binop1.col_offset, 8)
153 self.assertEqual(binop1.left.col_offset, 8)
154 self.assertEqual(binop1.right.col_offset, 12)
155 # check the second binop location
156 binop2 = t.body[1].value.values[3].value
157 self.assertEqual(type(binop2), ast.BinOp)
158 self.assertEqual(type(binop2.left), ast.Name)
159 self.assertEqual(type(binop2.op), ast.Add)
160 self.assertEqual(type(binop2.right), ast.Call)
161 self.assertEqual(binop2.lineno, 3)
162 self.assertEqual(binop2.left.lineno, 3)
163 self.assertEqual(binop2.right.lineno, 3)
164 self.assertEqual(binop2.col_offset, 23)
165 self.assertEqual(binop2.left.col_offset, 23)
166 self.assertEqual(binop2.right.col_offset, 27)
167
168 def test_ast_line_numbers_nested(self):
169 expr = """
170a = 10
171f'{a * f"-{x()}-"}'"""
172 t = ast.parse(expr)
173 self.assertEqual(type(t), ast.Module)
174 self.assertEqual(len(t.body), 2)
175 # check `a = 10`
176 self.assertEqual(type(t.body[0]), ast.Assign)
177 self.assertEqual(t.body[0].lineno, 2)
178 # check `f'...'`
179 self.assertEqual(type(t.body[1]), ast.Expr)
180 self.assertEqual(type(t.body[1].value), ast.JoinedStr)
181 self.assertEqual(len(t.body[1].value.values), 1)
182 self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)
183 self.assertEqual(t.body[1].lineno, 3)
184 self.assertEqual(t.body[1].value.lineno, 3)
185 self.assertEqual(t.body[1].value.values[0].lineno, 3)
186 # check the binop location
187 binop = t.body[1].value.values[0].value
188 self.assertEqual(type(binop), ast.BinOp)
189 self.assertEqual(type(binop.left), ast.Name)
190 self.assertEqual(type(binop.op), ast.Mult)
191 self.assertEqual(type(binop.right), ast.JoinedStr)
192 self.assertEqual(binop.lineno, 3)
193 self.assertEqual(binop.left.lineno, 3)
194 self.assertEqual(binop.right.lineno, 3)
195 self.assertEqual(binop.col_offset, 3)
196 self.assertEqual(binop.left.col_offset, 3)
197 self.assertEqual(binop.right.col_offset, 7)
198 # check the nested call location
199 self.assertEqual(len(binop.right.values), 3)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300200 self.assertEqual(type(binop.right.values[0]), ast.Constant)
201 self.assertEqual(type(binop.right.values[0].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700202 self.assertEqual(type(binop.right.values[1]), ast.FormattedValue)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300203 self.assertEqual(type(binop.right.values[2]), ast.Constant)
204 self.assertEqual(type(binop.right.values[2].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700205 self.assertEqual(binop.right.values[0].lineno, 3)
206 self.assertEqual(binop.right.values[1].lineno, 3)
207 self.assertEqual(binop.right.values[2].lineno, 3)
208 call = binop.right.values[1].value
209 self.assertEqual(type(call), ast.Call)
210 self.assertEqual(call.lineno, 3)
Lysandros Nikolaou37af21b2020-04-29 03:43:50 +0300211 self.assertEqual(call.col_offset, 11)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700212
213 def test_ast_line_numbers_duplicate_expression(self):
214 """Duplicate expression
215
216 NOTE: this is currently broken, always sets location of the first
217 expression.
218 """
219 expr = """
220a = 10
221f'{a * x()} {a * x()} {a * x()}'
222"""
223 t = ast.parse(expr)
224 self.assertEqual(type(t), ast.Module)
225 self.assertEqual(len(t.body), 2)
226 # check `a = 10`
227 self.assertEqual(type(t.body[0]), ast.Assign)
228 self.assertEqual(t.body[0].lineno, 2)
229 # check `f'...'`
230 self.assertEqual(type(t.body[1]), ast.Expr)
231 self.assertEqual(type(t.body[1].value), ast.JoinedStr)
232 self.assertEqual(len(t.body[1].value.values), 5)
233 self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300234 self.assertEqual(type(t.body[1].value.values[1]), ast.Constant)
235 self.assertEqual(type(t.body[1].value.values[1].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700236 self.assertEqual(type(t.body[1].value.values[2]), ast.FormattedValue)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300237 self.assertEqual(type(t.body[1].value.values[3]), ast.Constant)
238 self.assertEqual(type(t.body[1].value.values[3].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700239 self.assertEqual(type(t.body[1].value.values[4]), ast.FormattedValue)
240 self.assertEqual(t.body[1].lineno, 3)
241 self.assertEqual(t.body[1].value.lineno, 3)
242 self.assertEqual(t.body[1].value.values[0].lineno, 3)
243 self.assertEqual(t.body[1].value.values[1].lineno, 3)
244 self.assertEqual(t.body[1].value.values[2].lineno, 3)
245 self.assertEqual(t.body[1].value.values[3].lineno, 3)
246 self.assertEqual(t.body[1].value.values[4].lineno, 3)
247 # check the first binop location
248 binop = t.body[1].value.values[0].value
249 self.assertEqual(type(binop), ast.BinOp)
250 self.assertEqual(type(binop.left), ast.Name)
251 self.assertEqual(type(binop.op), ast.Mult)
252 self.assertEqual(type(binop.right), ast.Call)
253 self.assertEqual(binop.lineno, 3)
254 self.assertEqual(binop.left.lineno, 3)
255 self.assertEqual(binop.right.lineno, 3)
256 self.assertEqual(binop.col_offset, 3)
257 self.assertEqual(binop.left.col_offset, 3)
258 self.assertEqual(binop.right.col_offset, 7)
259 # check the second binop location
260 binop = t.body[1].value.values[2].value
261 self.assertEqual(type(binop), ast.BinOp)
262 self.assertEqual(type(binop.left), ast.Name)
263 self.assertEqual(type(binop.op), ast.Mult)
264 self.assertEqual(type(binop.right), ast.Call)
265 self.assertEqual(binop.lineno, 3)
266 self.assertEqual(binop.left.lineno, 3)
267 self.assertEqual(binop.right.lineno, 3)
268 self.assertEqual(binop.col_offset, 3) # FIXME: this is wrong
269 self.assertEqual(binop.left.col_offset, 3) # FIXME: this is wrong
270 self.assertEqual(binop.right.col_offset, 7) # FIXME: this is wrong
271 # check the third binop location
272 binop = t.body[1].value.values[4].value
273 self.assertEqual(type(binop), ast.BinOp)
274 self.assertEqual(type(binop.left), ast.Name)
275 self.assertEqual(type(binop.op), ast.Mult)
276 self.assertEqual(type(binop.right), ast.Call)
277 self.assertEqual(binop.lineno, 3)
278 self.assertEqual(binop.left.lineno, 3)
279 self.assertEqual(binop.right.lineno, 3)
280 self.assertEqual(binop.col_offset, 3) # FIXME: this is wrong
281 self.assertEqual(binop.left.col_offset, 3) # FIXME: this is wrong
282 self.assertEqual(binop.right.col_offset, 7) # FIXME: this is wrong
283
284 def test_ast_line_numbers_multiline_fstring(self):
Anthony Sottile995d9b92019-01-12 20:05:13 -0800285 # See bpo-30465 for details.
Łukasz Langae7c566c2017-09-06 17:27:58 -0700286 expr = """
287a = 10
288f'''
289 {a
290 *
291 x()}
292non-important content
293'''
294"""
295 t = ast.parse(expr)
296 self.assertEqual(type(t), ast.Module)
297 self.assertEqual(len(t.body), 2)
298 # check `a = 10`
299 self.assertEqual(type(t.body[0]), ast.Assign)
300 self.assertEqual(t.body[0].lineno, 2)
301 # check `f'...'`
302 self.assertEqual(type(t.body[1]), ast.Expr)
303 self.assertEqual(type(t.body[1].value), ast.JoinedStr)
304 self.assertEqual(len(t.body[1].value.values), 3)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300305 self.assertEqual(type(t.body[1].value.values[0]), ast.Constant)
306 self.assertEqual(type(t.body[1].value.values[0].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700307 self.assertEqual(type(t.body[1].value.values[1]), ast.FormattedValue)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300308 self.assertEqual(type(t.body[1].value.values[2]), ast.Constant)
309 self.assertEqual(type(t.body[1].value.values[2].value), str)
Anthony Sottile995d9b92019-01-12 20:05:13 -0800310 self.assertEqual(t.body[1].lineno, 3)
311 self.assertEqual(t.body[1].value.lineno, 3)
312 self.assertEqual(t.body[1].value.values[0].lineno, 3)
313 self.assertEqual(t.body[1].value.values[1].lineno, 3)
314 self.assertEqual(t.body[1].value.values[2].lineno, 3)
315 self.assertEqual(t.body[1].col_offset, 0)
316 self.assertEqual(t.body[1].value.col_offset, 0)
317 self.assertEqual(t.body[1].value.values[0].col_offset, 0)
318 self.assertEqual(t.body[1].value.values[1].col_offset, 0)
319 self.assertEqual(t.body[1].value.values[2].col_offset, 0)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700320 # NOTE: the following lineno information and col_offset is correct for
321 # expressions within FormattedValues.
322 binop = t.body[1].value.values[1].value
323 self.assertEqual(type(binop), ast.BinOp)
324 self.assertEqual(type(binop.left), ast.Name)
325 self.assertEqual(type(binop.op), ast.Mult)
326 self.assertEqual(type(binop.right), ast.Call)
327 self.assertEqual(binop.lineno, 4)
328 self.assertEqual(binop.left.lineno, 4)
329 self.assertEqual(binop.right.lineno, 6)
Anthony Sottile995d9b92019-01-12 20:05:13 -0800330 self.assertEqual(binop.col_offset, 4)
331 self.assertEqual(binop.left.col_offset, 4)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700332 self.assertEqual(binop.right.col_offset, 7)
333
Serhiy Storchaka4cc30ae2016-12-11 19:37:19 +0200334 def test_docstring(self):
335 def f():
336 f'''Not a docstring'''
337 self.assertIsNone(f.__doc__)
338 def g():
339 '''Not a docstring''' \
340 f''
341 self.assertIsNone(g.__doc__)
342
Eric V. Smith235a6f02015-09-19 14:51:32 -0400343 def test_literal_eval(self):
Eric V. Smith235a6f02015-09-19 14:51:32 -0400344 with self.assertRaisesRegex(ValueError, 'malformed node or string'):
Serhiy Storchaka4cc30ae2016-12-11 19:37:19 +0200345 ast.literal_eval("f'x'")
Eric V. Smith235a6f02015-09-19 14:51:32 -0400346
347 def test_ast_compile_time_concat(self):
348 x = ['']
349
350 expr = """x[0] = 'foo' f'{3}'"""
351 t = ast.parse(expr)
352 c = compile(t, '', 'exec')
353 exec(c)
354 self.assertEqual(x[0], 'foo3')
355
Eric V. Smith9b88fdf2016-11-07 17:54:01 -0500356 def test_compile_time_concat_errors(self):
357 self.assertAllRaise(SyntaxError,
358 'cannot mix bytes and nonbytes literals',
359 [r"""f'' b''""",
360 r"""b'' f''""",
361 ])
362
Eric V. Smith235a6f02015-09-19 14:51:32 -0400363 def test_literal(self):
364 self.assertEqual(f'', '')
365 self.assertEqual(f'a', 'a')
366 self.assertEqual(f' ', ' ')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400367
368 def test_unterminated_string(self):
369 self.assertAllRaise(SyntaxError, 'f-string: unterminated string',
370 [r"""f'{"x'""",
371 r"""f'{"x}'""",
372 r"""f'{("x'""",
373 r"""f'{("x}'""",
374 ])
375
376 def test_mismatched_parens(self):
Serhiy Storchaka58159ef2019-01-12 09:46:50 +0200377 self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\}' "
378 r"does not match opening parenthesis '\('",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400379 ["f'{((}'",
380 ])
Serhiy Storchaka58159ef2019-01-12 09:46:50 +0200381 self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\)' "
382 r"does not match opening parenthesis '\['",
383 ["f'{a[4)}'",
384 ])
385 self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\]' "
386 r"does not match opening parenthesis '\('",
387 ["f'{a(4]}'",
388 ])
389 self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\}' "
390 r"does not match opening parenthesis '\['",
391 ["f'{a[4}'",
392 ])
393 self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\}' "
394 r"does not match opening parenthesis '\('",
395 ["f'{a(4}'",
396 ])
397 self.assertRaises(SyntaxError, eval, "f'{" + "("*500 + "}'")
Eric V. Smith235a6f02015-09-19 14:51:32 -0400398
399 def test_double_braces(self):
400 self.assertEqual(f'{{', '{')
401 self.assertEqual(f'a{{', 'a{')
402 self.assertEqual(f'{{b', '{b')
403 self.assertEqual(f'a{{b', 'a{b')
404 self.assertEqual(f'}}', '}')
405 self.assertEqual(f'a}}', 'a}')
406 self.assertEqual(f'}}b', '}b')
407 self.assertEqual(f'a}}b', 'a}b')
Eric V. Smith451d0e32016-09-09 21:56:20 -0400408 self.assertEqual(f'{{}}', '{}')
409 self.assertEqual(f'a{{}}', 'a{}')
410 self.assertEqual(f'{{b}}', '{b}')
411 self.assertEqual(f'{{}}c', '{}c')
412 self.assertEqual(f'a{{b}}', 'a{b}')
413 self.assertEqual(f'a{{}}c', 'a{}c')
414 self.assertEqual(f'{{b}}c', '{b}c')
415 self.assertEqual(f'a{{b}}c', 'a{b}c')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400416
417 self.assertEqual(f'{{{10}', '{10')
418 self.assertEqual(f'}}{10}', '}10')
419 self.assertEqual(f'}}{{{10}', '}{10')
420 self.assertEqual(f'}}a{{{10}', '}a{10')
421
422 self.assertEqual(f'{10}{{', '10{')
423 self.assertEqual(f'{10}}}', '10}')
424 self.assertEqual(f'{10}}}{{', '10}{')
425 self.assertEqual(f'{10}}}a{{' '}', '10}a{}')
426
427 # Inside of strings, don't interpret doubled brackets.
428 self.assertEqual(f'{"{{}}"}', '{{}}')
429
430 self.assertAllRaise(TypeError, 'unhashable type',
431 ["f'{ {{}} }'", # dict in a set
432 ])
433
434 def test_compile_time_concat(self):
435 x = 'def'
436 self.assertEqual('abc' f'## {x}ghi', 'abc## defghi')
437 self.assertEqual('abc' f'{x}' 'ghi', 'abcdefghi')
438 self.assertEqual('abc' f'{x}' 'gh' f'i{x:4}', 'abcdefghidef ')
439 self.assertEqual('{x}' f'{x}', '{x}def')
440 self.assertEqual('{x' f'{x}', '{xdef')
441 self.assertEqual('{x}' f'{x}', '{x}def')
442 self.assertEqual('{{x}}' f'{x}', '{{x}}def')
443 self.assertEqual('{{x' f'{x}', '{{xdef')
444 self.assertEqual('x}}' f'{x}', 'x}}def')
445 self.assertEqual(f'{x}' 'x}}', 'defx}}')
446 self.assertEqual(f'{x}' '', 'def')
447 self.assertEqual('' f'{x}' '', 'def')
448 self.assertEqual('' f'{x}', 'def')
449 self.assertEqual(f'{x}' '2', 'def2')
450 self.assertEqual('1' f'{x}' '2', '1def2')
451 self.assertEqual('1' f'{x}', '1def')
452 self.assertEqual(f'{x}' f'-{x}', 'def-def')
453 self.assertEqual('' f'', '')
454 self.assertEqual('' f'' '', '')
455 self.assertEqual('' f'' '' f'', '')
456 self.assertEqual(f'', '')
457 self.assertEqual(f'' '', '')
458 self.assertEqual(f'' '' f'', '')
459 self.assertEqual(f'' '' f'' '', '')
460
461 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
462 ["f'{3' f'}'", # can't concat to get a valid f-string
463 ])
464
465 def test_comments(self):
466 # These aren't comments, since they're in strings.
467 d = {'#': 'hash'}
468 self.assertEqual(f'{"#"}', '#')
469 self.assertEqual(f'{d["#"]}', 'hash')
470
Eric V. Smith09835dc2016-09-11 18:58:20 -0400471 self.assertAllRaise(SyntaxError, "f-string expression part cannot include '#'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400472 ["f'{1#}'", # error because the expression becomes "(1#)"
473 "f'{3(#)}'",
Eric V. Smith09835dc2016-09-11 18:58:20 -0400474 "f'{#}'",
Serhiy Storchaka58159ef2019-01-12 09:46:50 +0200475 ])
476 self.assertAllRaise(SyntaxError, r"f-string: unmatched '\)'",
477 ["f'{)#}'", # When wrapped in parens, this becomes
Eric V. Smith35a24c52016-09-11 19:01:22 -0400478 # '()#)'. Make sure that doesn't compile.
Eric V. Smith235a6f02015-09-19 14:51:32 -0400479 ])
480
481 def test_many_expressions(self):
482 # Create a string with many expressions in it. Note that
483 # because we have a space in here as a literal, we're actually
484 # going to use twice as many ast nodes: one for each literal
485 # plus one for each expression.
486 def build_fstr(n, extra=''):
487 return "f'" + ('{x} ' * n) + extra + "'"
488
489 x = 'X'
490 width = 1
491
492 # Test around 256.
493 for i in range(250, 260):
494 self.assertEqual(eval(build_fstr(i)), (x+' ')*i)
495
496 # Test concatenating 2 largs fstrings.
497 self.assertEqual(eval(build_fstr(255)*256), (x+' ')*(255*256))
498
499 s = build_fstr(253, '{x:{width}} ')
500 self.assertEqual(eval(s), (x+' ')*254)
501
502 # Test lots of expressions and constants, concatenated.
503 s = "f'{1}' 'x' 'y'" * 1024
504 self.assertEqual(eval(s), '1xy' * 1024)
505
506 def test_format_specifier_expressions(self):
507 width = 10
508 precision = 4
509 value = decimal.Decimal('12.34567')
510 self.assertEqual(f'result: {value:{width}.{precision}}', 'result: 12.35')
511 self.assertEqual(f'result: {value:{width!r}.{precision}}', 'result: 12.35')
512 self.assertEqual(f'result: {value:{width:0}.{precision:1}}', 'result: 12.35')
513 self.assertEqual(f'result: {value:{1}{0:0}.{precision:1}}', 'result: 12.35')
514 self.assertEqual(f'result: {value:{ 1}{ 0:0}.{ precision:1}}', 'result: 12.35')
515 self.assertEqual(f'{10:#{1}0x}', ' 0xa')
516 self.assertEqual(f'{10:{"#"}1{0}{"x"}}', ' 0xa')
517 self.assertEqual(f'{-10:-{"#"}1{0}x}', ' -0xa')
518 self.assertEqual(f'{-10:{"-"}#{1}0{"x"}}', ' -0xa')
519 self.assertEqual(f'{10:#{3 != {4:5} and width}x}', ' 0xa')
520
521 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
522 ["""f'{"s"!r{":10"}}'""",
523
524 # This looks like a nested format spec.
525 ])
526
Lysandros Nikolaou990efe02020-06-29 15:55:57 +0300527 err_msg = "invalid syntax" if use_old_parser() else "f-string: invalid syntax"
528 self.assertAllRaise(SyntaxError, err_msg,
Martin Panter263893c2016-07-28 01:25:31 +0000529 [# Invalid syntax inside a nested spec.
Eric V. Smith235a6f02015-09-19 14:51:32 -0400530 "f'{4:{/5}}'",
531 ])
532
533 self.assertAllRaise(SyntaxError, "f-string: expressions nested too deeply",
534 [# Can't nest format specifiers.
535 "f'result: {value:{width:{0}}.{precision:1}}'",
536 ])
537
538 self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character',
539 [# No expansion inside conversion or for
540 # the : or ! itself.
541 """f'{"s"!{"r"}}'""",
542 ])
543
544 def test_side_effect_order(self):
545 class X:
546 def __init__(self):
547 self.i = 0
548 def __format__(self, spec):
549 self.i += 1
550 return str(self.i)
551
552 x = X()
553 self.assertEqual(f'{x} {x}', '1 2')
554
555 def test_missing_expression(self):
556 self.assertAllRaise(SyntaxError, 'f-string: empty expression not allowed',
557 ["f'{}'",
558 "f'{ }'"
559 "f' {} '",
560 "f'{!r}'",
561 "f'{ !r}'",
562 "f'{10:{ }}'",
563 "f' { } '",
Eric V. Smith1d44c412015-09-23 07:49:00 -0400564
Serhiy Storchaka2e9cd582017-06-08 23:43:54 +0300565 # The Python parser ignores also the following
566 # whitespace characters in additional to a space.
567 "f'''{\t\f\r\n}'''",
568
Eric V. Smith1d44c412015-09-23 07:49:00 -0400569 # Catch the empty expression before the
570 # invalid conversion.
571 "f'{!x}'",
572 "f'{ !xr}'",
573 "f'{!x:}'",
574 "f'{!x:a}'",
575 "f'{ !xr:}'",
576 "f'{ !xr:a}'",
Eric V. Smith548c4d32015-09-23 08:00:01 -0400577
578 "f'{!}'",
579 "f'{:}'",
Eric V. Smithb2080f62015-09-23 10:24:43 -0400580
581 # We find the empty expression before the
582 # missing closing brace.
583 "f'{!'",
584 "f'{!s:'",
585 "f'{:'",
586 "f'{:x'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400587 ])
588
Serhiy Storchaka2e9cd582017-06-08 23:43:54 +0300589 # Different error message is raised for other whitespace characters.
Serhiy Storchaka74ea6b52020-05-12 12:42:04 +0300590 self.assertAllRaise(SyntaxError, r"invalid non-printable character U\+00A0",
Serhiy Storchaka2e9cd582017-06-08 23:43:54 +0300591 ["f'''{\xa0}'''",
592 "\xa0",
593 ])
594
Eric V. Smith235a6f02015-09-19 14:51:32 -0400595 def test_parens_in_expressions(self):
596 self.assertEqual(f'{3,}', '(3,)')
597
598 # Add these because when an expression is evaluated, parens
599 # are added around it. But we shouldn't go from an invalid
600 # expression to a valid one. The added parens are just
601 # supposed to allow whitespace (including newlines).
Lysandros Nikolaou990efe02020-06-29 15:55:57 +0300602 err_msg = "invalid syntax" if use_old_parser() else "f-string: invalid syntax"
603 self.assertAllRaise(SyntaxError, err_msg,
Eric V. Smith235a6f02015-09-19 14:51:32 -0400604 ["f'{,}'",
605 "f'{,}'", # this is (,), which is an error
606 ])
607
Serhiy Storchaka58159ef2019-01-12 09:46:50 +0200608 self.assertAllRaise(SyntaxError, r"f-string: unmatched '\)'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400609 ["f'{3)+(4}'",
610 ])
611
612 self.assertAllRaise(SyntaxError, 'EOL while scanning string literal',
613 ["f'{\n}'",
614 ])
615
Eric V. Smith451d0e32016-09-09 21:56:20 -0400616 def test_backslashes_in_string_part(self):
617 self.assertEqual(f'\t', '\t')
618 self.assertEqual(r'\t', '\\t')
619 self.assertEqual(rf'\t', '\\t')
620 self.assertEqual(f'{2}\t', '2\t')
621 self.assertEqual(f'{2}\t{3}', '2\t3')
622 self.assertEqual(f'\t{3}', '\t3')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400623
Eric V. Smith451d0e32016-09-09 21:56:20 -0400624 self.assertEqual(f'\u0394', '\u0394')
625 self.assertEqual(r'\u0394', '\\u0394')
626 self.assertEqual(rf'\u0394', '\\u0394')
627 self.assertEqual(f'{2}\u0394', '2\u0394')
628 self.assertEqual(f'{2}\u0394{3}', '2\u03943')
629 self.assertEqual(f'\u0394{3}', '\u03943')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400630
Eric V. Smith451d0e32016-09-09 21:56:20 -0400631 self.assertEqual(f'\U00000394', '\u0394')
632 self.assertEqual(r'\U00000394', '\\U00000394')
633 self.assertEqual(rf'\U00000394', '\\U00000394')
634 self.assertEqual(f'{2}\U00000394', '2\u0394')
635 self.assertEqual(f'{2}\U00000394{3}', '2\u03943')
636 self.assertEqual(f'\U00000394{3}', '\u03943')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400637
Eric V. Smith451d0e32016-09-09 21:56:20 -0400638 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}', '\u0394')
639 self.assertEqual(f'{2}\N{GREEK CAPITAL LETTER DELTA}', '2\u0394')
640 self.assertEqual(f'{2}\N{GREEK CAPITAL LETTER DELTA}{3}', '2\u03943')
641 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}{3}', '\u03943')
642 self.assertEqual(f'2\N{GREEK CAPITAL LETTER DELTA}', '2\u0394')
643 self.assertEqual(f'2\N{GREEK CAPITAL LETTER DELTA}3', '2\u03943')
644 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}3', '\u03943')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400645
Eric V. Smith451d0e32016-09-09 21:56:20 -0400646 self.assertEqual(f'\x20', ' ')
647 self.assertEqual(r'\x20', '\\x20')
648 self.assertEqual(rf'\x20', '\\x20')
649 self.assertEqual(f'{2}\x20', '2 ')
650 self.assertEqual(f'{2}\x20{3}', '2 3')
651 self.assertEqual(f'\x20{3}', ' 3')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400652
Eric V. Smith451d0e32016-09-09 21:56:20 -0400653 self.assertEqual(f'2\x20', '2 ')
654 self.assertEqual(f'2\x203', '2 3')
655 self.assertEqual(f'\x203', ' 3')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400656
Gregory P. Smithb4be87a2019-08-10 00:19:07 -0700657 with self.assertWarns(DeprecationWarning): # invalid escape sequence
Serhiy Storchaka0cd7a3f2017-05-25 13:33:55 +0300658 value = eval(r"f'\{6*7}'")
659 self.assertEqual(value, '\\42')
660 self.assertEqual(f'\\{6*7}', '\\42')
661 self.assertEqual(fr'\{6*7}', '\\42')
662
663 AMPERSAND = 'spam'
664 # Get the right unicode character (&), or pick up local variable
665 # depending on the number of backslashes.
666 self.assertEqual(f'\N{AMPERSAND}', '&')
667 self.assertEqual(f'\\N{AMPERSAND}', '\\Nspam')
668 self.assertEqual(fr'\N{AMPERSAND}', '\\Nspam')
669 self.assertEqual(f'\\\N{AMPERSAND}', '\\&')
670
Eric V. Smith451d0e32016-09-09 21:56:20 -0400671 def test_misformed_unicode_character_name(self):
672 # These test are needed because unicode names are parsed
673 # differently inside f-strings.
674 self.assertAllRaise(SyntaxError, r"\(unicode error\) 'unicodeescape' codec can't decode bytes in position .*: malformed \\N character escape",
675 [r"f'\N'",
676 r"f'\N{'",
677 r"f'\N{GREEK CAPITAL LETTER DELTA'",
678
679 # Here are the non-f-string versions,
680 # which should give the same errors.
681 r"'\N'",
682 r"'\N{'",
683 r"'\N{GREEK CAPITAL LETTER DELTA'",
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400684 ])
685
Eric V. Smith451d0e32016-09-09 21:56:20 -0400686 def test_no_backslashes_in_expression_part(self):
687 self.assertAllRaise(SyntaxError, 'f-string expression part cannot include a backslash',
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400688 [r"f'{\'a\'}'",
689 r"f'{\t3}'",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400690 r"f'{\}'",
691 r"rf'{\'a\'}'",
692 r"rf'{\t3}'",
693 r"rf'{\}'",
694 r"""rf'{"\N{LEFT CURLY BRACKET}"}'""",
Jason R. Coombs45cab8c2016-11-06 11:01:08 -0500695 r"f'{\n}'",
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400696 ])
697
Eric V. Smith451d0e32016-09-09 21:56:20 -0400698 def test_no_escapes_for_braces(self):
Jason R. Coombs1c92a762016-11-06 11:25:54 -0500699 """
700 Only literal curly braces begin an expression.
701 """
702 # \x7b is '{'.
703 self.assertEqual(f'\x7b1+1}}', '{1+1}')
704 self.assertEqual(f'\x7b1+1', '{1+1')
705 self.assertEqual(f'\u007b1+1', '{1+1')
706 self.assertEqual(f'\N{LEFT CURLY BRACKET}1+1\N{RIGHT CURLY BRACKET}', '{1+1}')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400707
Eric V. Smith235a6f02015-09-19 14:51:32 -0400708 def test_newlines_in_expressions(self):
709 self.assertEqual(f'{0}', '0')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400710 self.assertEqual(rf'''{3+
7114}''', '7')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400712
713 def test_lambda(self):
714 x = 5
715 self.assertEqual(f'{(lambda y:x*y)("8")!r}', "'88888'")
716 self.assertEqual(f'{(lambda y:x*y)("8")!r:10}', "'88888' ")
717 self.assertEqual(f'{(lambda y:x*y)("8"):10}', "88888 ")
718
719 # lambda doesn't work without parens, because the colon
720 # makes the parser think it's a format_spec
Lysandros Nikolaou990efe02020-06-29 15:55:57 +0300721 err_msg = "invalid syntax" if use_old_parser() else "f-string: invalid syntax"
722 self.assertAllRaise(SyntaxError, err_msg,
Eric V. Smith235a6f02015-09-19 14:51:32 -0400723 ["f'{lambda x:x}'",
724 ])
725
726 def test_yield(self):
727 # Not terribly useful, but make sure the yield turns
728 # a function into a generator
729 def fn(y):
730 f'y:{yield y*2}'
Miss Islington (bot)64409112020-06-07 18:08:53 -0700731 f'{yield}'
Eric V. Smith235a6f02015-09-19 14:51:32 -0400732
733 g = fn(4)
734 self.assertEqual(next(g), 8)
Miss Islington (bot)64409112020-06-07 18:08:53 -0700735 self.assertEqual(next(g), None)
Eric V. Smith235a6f02015-09-19 14:51:32 -0400736
737 def test_yield_send(self):
738 def fn(x):
739 yield f'x:{yield (lambda i: x * i)}'
740
741 g = fn(10)
742 the_lambda = next(g)
743 self.assertEqual(the_lambda(4), 40)
744 self.assertEqual(g.send('string'), 'x:string')
745
746 def test_expressions_with_triple_quoted_strings(self):
747 self.assertEqual(f"{'''x'''}", 'x')
748 self.assertEqual(f"{'''eric's'''}", "eric's")
Eric V. Smith235a6f02015-09-19 14:51:32 -0400749
750 # Test concatenation within an expression
751 self.assertEqual(f'{"x" """eric"s""" "y"}', 'xeric"sy')
752 self.assertEqual(f'{"x" """eric"s"""}', 'xeric"s')
753 self.assertEqual(f'{"""eric"s""" "y"}', 'eric"sy')
754 self.assertEqual(f'{"""x""" """eric"s""" "y"}', 'xeric"sy')
755 self.assertEqual(f'{"""x""" """eric"s""" """y"""}', 'xeric"sy')
756 self.assertEqual(f'{r"""x""" """eric"s""" """y"""}', 'xeric"sy')
757
758 def test_multiple_vars(self):
759 x = 98
760 y = 'abc'
761 self.assertEqual(f'{x}{y}', '98abc')
762
763 self.assertEqual(f'X{x}{y}', 'X98abc')
764 self.assertEqual(f'{x}X{y}', '98Xabc')
765 self.assertEqual(f'{x}{y}X', '98abcX')
766
767 self.assertEqual(f'X{x}Y{y}', 'X98Yabc')
768 self.assertEqual(f'X{x}{y}Y', 'X98abcY')
769 self.assertEqual(f'{x}X{y}Y', '98XabcY')
770
771 self.assertEqual(f'X{x}Y{y}Z', 'X98YabcZ')
772
773 def test_closure(self):
774 def outer(x):
775 def inner():
776 return f'x:{x}'
777 return inner
778
779 self.assertEqual(outer('987')(), 'x:987')
780 self.assertEqual(outer(7)(), 'x:7')
781
782 def test_arguments(self):
783 y = 2
784 def f(x, width):
785 return f'x={x*y:{width}}'
786
787 self.assertEqual(f('foo', 10), 'x=foofoo ')
788 x = 'bar'
789 self.assertEqual(f(10, 10), 'x= 20')
790
791 def test_locals(self):
792 value = 123
793 self.assertEqual(f'v:{value}', 'v:123')
794
795 def test_missing_variable(self):
796 with self.assertRaises(NameError):
797 f'v:{value}'
798
799 def test_missing_format_spec(self):
800 class O:
801 def __format__(self, spec):
802 if not spec:
803 return '*'
804 return spec
805
806 self.assertEqual(f'{O():x}', 'x')
807 self.assertEqual(f'{O()}', '*')
808 self.assertEqual(f'{O():}', '*')
809
810 self.assertEqual(f'{3:}', '3')
811 self.assertEqual(f'{3!s:}', '3')
812
813 def test_global(self):
814 self.assertEqual(f'g:{a_global}', 'g:global variable')
815 self.assertEqual(f'g:{a_global!r}', "g:'global variable'")
816
817 a_local = 'local variable'
818 self.assertEqual(f'g:{a_global} l:{a_local}',
819 'g:global variable l:local variable')
820 self.assertEqual(f'g:{a_global!r}',
821 "g:'global variable'")
822 self.assertEqual(f'g:{a_global} l:{a_local!r}',
823 "g:global variable l:'local variable'")
824
825 self.assertIn("module 'unittest' from", f'{unittest}')
826
827 def test_shadowed_global(self):
828 a_global = 'really a local'
829 self.assertEqual(f'g:{a_global}', 'g:really a local')
830 self.assertEqual(f'g:{a_global!r}', "g:'really a local'")
831
832 a_local = 'local variable'
833 self.assertEqual(f'g:{a_global} l:{a_local}',
834 'g:really a local l:local variable')
835 self.assertEqual(f'g:{a_global!r}',
836 "g:'really a local'")
837 self.assertEqual(f'g:{a_global} l:{a_local!r}',
838 "g:really a local l:'local variable'")
839
840 def test_call(self):
841 def foo(x):
842 return 'x=' + str(x)
843
844 self.assertEqual(f'{foo(10)}', 'x=10')
845
846 def test_nested_fstrings(self):
847 y = 5
848 self.assertEqual(f'{f"{0}"*3}', '000')
849 self.assertEqual(f'{f"{y}"*3}', '555')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400850
851 def test_invalid_string_prefixes(self):
Pablo Galindo70c188e2020-04-13 02:47:35 +0100852 single_quote_cases = ["fu''",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400853 "uf''",
854 "Fu''",
855 "fU''",
856 "Uf''",
857 "uF''",
858 "ufr''",
859 "urf''",
860 "fur''",
861 "fru''",
862 "rfu''",
863 "ruf''",
864 "FUR''",
865 "Fur''",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400866 "fb''",
867 "fB''",
868 "Fb''",
869 "FB''",
870 "bf''",
871 "bF''",
872 "Bf''",
Pablo Galindo70c188e2020-04-13 02:47:35 +0100873 "BF''",]
874 double_quote_cases = [case.replace("'", '"') for case in single_quote_cases]
Lysandros Nikolaou6cb0ad22020-05-26 03:10:00 +0300875 error_msg = (
876 'invalid syntax'
877 if use_old_parser()
878 else 'unexpected EOF while parsing'
879 )
880 self.assertAllRaise(SyntaxError, error_msg,
Pablo Galindo70c188e2020-04-13 02:47:35 +0100881 single_quote_cases + double_quote_cases)
Eric V. Smith235a6f02015-09-19 14:51:32 -0400882
883 def test_leading_trailing_spaces(self):
884 self.assertEqual(f'{ 3}', '3')
885 self.assertEqual(f'{ 3}', '3')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400886 self.assertEqual(f'{3 }', '3')
887 self.assertEqual(f'{3 }', '3')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400888
889 self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]}}',
890 'expr={1: 2}')
891 self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]} }',
892 'expr={1: 2}')
893
Eric V. Smith235a6f02015-09-19 14:51:32 -0400894 def test_not_equal(self):
895 # There's a special test for this because there's a special
896 # case in the f-string parser to look for != as not ending an
897 # expression. Normally it would, while looking for !s or !r.
898
899 self.assertEqual(f'{3!=4}', 'True')
900 self.assertEqual(f'{3!=4:}', 'True')
901 self.assertEqual(f'{3!=4!s}', 'True')
902 self.assertEqual(f'{3!=4!s:.3}', 'Tru')
903
Eric V. Smith9a4135e2019-05-08 16:28:48 -0400904 def test_equal_equal(self):
905 # Because an expression ending in = has special meaning,
906 # there's a special test for ==. Make sure it works.
907
908 self.assertEqual(f'{0==1}', 'False')
909
Eric V. Smith235a6f02015-09-19 14:51:32 -0400910 def test_conversions(self):
911 self.assertEqual(f'{3.14:10.10}', ' 3.14')
912 self.assertEqual(f'{3.14!s:10.10}', '3.14 ')
913 self.assertEqual(f'{3.14!r:10.10}', '3.14 ')
914 self.assertEqual(f'{3.14!a:10.10}', '3.14 ')
915
916 self.assertEqual(f'{"a"}', 'a')
917 self.assertEqual(f'{"a"!r}', "'a'")
918 self.assertEqual(f'{"a"!a}', "'a'")
919
920 # Not a conversion.
921 self.assertEqual(f'{"a!r"}', "a!r")
922
923 # Not a conversion, but show that ! is allowed in a format spec.
924 self.assertEqual(f'{3.14:!<10.10}', '3.14!!!!!!')
925
Eric V. Smith235a6f02015-09-19 14:51:32 -0400926 self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character',
927 ["f'{3!g}'",
928 "f'{3!A}'",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400929 "f'{3!3}'",
930 "f'{3!G}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400931 "f'{3!!}'",
932 "f'{3!:}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400933 "f'{3! s}'", # no space before conversion char
Eric V. Smith235a6f02015-09-19 14:51:32 -0400934 ])
935
936 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
937 ["f'{x!s{y}}'",
938 "f'{3!ss}'",
939 "f'{3!ss:}'",
940 "f'{3!ss:s}'",
941 ])
942
943 def test_assignment(self):
944 self.assertAllRaise(SyntaxError, 'invalid syntax',
945 ["f'' = 3",
946 "f'{0}' = x",
947 "f'{x}' = x",
948 ])
949
950 def test_del(self):
951 self.assertAllRaise(SyntaxError, 'invalid syntax',
952 ["del f''",
953 "del '' f''",
954 ])
955
956 def test_mismatched_braces(self):
957 self.assertAllRaise(SyntaxError, "f-string: single '}' is not allowed",
958 ["f'{{}'",
959 "f'{{}}}'",
960 "f'}'",
961 "f'x}'",
962 "f'x}x'",
Jason R. Coombsda25abf72016-11-06 11:14:48 -0500963 r"f'\u007b}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400964
965 # Can't have { or } in a format spec.
966 "f'{3:}>10}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400967 "f'{3:}}>10}'",
968 ])
969
970 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
971 ["f'{3:{{>10}'",
972 "f'{3'",
973 "f'{3!'",
974 "f'{3:'",
975 "f'{3!s'",
976 "f'{3!s:'",
977 "f'{3!s:3'",
978 "f'x{'",
979 "f'x{x'",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400980 "f'{x'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400981 "f'{3:s'",
982 "f'{{{'",
983 "f'{{}}{'",
984 "f'{'",
985 ])
986
Eric V. Smith235a6f02015-09-19 14:51:32 -0400987 # But these are just normal strings.
988 self.assertEqual(f'{"{"}', '{')
989 self.assertEqual(f'{"}"}', '}')
990 self.assertEqual(f'{3:{"}"}>10}', '}}}}}}}}}3')
991 self.assertEqual(f'{2:{"{"}>10}', '{{{{{{{{{2')
992
993 def test_if_conditional(self):
994 # There's special logic in compile.c to test if the
995 # conditional for an if (and while) are constants. Exercise
996 # that code.
997
998 def test_fstring(x, expected):
999 flag = 0
1000 if f'{x}':
1001 flag = 1
1002 else:
1003 flag = 2
1004 self.assertEqual(flag, expected)
1005
1006 def test_concat_empty(x, expected):
1007 flag = 0
1008 if '' f'{x}':
1009 flag = 1
1010 else:
1011 flag = 2
1012 self.assertEqual(flag, expected)
1013
1014 def test_concat_non_empty(x, expected):
1015 flag = 0
1016 if ' ' f'{x}':
1017 flag = 1
1018 else:
1019 flag = 2
1020 self.assertEqual(flag, expected)
1021
1022 test_fstring('', 2)
1023 test_fstring(' ', 1)
1024
1025 test_concat_empty('', 2)
1026 test_concat_empty(' ', 1)
1027
1028 test_concat_non_empty('', 1)
1029 test_concat_non_empty(' ', 1)
1030
1031 def test_empty_format_specifier(self):
1032 x = 'test'
1033 self.assertEqual(f'{x}', 'test')
1034 self.assertEqual(f'{x:}', 'test')
1035 self.assertEqual(f'{x!s:}', 'test')
1036 self.assertEqual(f'{x!r:}', "'test'")
1037
1038 def test_str_format_differences(self):
1039 d = {'a': 'string',
1040 0: 'integer',
1041 }
1042 a = 0
1043 self.assertEqual(f'{d[0]}', 'integer')
1044 self.assertEqual(f'{d["a"]}', 'string')
1045 self.assertEqual(f'{d[a]}', 'integer')
1046 self.assertEqual('{d[a]}'.format(d=d), 'string')
1047 self.assertEqual('{d[0]}'.format(d=d), 'integer')
1048
Eric V. Smith135d5f42016-02-05 18:23:08 -05001049 def test_errors(self):
1050 # see issue 26287
Serhiy Storchaka13c8f322016-10-31 08:13:00 +02001051 self.assertAllRaise(TypeError, 'unsupported',
Eric V. Smith135d5f42016-02-05 18:23:08 -05001052 [r"f'{(lambda: 0):x}'",
1053 r"f'{(0,):x}'",
1054 ])
1055 self.assertAllRaise(ValueError, 'Unknown format code',
1056 [r"f'{1000:j}'",
1057 r"f'{1000:j}'",
1058 ])
1059
Lysandros Nikolaou791a46e2020-05-26 04:24:31 +03001060 @unittest.skipIf(use_old_parser(), "The old parser only supports <fstring> as the filename")
1061 def test_filename_in_syntaxerror(self):
1062 # see issue 38964
1063 with temp_cwd() as cwd:
1064 file_path = os.path.join(cwd, 't.py')
1065 with open(file_path, 'w') as f:
1066 f.write('f"{a b}"') # This generates a SyntaxError
Serhiy Storchakaf9254072020-06-25 20:39:12 +03001067 _, _, stderr = assert_python_failure(file_path,
1068 PYTHONIOENCODING='ascii')
1069 self.assertIn(file_path.encode('ascii', 'backslashreplace'), stderr)
Lysandros Nikolaou791a46e2020-05-26 04:24:31 +03001070
Eric V. Smith235a6f02015-09-19 14:51:32 -04001071 def test_loop(self):
1072 for i in range(1000):
1073 self.assertEqual(f'i:{i}', 'i:' + str(i))
1074
1075 def test_dict(self):
1076 d = {'"': 'dquote',
1077 "'": 'squote',
1078 'foo': 'bar',
1079 }
Eric V. Smith235a6f02015-09-19 14:51:32 -04001080 self.assertEqual(f'''{d["'"]}''', 'squote')
1081 self.assertEqual(f"""{d['"']}""", 'dquote')
1082
1083 self.assertEqual(f'{d["foo"]}', 'bar')
1084 self.assertEqual(f"{d['foo']}", 'bar')
Eric V. Smith235a6f02015-09-19 14:51:32 -04001085
ericvsmith11e97f22017-06-16 06:19:32 -04001086 def test_backslash_char(self):
1087 # Check eval of a backslash followed by a control char.
1088 # See bpo-30682: this used to raise an assert in pydebug mode.
1089 self.assertEqual(eval('f"\\\n"'), '')
1090 self.assertEqual(eval('f"\\\r"'), '')
1091
Eric V. Smith9a4135e2019-05-08 16:28:48 -04001092 def test_debug_conversion(self):
1093 x = 'A string'
1094 self.assertEqual(f'{x=}', 'x=' + repr(x))
1095 self.assertEqual(f'{x =}', 'x =' + repr(x))
1096 self.assertEqual(f'{x=!s}', 'x=' + str(x))
1097 self.assertEqual(f'{x=!r}', 'x=' + repr(x))
1098 self.assertEqual(f'{x=!a}', 'x=' + ascii(x))
1099
1100 x = 2.71828
1101 self.assertEqual(f'{x=:.2f}', 'x=' + format(x, '.2f'))
1102 self.assertEqual(f'{x=:}', 'x=' + format(x, ''))
1103 self.assertEqual(f'{x=!r:^20}', 'x=' + format(repr(x), '^20'))
1104 self.assertEqual(f'{x=!s:^20}', 'x=' + format(str(x), '^20'))
1105 self.assertEqual(f'{x=!a:^20}', 'x=' + format(ascii(x), '^20'))
1106
1107 x = 9
1108 self.assertEqual(f'{3*x+15=}', '3*x+15=42')
1109
1110 # There is code in ast.c that deals with non-ascii expression values. So,
1111 # use a unicode identifier to trigger that.
1112 tenπ = 31.4
1113 self.assertEqual(f'{tenπ=:.2f}', 'tenπ=31.40')
1114
1115 # Also test with Unicode in non-identifiers.
1116 self.assertEqual(f'{"Σ"=}', '"Σ"=\'Σ\'')
1117
1118 # Make sure nested fstrings still work.
1119 self.assertEqual(f'{f"{3.1415=:.1f}":*^20}', '*****3.1415=3.1*****')
1120
1121 # Make sure text before and after an expression with = works
1122 # correctly.
1123 pi = 'π'
1124 self.assertEqual(f'alpha α {pi=} ω omega', "alpha α pi='π' ω omega")
1125
1126 # Check multi-line expressions.
1127 self.assertEqual(f'''{
11283
1129=}''', '\n3\n=3')
1130
1131 # Since = is handled specially, make sure all existing uses of
1132 # it still work.
1133
1134 self.assertEqual(f'{0==1}', 'False')
1135 self.assertEqual(f'{0!=1}', 'True')
1136 self.assertEqual(f'{0<=1}', 'True')
1137 self.assertEqual(f'{0>=1}', 'False')
1138 self.assertEqual(f'{(x:="5")}', '5')
1139 self.assertEqual(x, '5')
1140 self.assertEqual(f'{(x:=5)}', '5')
1141 self.assertEqual(x, 5)
1142 self.assertEqual(f'{"="}', '=')
1143
1144 x = 20
1145 # This isn't an assignment expression, it's 'x', with a format
1146 # spec of '=10'. See test_walrus: you need to use parens.
1147 self.assertEqual(f'{x:=10}', ' 20')
1148
1149 # Test named function parameters, to make sure '=' parsing works
1150 # there.
1151 def f(a):
1152 nonlocal x
1153 oldx = x
1154 x = a
1155 return oldx
1156 x = 0
1157 self.assertEqual(f'{f(a="3=")}', '0')
1158 self.assertEqual(x, '3=')
1159 self.assertEqual(f'{f(a=4)}', '3=')
1160 self.assertEqual(x, 4)
1161
1162 # Make sure __format__ is being called.
1163 class C:
1164 def __format__(self, s):
1165 return f'FORMAT-{s}'
1166 def __repr__(self):
1167 return 'REPR'
1168
1169 self.assertEqual(f'{C()=}', 'C()=REPR')
1170 self.assertEqual(f'{C()=!r}', 'C()=REPR')
1171 self.assertEqual(f'{C()=:}', 'C()=FORMAT-')
1172 self.assertEqual(f'{C()=: }', 'C()=FORMAT- ')
1173 self.assertEqual(f'{C()=:x}', 'C()=FORMAT-x')
1174 self.assertEqual(f'{C()=!r:*^20}', 'C()=********REPR********')
1175
Pablo Galindo26f55c22019-05-12 01:43:04 +01001176 self.assertRaises(SyntaxError, eval, "f'{C=]'")
1177
Eric V. Smith6f6ff8a2019-05-27 15:31:52 -04001178 # Make sure leading and following text works.
1179 x = 'foo'
1180 self.assertEqual(f'X{x=}Y', 'Xx='+repr(x)+'Y')
1181
1182 # Make sure whitespace around the = works.
1183 self.assertEqual(f'X{x =}Y', 'Xx ='+repr(x)+'Y')
1184 self.assertEqual(f'X{x= }Y', 'Xx= '+repr(x)+'Y')
1185 self.assertEqual(f'X{x = }Y', 'Xx = '+repr(x)+'Y')
1186
1187 # These next lines contains tabs. Backslash escapes don't
1188 # work in f-strings.
Min ho Kim96e12d52019-07-22 06:12:33 +10001189 # patchcheck doesn't like these tabs. So the only way to test
Eric V. Smith6f6ff8a2019-05-27 15:31:52 -04001190 # this will be to dynamically created and exec the f-strings. But
1191 # that's such a hassle I'll save it for another day. For now, convert
1192 # the tabs to spaces just to shut up patchcheck.
1193 #self.assertEqual(f'X{x =}Y', 'Xx\t='+repr(x)+'Y')
1194 #self.assertEqual(f'X{x = }Y', 'Xx\t=\t'+repr(x)+'Y')
1195
Eric V. Smith9a4135e2019-05-08 16:28:48 -04001196 def test_walrus(self):
1197 x = 20
1198 # This isn't an assignment expression, it's 'x', with a format
1199 # spec of '=10'.
1200 self.assertEqual(f'{x:=10}', ' 20')
1201
1202 # This is an assignment expression, which requires parens.
1203 self.assertEqual(f'{(x:=10)}', '10')
1204 self.assertEqual(x, 10)
1205
Miss Islington (bot)cb0dc522020-06-27 12:43:49 -07001206 def test_invalid_syntax_error_message(self):
Lysandros Nikolaou990efe02020-06-29 15:55:57 +03001207 err_msg = "invalid syntax" if use_old_parser() else "f-string: invalid syntax"
1208 with self.assertRaisesRegex(SyntaxError, err_msg):
Miss Islington (bot)cb0dc522020-06-27 12:43:49 -07001209 compile("f'{a $ b}'", "?", "exec")
1210
Łukasz Langae7c566c2017-09-06 17:27:58 -07001211
Eric V. Smith235a6f02015-09-19 14:51:32 -04001212if __name__ == '__main__':
1213 unittest.main()