blob: 7ffe01d2d8c31e3dd7c95f3913f459a8b723ada9 [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 Nikolaouf7b1e462020-05-26 03:32:18 +030011import os
Eric V. Smith235a6f02015-09-19 14:51:32 -040012import types
13import decimal
14import unittest
Pablo Galindo1ed83ad2020-06-11 17:30:46 +010015from test.support import temp_cwd
Lysandros Nikolaouf7b1e462020-05-26 03:32:18 +030016from 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
527 self.assertAllRaise(SyntaxError, "invalid syntax",
Martin Panter263893c2016-07-28 01:25:31 +0000528 [# Invalid syntax inside a nested spec.
Eric V. Smith235a6f02015-09-19 14:51:32 -0400529 "f'{4:{/5}}'",
530 ])
531
532 self.assertAllRaise(SyntaxError, "f-string: expressions nested too deeply",
533 [# Can't nest format specifiers.
534 "f'result: {value:{width:{0}}.{precision:1}}'",
535 ])
536
537 self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character',
538 [# No expansion inside conversion or for
539 # the : or ! itself.
540 """f'{"s"!{"r"}}'""",
541 ])
542
543 def test_side_effect_order(self):
544 class X:
545 def __init__(self):
546 self.i = 0
547 def __format__(self, spec):
548 self.i += 1
549 return str(self.i)
550
551 x = X()
552 self.assertEqual(f'{x} {x}', '1 2')
553
554 def test_missing_expression(self):
555 self.assertAllRaise(SyntaxError, 'f-string: empty expression not allowed',
556 ["f'{}'",
557 "f'{ }'"
558 "f' {} '",
559 "f'{!r}'",
560 "f'{ !r}'",
561 "f'{10:{ }}'",
562 "f' { } '",
Eric V. Smith1d44c412015-09-23 07:49:00 -0400563
Serhiy Storchaka2e9cd582017-06-08 23:43:54 +0300564 # The Python parser ignores also the following
565 # whitespace characters in additional to a space.
566 "f'''{\t\f\r\n}'''",
567
Eric V. Smith1d44c412015-09-23 07:49:00 -0400568 # Catch the empty expression before the
569 # invalid conversion.
570 "f'{!x}'",
571 "f'{ !xr}'",
572 "f'{!x:}'",
573 "f'{!x:a}'",
574 "f'{ !xr:}'",
575 "f'{ !xr:a}'",
Eric V. Smith548c4d32015-09-23 08:00:01 -0400576
577 "f'{!}'",
578 "f'{:}'",
Eric V. Smithb2080f62015-09-23 10:24:43 -0400579
580 # We find the empty expression before the
581 # missing closing brace.
582 "f'{!'",
583 "f'{!s:'",
584 "f'{:'",
585 "f'{:x'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400586 ])
587
Serhiy Storchaka2e9cd582017-06-08 23:43:54 +0300588 # Different error message is raised for other whitespace characters.
Serhiy Storchaka74ea6b52020-05-12 12:42:04 +0300589 self.assertAllRaise(SyntaxError, r"invalid non-printable character U\+00A0",
Serhiy Storchaka2e9cd582017-06-08 23:43:54 +0300590 ["f'''{\xa0}'''",
591 "\xa0",
592 ])
593
Eric V. Smith235a6f02015-09-19 14:51:32 -0400594 def test_parens_in_expressions(self):
595 self.assertEqual(f'{3,}', '(3,)')
596
597 # Add these because when an expression is evaluated, parens
598 # are added around it. But we shouldn't go from an invalid
599 # expression to a valid one. The added parens are just
600 # supposed to allow whitespace (including newlines).
601 self.assertAllRaise(SyntaxError, 'invalid syntax',
602 ["f'{,}'",
603 "f'{,}'", # this is (,), which is an error
604 ])
605
Serhiy Storchaka58159ef2019-01-12 09:46:50 +0200606 self.assertAllRaise(SyntaxError, r"f-string: unmatched '\)'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400607 ["f'{3)+(4}'",
608 ])
609
610 self.assertAllRaise(SyntaxError, 'EOL while scanning string literal',
611 ["f'{\n}'",
612 ])
613
Eric V. Smith451d0e32016-09-09 21:56:20 -0400614 def test_backslashes_in_string_part(self):
615 self.assertEqual(f'\t', '\t')
616 self.assertEqual(r'\t', '\\t')
617 self.assertEqual(rf'\t', '\\t')
618 self.assertEqual(f'{2}\t', '2\t')
619 self.assertEqual(f'{2}\t{3}', '2\t3')
620 self.assertEqual(f'\t{3}', '\t3')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400621
Eric V. Smith451d0e32016-09-09 21:56:20 -0400622 self.assertEqual(f'\u0394', '\u0394')
623 self.assertEqual(r'\u0394', '\\u0394')
624 self.assertEqual(rf'\u0394', '\\u0394')
625 self.assertEqual(f'{2}\u0394', '2\u0394')
626 self.assertEqual(f'{2}\u0394{3}', '2\u03943')
627 self.assertEqual(f'\u0394{3}', '\u03943')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400628
Eric V. Smith451d0e32016-09-09 21:56:20 -0400629 self.assertEqual(f'\U00000394', '\u0394')
630 self.assertEqual(r'\U00000394', '\\U00000394')
631 self.assertEqual(rf'\U00000394', '\\U00000394')
632 self.assertEqual(f'{2}\U00000394', '2\u0394')
633 self.assertEqual(f'{2}\U00000394{3}', '2\u03943')
634 self.assertEqual(f'\U00000394{3}', '\u03943')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400635
Eric V. Smith451d0e32016-09-09 21:56:20 -0400636 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}', '\u0394')
637 self.assertEqual(f'{2}\N{GREEK CAPITAL LETTER DELTA}', '2\u0394')
638 self.assertEqual(f'{2}\N{GREEK CAPITAL LETTER DELTA}{3}', '2\u03943')
639 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}{3}', '\u03943')
640 self.assertEqual(f'2\N{GREEK CAPITAL LETTER DELTA}', '2\u0394')
641 self.assertEqual(f'2\N{GREEK CAPITAL LETTER DELTA}3', '2\u03943')
642 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}3', '\u03943')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400643
Eric V. Smith451d0e32016-09-09 21:56:20 -0400644 self.assertEqual(f'\x20', ' ')
645 self.assertEqual(r'\x20', '\\x20')
646 self.assertEqual(rf'\x20', '\\x20')
647 self.assertEqual(f'{2}\x20', '2 ')
648 self.assertEqual(f'{2}\x20{3}', '2 3')
649 self.assertEqual(f'\x20{3}', ' 3')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400650
Eric V. Smith451d0e32016-09-09 21:56:20 -0400651 self.assertEqual(f'2\x20', '2 ')
652 self.assertEqual(f'2\x203', '2 3')
653 self.assertEqual(f'\x203', ' 3')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400654
Gregory P. Smithb4be87a2019-08-10 00:19:07 -0700655 with self.assertWarns(DeprecationWarning): # invalid escape sequence
Serhiy Storchaka0cd7a3f2017-05-25 13:33:55 +0300656 value = eval(r"f'\{6*7}'")
657 self.assertEqual(value, '\\42')
658 self.assertEqual(f'\\{6*7}', '\\42')
659 self.assertEqual(fr'\{6*7}', '\\42')
660
661 AMPERSAND = 'spam'
662 # Get the right unicode character (&), or pick up local variable
663 # depending on the number of backslashes.
664 self.assertEqual(f'\N{AMPERSAND}', '&')
665 self.assertEqual(f'\\N{AMPERSAND}', '\\Nspam')
666 self.assertEqual(fr'\N{AMPERSAND}', '\\Nspam')
667 self.assertEqual(f'\\\N{AMPERSAND}', '\\&')
668
Eric V. Smith451d0e32016-09-09 21:56:20 -0400669 def test_misformed_unicode_character_name(self):
670 # These test are needed because unicode names are parsed
671 # differently inside f-strings.
672 self.assertAllRaise(SyntaxError, r"\(unicode error\) 'unicodeescape' codec can't decode bytes in position .*: malformed \\N character escape",
673 [r"f'\N'",
674 r"f'\N{'",
675 r"f'\N{GREEK CAPITAL LETTER DELTA'",
676
677 # Here are the non-f-string versions,
678 # which should give the same errors.
679 r"'\N'",
680 r"'\N{'",
681 r"'\N{GREEK CAPITAL LETTER DELTA'",
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400682 ])
683
Eric V. Smith451d0e32016-09-09 21:56:20 -0400684 def test_no_backslashes_in_expression_part(self):
685 self.assertAllRaise(SyntaxError, 'f-string expression part cannot include a backslash',
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400686 [r"f'{\'a\'}'",
687 r"f'{\t3}'",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400688 r"f'{\}'",
689 r"rf'{\'a\'}'",
690 r"rf'{\t3}'",
691 r"rf'{\}'",
692 r"""rf'{"\N{LEFT CURLY BRACKET}"}'""",
Jason R. Coombs45cab8c2016-11-06 11:01:08 -0500693 r"f'{\n}'",
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400694 ])
695
Eric V. Smith451d0e32016-09-09 21:56:20 -0400696 def test_no_escapes_for_braces(self):
Jason R. Coombs1c92a762016-11-06 11:25:54 -0500697 """
698 Only literal curly braces begin an expression.
699 """
700 # \x7b is '{'.
701 self.assertEqual(f'\x7b1+1}}', '{1+1}')
702 self.assertEqual(f'\x7b1+1', '{1+1')
703 self.assertEqual(f'\u007b1+1', '{1+1')
704 self.assertEqual(f'\N{LEFT CURLY BRACKET}1+1\N{RIGHT CURLY BRACKET}', '{1+1}')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400705
Eric V. Smith235a6f02015-09-19 14:51:32 -0400706 def test_newlines_in_expressions(self):
707 self.assertEqual(f'{0}', '0')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400708 self.assertEqual(rf'''{3+
7094}''', '7')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400710
711 def test_lambda(self):
712 x = 5
713 self.assertEqual(f'{(lambda y:x*y)("8")!r}', "'88888'")
714 self.assertEqual(f'{(lambda y:x*y)("8")!r:10}', "'88888' ")
715 self.assertEqual(f'{(lambda y:x*y)("8"):10}', "88888 ")
716
717 # lambda doesn't work without parens, because the colon
718 # makes the parser think it's a format_spec
Lysandros Nikolaou9a4b38f2020-04-15 21:22:10 +0300719 self.assertAllRaise(SyntaxError, 'invalid syntax',
Eric V. Smith235a6f02015-09-19 14:51:32 -0400720 ["f'{lambda x:x}'",
721 ])
722
723 def test_yield(self):
724 # Not terribly useful, but make sure the yield turns
725 # a function into a generator
726 def fn(y):
727 f'y:{yield y*2}'
Pablo Galindo972ab032020-06-08 01:47:37 +0100728 f'{yield}'
Eric V. Smith235a6f02015-09-19 14:51:32 -0400729
730 g = fn(4)
731 self.assertEqual(next(g), 8)
Pablo Galindo972ab032020-06-08 01:47:37 +0100732 self.assertEqual(next(g), None)
Eric V. Smith235a6f02015-09-19 14:51:32 -0400733
734 def test_yield_send(self):
735 def fn(x):
736 yield f'x:{yield (lambda i: x * i)}'
737
738 g = fn(10)
739 the_lambda = next(g)
740 self.assertEqual(the_lambda(4), 40)
741 self.assertEqual(g.send('string'), 'x:string')
742
743 def test_expressions_with_triple_quoted_strings(self):
744 self.assertEqual(f"{'''x'''}", 'x')
745 self.assertEqual(f"{'''eric's'''}", "eric's")
Eric V. Smith235a6f02015-09-19 14:51:32 -0400746
747 # Test concatenation within an expression
748 self.assertEqual(f'{"x" """eric"s""" "y"}', 'xeric"sy')
749 self.assertEqual(f'{"x" """eric"s"""}', 'xeric"s')
750 self.assertEqual(f'{"""eric"s""" "y"}', 'eric"sy')
751 self.assertEqual(f'{"""x""" """eric"s""" "y"}', 'xeric"sy')
752 self.assertEqual(f'{"""x""" """eric"s""" """y"""}', 'xeric"sy')
753 self.assertEqual(f'{r"""x""" """eric"s""" """y"""}', 'xeric"sy')
754
755 def test_multiple_vars(self):
756 x = 98
757 y = 'abc'
758 self.assertEqual(f'{x}{y}', '98abc')
759
760 self.assertEqual(f'X{x}{y}', 'X98abc')
761 self.assertEqual(f'{x}X{y}', '98Xabc')
762 self.assertEqual(f'{x}{y}X', '98abcX')
763
764 self.assertEqual(f'X{x}Y{y}', 'X98Yabc')
765 self.assertEqual(f'X{x}{y}Y', 'X98abcY')
766 self.assertEqual(f'{x}X{y}Y', '98XabcY')
767
768 self.assertEqual(f'X{x}Y{y}Z', 'X98YabcZ')
769
770 def test_closure(self):
771 def outer(x):
772 def inner():
773 return f'x:{x}'
774 return inner
775
776 self.assertEqual(outer('987')(), 'x:987')
777 self.assertEqual(outer(7)(), 'x:7')
778
779 def test_arguments(self):
780 y = 2
781 def f(x, width):
782 return f'x={x*y:{width}}'
783
784 self.assertEqual(f('foo', 10), 'x=foofoo ')
785 x = 'bar'
786 self.assertEqual(f(10, 10), 'x= 20')
787
788 def test_locals(self):
789 value = 123
790 self.assertEqual(f'v:{value}', 'v:123')
791
792 def test_missing_variable(self):
793 with self.assertRaises(NameError):
794 f'v:{value}'
795
796 def test_missing_format_spec(self):
797 class O:
798 def __format__(self, spec):
799 if not spec:
800 return '*'
801 return spec
802
803 self.assertEqual(f'{O():x}', 'x')
804 self.assertEqual(f'{O()}', '*')
805 self.assertEqual(f'{O():}', '*')
806
807 self.assertEqual(f'{3:}', '3')
808 self.assertEqual(f'{3!s:}', '3')
809
810 def test_global(self):
811 self.assertEqual(f'g:{a_global}', 'g:global variable')
812 self.assertEqual(f'g:{a_global!r}', "g:'global variable'")
813
814 a_local = 'local variable'
815 self.assertEqual(f'g:{a_global} l:{a_local}',
816 'g:global variable l:local variable')
817 self.assertEqual(f'g:{a_global!r}',
818 "g:'global variable'")
819 self.assertEqual(f'g:{a_global} l:{a_local!r}',
820 "g:global variable l:'local variable'")
821
822 self.assertIn("module 'unittest' from", f'{unittest}')
823
824 def test_shadowed_global(self):
825 a_global = 'really a local'
826 self.assertEqual(f'g:{a_global}', 'g:really a local')
827 self.assertEqual(f'g:{a_global!r}', "g:'really a local'")
828
829 a_local = 'local variable'
830 self.assertEqual(f'g:{a_global} l:{a_local}',
831 'g:really a local l:local variable')
832 self.assertEqual(f'g:{a_global!r}',
833 "g:'really a local'")
834 self.assertEqual(f'g:{a_global} l:{a_local!r}',
835 "g:really a local l:'local variable'")
836
837 def test_call(self):
838 def foo(x):
839 return 'x=' + str(x)
840
841 self.assertEqual(f'{foo(10)}', 'x=10')
842
843 def test_nested_fstrings(self):
844 y = 5
845 self.assertEqual(f'{f"{0}"*3}', '000')
846 self.assertEqual(f'{f"{y}"*3}', '555')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400847
848 def test_invalid_string_prefixes(self):
Pablo Galindo70c188e2020-04-13 02:47:35 +0100849 single_quote_cases = ["fu''",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400850 "uf''",
851 "Fu''",
852 "fU''",
853 "Uf''",
854 "uF''",
855 "ufr''",
856 "urf''",
857 "fur''",
858 "fru''",
859 "rfu''",
860 "ruf''",
861 "FUR''",
862 "Fur''",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400863 "fb''",
864 "fB''",
865 "Fb''",
866 "FB''",
867 "bf''",
868 "bF''",
869 "Bf''",
Pablo Galindo70c188e2020-04-13 02:47:35 +0100870 "BF''",]
871 double_quote_cases = [case.replace("'", '"') for case in single_quote_cases]
Lysandros Nikolaou846d8b22020-05-04 14:32:18 +0300872 self.assertAllRaise(SyntaxError, 'unexpected EOF while parsing',
Pablo Galindo70c188e2020-04-13 02:47:35 +0100873 single_quote_cases + double_quote_cases)
Eric V. Smith235a6f02015-09-19 14:51:32 -0400874
875 def test_leading_trailing_spaces(self):
876 self.assertEqual(f'{ 3}', '3')
877 self.assertEqual(f'{ 3}', '3')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400878 self.assertEqual(f'{3 }', '3')
879 self.assertEqual(f'{3 }', '3')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400880
881 self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]}}',
882 'expr={1: 2}')
883 self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]} }',
884 'expr={1: 2}')
885
Eric V. Smith235a6f02015-09-19 14:51:32 -0400886 def test_not_equal(self):
887 # There's a special test for this because there's a special
888 # case in the f-string parser to look for != as not ending an
889 # expression. Normally it would, while looking for !s or !r.
890
891 self.assertEqual(f'{3!=4}', 'True')
892 self.assertEqual(f'{3!=4:}', 'True')
893 self.assertEqual(f'{3!=4!s}', 'True')
894 self.assertEqual(f'{3!=4!s:.3}', 'Tru')
895
Eric V. Smith9a4135e2019-05-08 16:28:48 -0400896 def test_equal_equal(self):
897 # Because an expression ending in = has special meaning,
898 # there's a special test for ==. Make sure it works.
899
900 self.assertEqual(f'{0==1}', 'False')
901
Eric V. Smith235a6f02015-09-19 14:51:32 -0400902 def test_conversions(self):
903 self.assertEqual(f'{3.14:10.10}', ' 3.14')
904 self.assertEqual(f'{3.14!s:10.10}', '3.14 ')
905 self.assertEqual(f'{3.14!r:10.10}', '3.14 ')
906 self.assertEqual(f'{3.14!a:10.10}', '3.14 ')
907
908 self.assertEqual(f'{"a"}', 'a')
909 self.assertEqual(f'{"a"!r}', "'a'")
910 self.assertEqual(f'{"a"!a}', "'a'")
911
912 # Not a conversion.
913 self.assertEqual(f'{"a!r"}', "a!r")
914
915 # Not a conversion, but show that ! is allowed in a format spec.
916 self.assertEqual(f'{3.14:!<10.10}', '3.14!!!!!!')
917
Eric V. Smith235a6f02015-09-19 14:51:32 -0400918 self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character',
919 ["f'{3!g}'",
920 "f'{3!A}'",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400921 "f'{3!3}'",
922 "f'{3!G}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400923 "f'{3!!}'",
924 "f'{3!:}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400925 "f'{3! s}'", # no space before conversion char
Eric V. Smith235a6f02015-09-19 14:51:32 -0400926 ])
927
928 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
929 ["f'{x!s{y}}'",
930 "f'{3!ss}'",
931 "f'{3!ss:}'",
932 "f'{3!ss:s}'",
933 ])
934
935 def test_assignment(self):
936 self.assertAllRaise(SyntaxError, 'invalid syntax',
937 ["f'' = 3",
938 "f'{0}' = x",
939 "f'{x}' = x",
940 ])
941
942 def test_del(self):
943 self.assertAllRaise(SyntaxError, 'invalid syntax',
944 ["del f''",
945 "del '' f''",
946 ])
947
948 def test_mismatched_braces(self):
949 self.assertAllRaise(SyntaxError, "f-string: single '}' is not allowed",
950 ["f'{{}'",
951 "f'{{}}}'",
952 "f'}'",
953 "f'x}'",
954 "f'x}x'",
Jason R. Coombsda25abf72016-11-06 11:14:48 -0500955 r"f'\u007b}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400956
957 # Can't have { or } in a format spec.
958 "f'{3:}>10}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400959 "f'{3:}}>10}'",
960 ])
961
962 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
963 ["f'{3:{{>10}'",
964 "f'{3'",
965 "f'{3!'",
966 "f'{3:'",
967 "f'{3!s'",
968 "f'{3!s:'",
969 "f'{3!s:3'",
970 "f'x{'",
971 "f'x{x'",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400972 "f'{x'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400973 "f'{3:s'",
974 "f'{{{'",
975 "f'{{}}{'",
976 "f'{'",
977 ])
978
Eric V. Smith235a6f02015-09-19 14:51:32 -0400979 # But these are just normal strings.
980 self.assertEqual(f'{"{"}', '{')
981 self.assertEqual(f'{"}"}', '}')
982 self.assertEqual(f'{3:{"}"}>10}', '}}}}}}}}}3')
983 self.assertEqual(f'{2:{"{"}>10}', '{{{{{{{{{2')
984
985 def test_if_conditional(self):
986 # There's special logic in compile.c to test if the
987 # conditional for an if (and while) are constants. Exercise
988 # that code.
989
990 def test_fstring(x, expected):
991 flag = 0
992 if f'{x}':
993 flag = 1
994 else:
995 flag = 2
996 self.assertEqual(flag, expected)
997
998 def test_concat_empty(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_non_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 test_fstring('', 2)
1015 test_fstring(' ', 1)
1016
1017 test_concat_empty('', 2)
1018 test_concat_empty(' ', 1)
1019
1020 test_concat_non_empty('', 1)
1021 test_concat_non_empty(' ', 1)
1022
1023 def test_empty_format_specifier(self):
1024 x = 'test'
1025 self.assertEqual(f'{x}', 'test')
1026 self.assertEqual(f'{x:}', 'test')
1027 self.assertEqual(f'{x!s:}', 'test')
1028 self.assertEqual(f'{x!r:}', "'test'")
1029
1030 def test_str_format_differences(self):
1031 d = {'a': 'string',
1032 0: 'integer',
1033 }
1034 a = 0
1035 self.assertEqual(f'{d[0]}', 'integer')
1036 self.assertEqual(f'{d["a"]}', 'string')
1037 self.assertEqual(f'{d[a]}', 'integer')
1038 self.assertEqual('{d[a]}'.format(d=d), 'string')
1039 self.assertEqual('{d[0]}'.format(d=d), 'integer')
1040
Eric V. Smith135d5f42016-02-05 18:23:08 -05001041 def test_errors(self):
1042 # see issue 26287
Serhiy Storchaka13c8f322016-10-31 08:13:00 +02001043 self.assertAllRaise(TypeError, 'unsupported',
Eric V. Smith135d5f42016-02-05 18:23:08 -05001044 [r"f'{(lambda: 0):x}'",
1045 r"f'{(0,):x}'",
1046 ])
1047 self.assertAllRaise(ValueError, 'Unknown format code',
1048 [r"f'{1000:j}'",
1049 r"f'{1000:j}'",
1050 ])
1051
Lysandros Nikolaouf7b1e462020-05-26 03:32:18 +03001052 def test_filename_in_syntaxerror(self):
1053 # see issue 38964
1054 with temp_cwd() as cwd:
1055 file_path = os.path.join(cwd, 't.py')
1056 with open(file_path, 'w') as f:
1057 f.write('f"{a b}"') # This generates a SyntaxError
Serhiy Storchaka700cfa82020-06-25 17:56:31 +03001058 _, _, stderr = assert_python_failure(file_path,
1059 PYTHONIOENCODING='ascii')
1060 self.assertIn(file_path.encode('ascii', 'backslashreplace'), stderr)
Lysandros Nikolaouf7b1e462020-05-26 03:32:18 +03001061
Eric V. Smith235a6f02015-09-19 14:51:32 -04001062 def test_loop(self):
1063 for i in range(1000):
1064 self.assertEqual(f'i:{i}', 'i:' + str(i))
1065
1066 def test_dict(self):
1067 d = {'"': 'dquote',
1068 "'": 'squote',
1069 'foo': 'bar',
1070 }
Eric V. Smith235a6f02015-09-19 14:51:32 -04001071 self.assertEqual(f'''{d["'"]}''', 'squote')
1072 self.assertEqual(f"""{d['"']}""", 'dquote')
1073
1074 self.assertEqual(f'{d["foo"]}', 'bar')
1075 self.assertEqual(f"{d['foo']}", 'bar')
Eric V. Smith235a6f02015-09-19 14:51:32 -04001076
ericvsmith11e97f22017-06-16 06:19:32 -04001077 def test_backslash_char(self):
1078 # Check eval of a backslash followed by a control char.
1079 # See bpo-30682: this used to raise an assert in pydebug mode.
1080 self.assertEqual(eval('f"\\\n"'), '')
1081 self.assertEqual(eval('f"\\\r"'), '')
1082
Eric V. Smith9a4135e2019-05-08 16:28:48 -04001083 def test_debug_conversion(self):
1084 x = 'A string'
1085 self.assertEqual(f'{x=}', 'x=' + repr(x))
1086 self.assertEqual(f'{x =}', 'x =' + repr(x))
1087 self.assertEqual(f'{x=!s}', 'x=' + str(x))
1088 self.assertEqual(f'{x=!r}', 'x=' + repr(x))
1089 self.assertEqual(f'{x=!a}', 'x=' + ascii(x))
1090
1091 x = 2.71828
1092 self.assertEqual(f'{x=:.2f}', 'x=' + format(x, '.2f'))
1093 self.assertEqual(f'{x=:}', 'x=' + format(x, ''))
1094 self.assertEqual(f'{x=!r:^20}', 'x=' + format(repr(x), '^20'))
1095 self.assertEqual(f'{x=!s:^20}', 'x=' + format(str(x), '^20'))
1096 self.assertEqual(f'{x=!a:^20}', 'x=' + format(ascii(x), '^20'))
1097
1098 x = 9
1099 self.assertEqual(f'{3*x+15=}', '3*x+15=42')
1100
1101 # There is code in ast.c that deals with non-ascii expression values. So,
1102 # use a unicode identifier to trigger that.
1103 tenπ = 31.4
1104 self.assertEqual(f'{tenπ=:.2f}', 'tenπ=31.40')
1105
1106 # Also test with Unicode in non-identifiers.
1107 self.assertEqual(f'{"Σ"=}', '"Σ"=\'Σ\'')
1108
1109 # Make sure nested fstrings still work.
1110 self.assertEqual(f'{f"{3.1415=:.1f}":*^20}', '*****3.1415=3.1*****')
1111
1112 # Make sure text before and after an expression with = works
1113 # correctly.
1114 pi = 'π'
1115 self.assertEqual(f'alpha α {pi=} ω omega', "alpha α pi='π' ω omega")
1116
1117 # Check multi-line expressions.
1118 self.assertEqual(f'''{
11193
1120=}''', '\n3\n=3')
1121
1122 # Since = is handled specially, make sure all existing uses of
1123 # it still work.
1124
1125 self.assertEqual(f'{0==1}', 'False')
1126 self.assertEqual(f'{0!=1}', 'True')
1127 self.assertEqual(f'{0<=1}', 'True')
1128 self.assertEqual(f'{0>=1}', 'False')
1129 self.assertEqual(f'{(x:="5")}', '5')
1130 self.assertEqual(x, '5')
1131 self.assertEqual(f'{(x:=5)}', '5')
1132 self.assertEqual(x, 5)
1133 self.assertEqual(f'{"="}', '=')
1134
1135 x = 20
1136 # This isn't an assignment expression, it's 'x', with a format
1137 # spec of '=10'. See test_walrus: you need to use parens.
1138 self.assertEqual(f'{x:=10}', ' 20')
1139
1140 # Test named function parameters, to make sure '=' parsing works
1141 # there.
1142 def f(a):
1143 nonlocal x
1144 oldx = x
1145 x = a
1146 return oldx
1147 x = 0
1148 self.assertEqual(f'{f(a="3=")}', '0')
1149 self.assertEqual(x, '3=')
1150 self.assertEqual(f'{f(a=4)}', '3=')
1151 self.assertEqual(x, 4)
1152
1153 # Make sure __format__ is being called.
1154 class C:
1155 def __format__(self, s):
1156 return f'FORMAT-{s}'
1157 def __repr__(self):
1158 return 'REPR'
1159
1160 self.assertEqual(f'{C()=}', 'C()=REPR')
1161 self.assertEqual(f'{C()=!r}', 'C()=REPR')
1162 self.assertEqual(f'{C()=:}', 'C()=FORMAT-')
1163 self.assertEqual(f'{C()=: }', 'C()=FORMAT- ')
1164 self.assertEqual(f'{C()=:x}', 'C()=FORMAT-x')
1165 self.assertEqual(f'{C()=!r:*^20}', 'C()=********REPR********')
1166
Pablo Galindo26f55c22019-05-12 01:43:04 +01001167 self.assertRaises(SyntaxError, eval, "f'{C=]'")
1168
Eric V. Smith6f6ff8a2019-05-27 15:31:52 -04001169 # Make sure leading and following text works.
1170 x = 'foo'
1171 self.assertEqual(f'X{x=}Y', 'Xx='+repr(x)+'Y')
1172
1173 # Make sure whitespace around the = works.
1174 self.assertEqual(f'X{x =}Y', 'Xx ='+repr(x)+'Y')
1175 self.assertEqual(f'X{x= }Y', 'Xx= '+repr(x)+'Y')
1176 self.assertEqual(f'X{x = }Y', 'Xx = '+repr(x)+'Y')
1177
1178 # These next lines contains tabs. Backslash escapes don't
1179 # work in f-strings.
Min ho Kim96e12d52019-07-22 06:12:33 +10001180 # patchcheck doesn't like these tabs. So the only way to test
Eric V. Smith6f6ff8a2019-05-27 15:31:52 -04001181 # this will be to dynamically created and exec the f-strings. But
1182 # that's such a hassle I'll save it for another day. For now, convert
1183 # the tabs to spaces just to shut up patchcheck.
1184 #self.assertEqual(f'X{x =}Y', 'Xx\t='+repr(x)+'Y')
1185 #self.assertEqual(f'X{x = }Y', 'Xx\t=\t'+repr(x)+'Y')
1186
Eric V. Smith9a4135e2019-05-08 16:28:48 -04001187 def test_walrus(self):
1188 x = 20
1189 # This isn't an assignment expression, it's 'x', with a format
1190 # spec of '=10'.
1191 self.assertEqual(f'{x:=10}', ' 20')
1192
1193 # This is an assignment expression, which requires parens.
1194 self.assertEqual(f'{(x:=10)}', '10')
1195 self.assertEqual(x, 10)
1196
Łukasz Langae7c566c2017-09-06 17:27:58 -07001197
Eric V. Smith235a6f02015-09-19 14:51:32 -04001198if __name__ == '__main__':
1199 unittest.main()