blob: 296cb05285c48052be8f9808c8700a1d8ae68f2c [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
han-solo0d6aa7f2020-09-01 10:34:29 -040012import re
Eric V. Smith235a6f02015-09-19 14:51:32 -040013import types
14import decimal
15import unittest
Hai Shia089d212020-07-06 17:15:08 +080016from test.support.os_helper import temp_cwd
Lysandros Nikolaouf7b1e462020-05-26 03:32:18 +030017from test.support.script_helper import assert_python_failure
Eric V. Smith235a6f02015-09-19 14:51:32 -040018
19a_global = 'global variable'
20
21# You could argue that I'm too strict in looking for specific error
22# values with assertRaisesRegex, but without it it's way too easy to
23# make a syntax error in the test strings. Especially with all of the
24# triple quotes, raw strings, backslashes, etc. I think it's a
25# worthwhile tradeoff. When I switched to this method, I found many
26# examples where I wasn't testing what I thought I was.
27
28class TestCase(unittest.TestCase):
29 def assertAllRaise(self, exception_type, regex, error_strings):
30 for str in error_strings:
31 with self.subTest(str=str):
32 with self.assertRaisesRegex(exception_type, regex):
33 eval(str)
34
35 def test__format__lookup(self):
36 # Make sure __format__ is looked up on the type, not the instance.
37 class X:
38 def __format__(self, spec):
39 return 'class'
40
41 x = X()
42
43 # Add a bound __format__ method to the 'y' instance, but not
44 # the 'x' instance.
45 y = X()
46 y.__format__ = types.MethodType(lambda self, spec: 'instance', y)
47
48 self.assertEqual(f'{y}', format(y))
49 self.assertEqual(f'{y}', 'class')
50 self.assertEqual(format(x), format(y))
51
52 # __format__ is not called this way, but still make sure it
53 # returns what we expect (so we can make sure we're bypassing
54 # it).
55 self.assertEqual(x.__format__(''), 'class')
56 self.assertEqual(y.__format__(''), 'instance')
57
58 # This is how __format__ is actually called.
59 self.assertEqual(type(x).__format__(x, ''), 'class')
60 self.assertEqual(type(y).__format__(y, ''), 'class')
61
62 def test_ast(self):
63 # Inspired by http://bugs.python.org/issue24975
64 class X:
65 def __init__(self):
66 self.called = False
67 def __call__(self):
68 self.called = True
69 return 4
70 x = X()
71 expr = """
72a = 10
73f'{a * x()}'"""
74 t = ast.parse(expr)
75 c = compile(t, '', 'exec')
76
77 # Make sure x was not called.
78 self.assertFalse(x.called)
79
80 # Actually run the code.
81 exec(c)
82
83 # Make sure x was called.
84 self.assertTrue(x.called)
85
Łukasz Langae7c566c2017-09-06 17:27:58 -070086 def test_ast_line_numbers(self):
87 expr = """
88a = 10
89f'{a * x()}'"""
90 t = ast.parse(expr)
91 self.assertEqual(type(t), ast.Module)
92 self.assertEqual(len(t.body), 2)
93 # check `a = 10`
94 self.assertEqual(type(t.body[0]), ast.Assign)
95 self.assertEqual(t.body[0].lineno, 2)
96 # check `f'...'`
97 self.assertEqual(type(t.body[1]), ast.Expr)
98 self.assertEqual(type(t.body[1].value), ast.JoinedStr)
99 self.assertEqual(len(t.body[1].value.values), 1)
100 self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)
101 self.assertEqual(t.body[1].lineno, 3)
102 self.assertEqual(t.body[1].value.lineno, 3)
103 self.assertEqual(t.body[1].value.values[0].lineno, 3)
104 # check the binop location
105 binop = t.body[1].value.values[0].value
106 self.assertEqual(type(binop), ast.BinOp)
107 self.assertEqual(type(binop.left), ast.Name)
108 self.assertEqual(type(binop.op), ast.Mult)
109 self.assertEqual(type(binop.right), ast.Call)
110 self.assertEqual(binop.lineno, 3)
111 self.assertEqual(binop.left.lineno, 3)
112 self.assertEqual(binop.right.lineno, 3)
113 self.assertEqual(binop.col_offset, 3)
114 self.assertEqual(binop.left.col_offset, 3)
115 self.assertEqual(binop.right.col_offset, 7)
116
117 def test_ast_line_numbers_multiple_formattedvalues(self):
118 expr = """
119f'no formatted values'
120f'eggs {a * x()} spam {b + y()}'"""
121 t = ast.parse(expr)
122 self.assertEqual(type(t), ast.Module)
123 self.assertEqual(len(t.body), 2)
124 # check `f'no formatted value'`
125 self.assertEqual(type(t.body[0]), ast.Expr)
126 self.assertEqual(type(t.body[0].value), ast.JoinedStr)
127 self.assertEqual(t.body[0].lineno, 2)
128 # check `f'...'`
129 self.assertEqual(type(t.body[1]), ast.Expr)
130 self.assertEqual(type(t.body[1].value), ast.JoinedStr)
131 self.assertEqual(len(t.body[1].value.values), 4)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300132 self.assertEqual(type(t.body[1].value.values[0]), ast.Constant)
133 self.assertEqual(type(t.body[1].value.values[0].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700134 self.assertEqual(type(t.body[1].value.values[1]), ast.FormattedValue)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300135 self.assertEqual(type(t.body[1].value.values[2]), ast.Constant)
136 self.assertEqual(type(t.body[1].value.values[2].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700137 self.assertEqual(type(t.body[1].value.values[3]), ast.FormattedValue)
138 self.assertEqual(t.body[1].lineno, 3)
139 self.assertEqual(t.body[1].value.lineno, 3)
140 self.assertEqual(t.body[1].value.values[0].lineno, 3)
141 self.assertEqual(t.body[1].value.values[1].lineno, 3)
142 self.assertEqual(t.body[1].value.values[2].lineno, 3)
143 self.assertEqual(t.body[1].value.values[3].lineno, 3)
144 # check the first binop location
145 binop1 = t.body[1].value.values[1].value
146 self.assertEqual(type(binop1), ast.BinOp)
147 self.assertEqual(type(binop1.left), ast.Name)
148 self.assertEqual(type(binop1.op), ast.Mult)
149 self.assertEqual(type(binop1.right), ast.Call)
150 self.assertEqual(binop1.lineno, 3)
151 self.assertEqual(binop1.left.lineno, 3)
152 self.assertEqual(binop1.right.lineno, 3)
153 self.assertEqual(binop1.col_offset, 8)
154 self.assertEqual(binop1.left.col_offset, 8)
155 self.assertEqual(binop1.right.col_offset, 12)
156 # check the second binop location
157 binop2 = t.body[1].value.values[3].value
158 self.assertEqual(type(binop2), ast.BinOp)
159 self.assertEqual(type(binop2.left), ast.Name)
160 self.assertEqual(type(binop2.op), ast.Add)
161 self.assertEqual(type(binop2.right), ast.Call)
162 self.assertEqual(binop2.lineno, 3)
163 self.assertEqual(binop2.left.lineno, 3)
164 self.assertEqual(binop2.right.lineno, 3)
165 self.assertEqual(binop2.col_offset, 23)
166 self.assertEqual(binop2.left.col_offset, 23)
167 self.assertEqual(binop2.right.col_offset, 27)
168
169 def test_ast_line_numbers_nested(self):
170 expr = """
171a = 10
172f'{a * f"-{x()}-"}'"""
173 t = ast.parse(expr)
174 self.assertEqual(type(t), ast.Module)
175 self.assertEqual(len(t.body), 2)
176 # check `a = 10`
177 self.assertEqual(type(t.body[0]), ast.Assign)
178 self.assertEqual(t.body[0].lineno, 2)
179 # check `f'...'`
180 self.assertEqual(type(t.body[1]), ast.Expr)
181 self.assertEqual(type(t.body[1].value), ast.JoinedStr)
182 self.assertEqual(len(t.body[1].value.values), 1)
183 self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)
184 self.assertEqual(t.body[1].lineno, 3)
185 self.assertEqual(t.body[1].value.lineno, 3)
186 self.assertEqual(t.body[1].value.values[0].lineno, 3)
187 # check the binop location
188 binop = t.body[1].value.values[0].value
189 self.assertEqual(type(binop), ast.BinOp)
190 self.assertEqual(type(binop.left), ast.Name)
191 self.assertEqual(type(binop.op), ast.Mult)
192 self.assertEqual(type(binop.right), ast.JoinedStr)
193 self.assertEqual(binop.lineno, 3)
194 self.assertEqual(binop.left.lineno, 3)
195 self.assertEqual(binop.right.lineno, 3)
196 self.assertEqual(binop.col_offset, 3)
197 self.assertEqual(binop.left.col_offset, 3)
198 self.assertEqual(binop.right.col_offset, 7)
199 # check the nested call location
200 self.assertEqual(len(binop.right.values), 3)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300201 self.assertEqual(type(binop.right.values[0]), ast.Constant)
202 self.assertEqual(type(binop.right.values[0].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700203 self.assertEqual(type(binop.right.values[1]), ast.FormattedValue)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300204 self.assertEqual(type(binop.right.values[2]), ast.Constant)
205 self.assertEqual(type(binop.right.values[2].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700206 self.assertEqual(binop.right.values[0].lineno, 3)
207 self.assertEqual(binop.right.values[1].lineno, 3)
208 self.assertEqual(binop.right.values[2].lineno, 3)
209 call = binop.right.values[1].value
210 self.assertEqual(type(call), ast.Call)
211 self.assertEqual(call.lineno, 3)
Lysandros Nikolaou37af21b2020-04-29 03:43:50 +0300212 self.assertEqual(call.col_offset, 11)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700213
214 def test_ast_line_numbers_duplicate_expression(self):
215 """Duplicate expression
216
217 NOTE: this is currently broken, always sets location of the first
218 expression.
219 """
220 expr = """
221a = 10
222f'{a * x()} {a * x()} {a * x()}'
223"""
224 t = ast.parse(expr)
225 self.assertEqual(type(t), ast.Module)
226 self.assertEqual(len(t.body), 2)
227 # check `a = 10`
228 self.assertEqual(type(t.body[0]), ast.Assign)
229 self.assertEqual(t.body[0].lineno, 2)
230 # check `f'...'`
231 self.assertEqual(type(t.body[1]), ast.Expr)
232 self.assertEqual(type(t.body[1].value), ast.JoinedStr)
233 self.assertEqual(len(t.body[1].value.values), 5)
234 self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300235 self.assertEqual(type(t.body[1].value.values[1]), ast.Constant)
236 self.assertEqual(type(t.body[1].value.values[1].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700237 self.assertEqual(type(t.body[1].value.values[2]), ast.FormattedValue)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300238 self.assertEqual(type(t.body[1].value.values[3]), ast.Constant)
239 self.assertEqual(type(t.body[1].value.values[3].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700240 self.assertEqual(type(t.body[1].value.values[4]), ast.FormattedValue)
241 self.assertEqual(t.body[1].lineno, 3)
242 self.assertEqual(t.body[1].value.lineno, 3)
243 self.assertEqual(t.body[1].value.values[0].lineno, 3)
244 self.assertEqual(t.body[1].value.values[1].lineno, 3)
245 self.assertEqual(t.body[1].value.values[2].lineno, 3)
246 self.assertEqual(t.body[1].value.values[3].lineno, 3)
247 self.assertEqual(t.body[1].value.values[4].lineno, 3)
248 # check the first binop location
249 binop = t.body[1].value.values[0].value
250 self.assertEqual(type(binop), ast.BinOp)
251 self.assertEqual(type(binop.left), ast.Name)
252 self.assertEqual(type(binop.op), ast.Mult)
253 self.assertEqual(type(binop.right), ast.Call)
254 self.assertEqual(binop.lineno, 3)
255 self.assertEqual(binop.left.lineno, 3)
256 self.assertEqual(binop.right.lineno, 3)
257 self.assertEqual(binop.col_offset, 3)
258 self.assertEqual(binop.left.col_offset, 3)
259 self.assertEqual(binop.right.col_offset, 7)
260 # check the second binop location
261 binop = t.body[1].value.values[2].value
262 self.assertEqual(type(binop), ast.BinOp)
263 self.assertEqual(type(binop.left), ast.Name)
264 self.assertEqual(type(binop.op), ast.Mult)
265 self.assertEqual(type(binop.right), ast.Call)
266 self.assertEqual(binop.lineno, 3)
267 self.assertEqual(binop.left.lineno, 3)
268 self.assertEqual(binop.right.lineno, 3)
269 self.assertEqual(binop.col_offset, 3) # FIXME: this is wrong
270 self.assertEqual(binop.left.col_offset, 3) # FIXME: this is wrong
271 self.assertEqual(binop.right.col_offset, 7) # FIXME: this is wrong
272 # check the third binop location
273 binop = t.body[1].value.values[4].value
274 self.assertEqual(type(binop), ast.BinOp)
275 self.assertEqual(type(binop.left), ast.Name)
276 self.assertEqual(type(binop.op), ast.Mult)
277 self.assertEqual(type(binop.right), ast.Call)
278 self.assertEqual(binop.lineno, 3)
279 self.assertEqual(binop.left.lineno, 3)
280 self.assertEqual(binop.right.lineno, 3)
281 self.assertEqual(binop.col_offset, 3) # FIXME: this is wrong
282 self.assertEqual(binop.left.col_offset, 3) # FIXME: this is wrong
283 self.assertEqual(binop.right.col_offset, 7) # FIXME: this is wrong
284
285 def test_ast_line_numbers_multiline_fstring(self):
Anthony Sottile995d9b92019-01-12 20:05:13 -0800286 # See bpo-30465 for details.
Łukasz Langae7c566c2017-09-06 17:27:58 -0700287 expr = """
288a = 10
289f'''
290 {a
291 *
292 x()}
293non-important content
294'''
295"""
296 t = ast.parse(expr)
297 self.assertEqual(type(t), ast.Module)
298 self.assertEqual(len(t.body), 2)
299 # check `a = 10`
300 self.assertEqual(type(t.body[0]), ast.Assign)
301 self.assertEqual(t.body[0].lineno, 2)
302 # check `f'...'`
303 self.assertEqual(type(t.body[1]), ast.Expr)
304 self.assertEqual(type(t.body[1].value), ast.JoinedStr)
305 self.assertEqual(len(t.body[1].value.values), 3)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300306 self.assertEqual(type(t.body[1].value.values[0]), ast.Constant)
307 self.assertEqual(type(t.body[1].value.values[0].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700308 self.assertEqual(type(t.body[1].value.values[1]), ast.FormattedValue)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300309 self.assertEqual(type(t.body[1].value.values[2]), ast.Constant)
310 self.assertEqual(type(t.body[1].value.values[2].value), str)
Anthony Sottile995d9b92019-01-12 20:05:13 -0800311 self.assertEqual(t.body[1].lineno, 3)
312 self.assertEqual(t.body[1].value.lineno, 3)
313 self.assertEqual(t.body[1].value.values[0].lineno, 3)
314 self.assertEqual(t.body[1].value.values[1].lineno, 3)
315 self.assertEqual(t.body[1].value.values[2].lineno, 3)
316 self.assertEqual(t.body[1].col_offset, 0)
317 self.assertEqual(t.body[1].value.col_offset, 0)
318 self.assertEqual(t.body[1].value.values[0].col_offset, 0)
319 self.assertEqual(t.body[1].value.values[1].col_offset, 0)
320 self.assertEqual(t.body[1].value.values[2].col_offset, 0)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700321 # NOTE: the following lineno information and col_offset is correct for
322 # expressions within FormattedValues.
323 binop = t.body[1].value.values[1].value
324 self.assertEqual(type(binop), ast.BinOp)
325 self.assertEqual(type(binop.left), ast.Name)
326 self.assertEqual(type(binop.op), ast.Mult)
327 self.assertEqual(type(binop.right), ast.Call)
328 self.assertEqual(binop.lineno, 4)
329 self.assertEqual(binop.left.lineno, 4)
330 self.assertEqual(binop.right.lineno, 6)
Anthony Sottile995d9b92019-01-12 20:05:13 -0800331 self.assertEqual(binop.col_offset, 4)
332 self.assertEqual(binop.left.col_offset, 4)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700333 self.assertEqual(binop.right.col_offset, 7)
334
Pablo Galindobd2728b2021-01-03 01:11:41 +0000335 def test_ast_line_numbers_with_parentheses(self):
336 expr = """
337x = (
338 f" {test(t)}"
339)"""
340 t = ast.parse(expr)
341 self.assertEqual(type(t), ast.Module)
342 self.assertEqual(len(t.body), 1)
343 # check the test(t) location
344 call = t.body[0].value.values[1].value
345 self.assertEqual(type(call), ast.Call)
346 self.assertEqual(call.lineno, 3)
347 self.assertEqual(call.end_lineno, 3)
348 self.assertEqual(call.col_offset, 8)
349 self.assertEqual(call.end_col_offset, 15)
350
351 expr = """
352x = (
353 'PERL_MM_OPT', (
354 f'wat'
355 f'some_string={f(x)} '
356 f'wat'
357 ),
358)
359"""
360 t = ast.parse(expr)
361 self.assertEqual(type(t), ast.Module)
362 self.assertEqual(len(t.body), 1)
363 # check the fstring
364 fstring = t.body[0].value.elts[1]
365 self.assertEqual(type(fstring), ast.JoinedStr)
366 self.assertEqual(len(fstring.values), 3)
367 wat1, middle, wat2 = fstring.values
368 # check the first wat
369 self.assertEqual(type(wat1), ast.Constant)
370 self.assertEqual(wat1.lineno, 4)
371 self.assertEqual(wat1.end_lineno, 6)
372 self.assertEqual(wat1.col_offset, 12)
373 self.assertEqual(wat1.end_col_offset, 18)
374 # check the call
375 call = middle.value
376 self.assertEqual(type(call), ast.Call)
377 self.assertEqual(call.lineno, 5)
378 self.assertEqual(call.end_lineno, 5)
379 self.assertEqual(call.col_offset, 27)
380 self.assertEqual(call.end_col_offset, 31)
381 # check the second wat
382 self.assertEqual(type(wat2), ast.Constant)
383 self.assertEqual(wat2.lineno, 4)
384 self.assertEqual(wat2.end_lineno, 6)
385 self.assertEqual(wat2.col_offset, 12)
386 self.assertEqual(wat2.end_col_offset, 18)
387
Serhiy Storchaka4cc30ae2016-12-11 19:37:19 +0200388 def test_docstring(self):
389 def f():
390 f'''Not a docstring'''
391 self.assertIsNone(f.__doc__)
392 def g():
393 '''Not a docstring''' \
394 f''
395 self.assertIsNone(g.__doc__)
396
Eric V. Smith235a6f02015-09-19 14:51:32 -0400397 def test_literal_eval(self):
Eric V. Smith235a6f02015-09-19 14:51:32 -0400398 with self.assertRaisesRegex(ValueError, 'malformed node or string'):
Serhiy Storchaka4cc30ae2016-12-11 19:37:19 +0200399 ast.literal_eval("f'x'")
Eric V. Smith235a6f02015-09-19 14:51:32 -0400400
401 def test_ast_compile_time_concat(self):
402 x = ['']
403
404 expr = """x[0] = 'foo' f'{3}'"""
405 t = ast.parse(expr)
406 c = compile(t, '', 'exec')
407 exec(c)
408 self.assertEqual(x[0], 'foo3')
409
Eric V. Smith9b88fdf2016-11-07 17:54:01 -0500410 def test_compile_time_concat_errors(self):
411 self.assertAllRaise(SyntaxError,
412 'cannot mix bytes and nonbytes literals',
413 [r"""f'' b''""",
414 r"""b'' f''""",
415 ])
416
Eric V. Smith235a6f02015-09-19 14:51:32 -0400417 def test_literal(self):
418 self.assertEqual(f'', '')
419 self.assertEqual(f'a', 'a')
420 self.assertEqual(f' ', ' ')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400421
422 def test_unterminated_string(self):
423 self.assertAllRaise(SyntaxError, 'f-string: unterminated string',
424 [r"""f'{"x'""",
425 r"""f'{"x}'""",
426 r"""f'{("x'""",
427 r"""f'{("x}'""",
428 ])
429
430 def test_mismatched_parens(self):
Serhiy Storchaka58159ef2019-01-12 09:46:50 +0200431 self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\}' "
432 r"does not match opening parenthesis '\('",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400433 ["f'{((}'",
434 ])
Serhiy Storchaka58159ef2019-01-12 09:46:50 +0200435 self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\)' "
436 r"does not match opening parenthesis '\['",
437 ["f'{a[4)}'",
438 ])
439 self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\]' "
440 r"does not match opening parenthesis '\('",
441 ["f'{a(4]}'",
442 ])
443 self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\}' "
444 r"does not match opening parenthesis '\['",
445 ["f'{a[4}'",
446 ])
447 self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\}' "
448 r"does not match opening parenthesis '\('",
449 ["f'{a(4}'",
450 ])
451 self.assertRaises(SyntaxError, eval, "f'{" + "("*500 + "}'")
Eric V. Smith235a6f02015-09-19 14:51:32 -0400452
453 def test_double_braces(self):
454 self.assertEqual(f'{{', '{')
455 self.assertEqual(f'a{{', 'a{')
456 self.assertEqual(f'{{b', '{b')
457 self.assertEqual(f'a{{b', 'a{b')
458 self.assertEqual(f'}}', '}')
459 self.assertEqual(f'a}}', 'a}')
460 self.assertEqual(f'}}b', '}b')
461 self.assertEqual(f'a}}b', 'a}b')
Eric V. Smith451d0e32016-09-09 21:56:20 -0400462 self.assertEqual(f'{{}}', '{}')
463 self.assertEqual(f'a{{}}', 'a{}')
464 self.assertEqual(f'{{b}}', '{b}')
465 self.assertEqual(f'{{}}c', '{}c')
466 self.assertEqual(f'a{{b}}', 'a{b}')
467 self.assertEqual(f'a{{}}c', 'a{}c')
468 self.assertEqual(f'{{b}}c', '{b}c')
469 self.assertEqual(f'a{{b}}c', 'a{b}c')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400470
471 self.assertEqual(f'{{{10}', '{10')
472 self.assertEqual(f'}}{10}', '}10')
473 self.assertEqual(f'}}{{{10}', '}{10')
474 self.assertEqual(f'}}a{{{10}', '}a{10')
475
476 self.assertEqual(f'{10}{{', '10{')
477 self.assertEqual(f'{10}}}', '10}')
478 self.assertEqual(f'{10}}}{{', '10}{')
479 self.assertEqual(f'{10}}}a{{' '}', '10}a{}')
480
481 # Inside of strings, don't interpret doubled brackets.
482 self.assertEqual(f'{"{{}}"}', '{{}}')
483
484 self.assertAllRaise(TypeError, 'unhashable type',
485 ["f'{ {{}} }'", # dict in a set
486 ])
487
488 def test_compile_time_concat(self):
489 x = 'def'
490 self.assertEqual('abc' f'## {x}ghi', 'abc## defghi')
491 self.assertEqual('abc' f'{x}' 'ghi', 'abcdefghi')
492 self.assertEqual('abc' f'{x}' 'gh' f'i{x:4}', 'abcdefghidef ')
493 self.assertEqual('{x}' f'{x}', '{x}def')
494 self.assertEqual('{x' f'{x}', '{xdef')
495 self.assertEqual('{x}' f'{x}', '{x}def')
496 self.assertEqual('{{x}}' f'{x}', '{{x}}def')
497 self.assertEqual('{{x' f'{x}', '{{xdef')
498 self.assertEqual('x}}' f'{x}', 'x}}def')
499 self.assertEqual(f'{x}' 'x}}', 'defx}}')
500 self.assertEqual(f'{x}' '', 'def')
501 self.assertEqual('' f'{x}' '', 'def')
502 self.assertEqual('' f'{x}', 'def')
503 self.assertEqual(f'{x}' '2', 'def2')
504 self.assertEqual('1' f'{x}' '2', '1def2')
505 self.assertEqual('1' f'{x}', '1def')
506 self.assertEqual(f'{x}' f'-{x}', 'def-def')
507 self.assertEqual('' f'', '')
508 self.assertEqual('' f'' '', '')
509 self.assertEqual('' f'' '' f'', '')
510 self.assertEqual(f'', '')
511 self.assertEqual(f'' '', '')
512 self.assertEqual(f'' '' f'', '')
513 self.assertEqual(f'' '' f'' '', '')
514
515 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
516 ["f'{3' f'}'", # can't concat to get a valid f-string
517 ])
518
519 def test_comments(self):
520 # These aren't comments, since they're in strings.
521 d = {'#': 'hash'}
522 self.assertEqual(f'{"#"}', '#')
523 self.assertEqual(f'{d["#"]}', 'hash')
524
Eric V. Smith09835dc2016-09-11 18:58:20 -0400525 self.assertAllRaise(SyntaxError, "f-string expression part cannot include '#'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400526 ["f'{1#}'", # error because the expression becomes "(1#)"
527 "f'{3(#)}'",
Eric V. Smith09835dc2016-09-11 18:58:20 -0400528 "f'{#}'",
Serhiy Storchaka58159ef2019-01-12 09:46:50 +0200529 ])
530 self.assertAllRaise(SyntaxError, r"f-string: unmatched '\)'",
531 ["f'{)#}'", # When wrapped in parens, this becomes
Eric V. Smith35a24c52016-09-11 19:01:22 -0400532 # '()#)'. Make sure that doesn't compile.
Eric V. Smith235a6f02015-09-19 14:51:32 -0400533 ])
534
535 def test_many_expressions(self):
536 # Create a string with many expressions in it. Note that
537 # because we have a space in here as a literal, we're actually
538 # going to use twice as many ast nodes: one for each literal
539 # plus one for each expression.
540 def build_fstr(n, extra=''):
541 return "f'" + ('{x} ' * n) + extra + "'"
542
543 x = 'X'
544 width = 1
545
546 # Test around 256.
547 for i in range(250, 260):
548 self.assertEqual(eval(build_fstr(i)), (x+' ')*i)
549
550 # Test concatenating 2 largs fstrings.
551 self.assertEqual(eval(build_fstr(255)*256), (x+' ')*(255*256))
552
553 s = build_fstr(253, '{x:{width}} ')
554 self.assertEqual(eval(s), (x+' ')*254)
555
556 # Test lots of expressions and constants, concatenated.
557 s = "f'{1}' 'x' 'y'" * 1024
558 self.assertEqual(eval(s), '1xy' * 1024)
559
560 def test_format_specifier_expressions(self):
561 width = 10
562 precision = 4
563 value = decimal.Decimal('12.34567')
564 self.assertEqual(f'result: {value:{width}.{precision}}', 'result: 12.35')
565 self.assertEqual(f'result: {value:{width!r}.{precision}}', 'result: 12.35')
566 self.assertEqual(f'result: {value:{width:0}.{precision:1}}', 'result: 12.35')
567 self.assertEqual(f'result: {value:{1}{0:0}.{precision:1}}', 'result: 12.35')
568 self.assertEqual(f'result: {value:{ 1}{ 0:0}.{ precision:1}}', 'result: 12.35')
569 self.assertEqual(f'{10:#{1}0x}', ' 0xa')
570 self.assertEqual(f'{10:{"#"}1{0}{"x"}}', ' 0xa')
571 self.assertEqual(f'{-10:-{"#"}1{0}x}', ' -0xa')
572 self.assertEqual(f'{-10:{"-"}#{1}0{"x"}}', ' -0xa')
573 self.assertEqual(f'{10:#{3 != {4:5} and width}x}', ' 0xa')
574
575 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
576 ["""f'{"s"!r{":10"}}'""",
577
578 # This looks like a nested format spec.
579 ])
580
Lysandros Nikolaou2e0a9202020-06-26 14:24:05 +0300581 self.assertAllRaise(SyntaxError, "f-string: invalid syntax",
Martin Panter263893c2016-07-28 01:25:31 +0000582 [# Invalid syntax inside a nested spec.
Eric V. Smith235a6f02015-09-19 14:51:32 -0400583 "f'{4:{/5}}'",
584 ])
585
586 self.assertAllRaise(SyntaxError, "f-string: expressions nested too deeply",
587 [# Can't nest format specifiers.
588 "f'result: {value:{width:{0}}.{precision:1}}'",
589 ])
590
591 self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character',
592 [# No expansion inside conversion or for
593 # the : or ! itself.
594 """f'{"s"!{"r"}}'""",
595 ])
596
597 def test_side_effect_order(self):
598 class X:
599 def __init__(self):
600 self.i = 0
601 def __format__(self, spec):
602 self.i += 1
603 return str(self.i)
604
605 x = X()
606 self.assertEqual(f'{x} {x}', '1 2')
607
608 def test_missing_expression(self):
609 self.assertAllRaise(SyntaxError, 'f-string: empty expression not allowed',
610 ["f'{}'",
611 "f'{ }'"
612 "f' {} '",
613 "f'{!r}'",
614 "f'{ !r}'",
615 "f'{10:{ }}'",
616 "f' { } '",
Eric V. Smith1d44c412015-09-23 07:49:00 -0400617
Serhiy Storchaka2e9cd582017-06-08 23:43:54 +0300618 # The Python parser ignores also the following
619 # whitespace characters in additional to a space.
620 "f'''{\t\f\r\n}'''",
621
Eric V. Smith1d44c412015-09-23 07:49:00 -0400622 # Catch the empty expression before the
623 # invalid conversion.
624 "f'{!x}'",
625 "f'{ !xr}'",
626 "f'{!x:}'",
627 "f'{!x:a}'",
628 "f'{ !xr:}'",
629 "f'{ !xr:a}'",
Eric V. Smith548c4d32015-09-23 08:00:01 -0400630
631 "f'{!}'",
632 "f'{:}'",
Eric V. Smithb2080f62015-09-23 10:24:43 -0400633
634 # We find the empty expression before the
635 # missing closing brace.
636 "f'{!'",
637 "f'{!s:'",
638 "f'{:'",
639 "f'{:x'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400640 ])
641
Serhiy Storchaka2e9cd582017-06-08 23:43:54 +0300642 # Different error message is raised for other whitespace characters.
Serhiy Storchaka74ea6b52020-05-12 12:42:04 +0300643 self.assertAllRaise(SyntaxError, r"invalid non-printable character U\+00A0",
Serhiy Storchaka2e9cd582017-06-08 23:43:54 +0300644 ["f'''{\xa0}'''",
645 "\xa0",
646 ])
647
Eric V. Smith235a6f02015-09-19 14:51:32 -0400648 def test_parens_in_expressions(self):
649 self.assertEqual(f'{3,}', '(3,)')
650
651 # Add these because when an expression is evaluated, parens
652 # are added around it. But we shouldn't go from an invalid
653 # expression to a valid one. The added parens are just
654 # supposed to allow whitespace (including newlines).
Lysandros Nikolaou2e0a9202020-06-26 14:24:05 +0300655 self.assertAllRaise(SyntaxError, 'f-string: invalid syntax',
Eric V. Smith235a6f02015-09-19 14:51:32 -0400656 ["f'{,}'",
657 "f'{,}'", # this is (,), which is an error
658 ])
659
Serhiy Storchaka58159ef2019-01-12 09:46:50 +0200660 self.assertAllRaise(SyntaxError, r"f-string: unmatched '\)'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400661 ["f'{3)+(4}'",
662 ])
663
Batuhan Taskayaa698d522021-01-21 00:38:47 +0300664 self.assertAllRaise(SyntaxError, 'unterminated string literal',
Eric V. Smith235a6f02015-09-19 14:51:32 -0400665 ["f'{\n}'",
666 ])
Pablo Galindo40901512021-01-31 22:48:23 +0000667 def test_newlines_before_syntax_error(self):
668 self.assertAllRaise(SyntaxError, "invalid syntax",
669 ["f'{.}'", "\nf'{.}'", "\n\nf'{.}'"])
Eric V. Smith235a6f02015-09-19 14:51:32 -0400670
Eric V. Smith451d0e32016-09-09 21:56:20 -0400671 def test_backslashes_in_string_part(self):
672 self.assertEqual(f'\t', '\t')
673 self.assertEqual(r'\t', '\\t')
674 self.assertEqual(rf'\t', '\\t')
675 self.assertEqual(f'{2}\t', '2\t')
676 self.assertEqual(f'{2}\t{3}', '2\t3')
677 self.assertEqual(f'\t{3}', '\t3')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400678
Eric V. Smith451d0e32016-09-09 21:56:20 -0400679 self.assertEqual(f'\u0394', '\u0394')
680 self.assertEqual(r'\u0394', '\\u0394')
681 self.assertEqual(rf'\u0394', '\\u0394')
682 self.assertEqual(f'{2}\u0394', '2\u0394')
683 self.assertEqual(f'{2}\u0394{3}', '2\u03943')
684 self.assertEqual(f'\u0394{3}', '\u03943')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400685
Eric V. Smith451d0e32016-09-09 21:56:20 -0400686 self.assertEqual(f'\U00000394', '\u0394')
687 self.assertEqual(r'\U00000394', '\\U00000394')
688 self.assertEqual(rf'\U00000394', '\\U00000394')
689 self.assertEqual(f'{2}\U00000394', '2\u0394')
690 self.assertEqual(f'{2}\U00000394{3}', '2\u03943')
691 self.assertEqual(f'\U00000394{3}', '\u03943')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400692
Eric V. Smith451d0e32016-09-09 21:56:20 -0400693 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}', '\u0394')
694 self.assertEqual(f'{2}\N{GREEK CAPITAL LETTER DELTA}', '2\u0394')
695 self.assertEqual(f'{2}\N{GREEK CAPITAL LETTER DELTA}{3}', '2\u03943')
696 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}{3}', '\u03943')
697 self.assertEqual(f'2\N{GREEK CAPITAL LETTER DELTA}', '2\u0394')
698 self.assertEqual(f'2\N{GREEK CAPITAL LETTER DELTA}3', '2\u03943')
699 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}3', '\u03943')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400700
Eric V. Smith451d0e32016-09-09 21:56:20 -0400701 self.assertEqual(f'\x20', ' ')
702 self.assertEqual(r'\x20', '\\x20')
703 self.assertEqual(rf'\x20', '\\x20')
704 self.assertEqual(f'{2}\x20', '2 ')
705 self.assertEqual(f'{2}\x20{3}', '2 3')
706 self.assertEqual(f'\x20{3}', ' 3')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400707
Eric V. Smith451d0e32016-09-09 21:56:20 -0400708 self.assertEqual(f'2\x20', '2 ')
709 self.assertEqual(f'2\x203', '2 3')
710 self.assertEqual(f'\x203', ' 3')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400711
Gregory P. Smithb4be87a2019-08-10 00:19:07 -0700712 with self.assertWarns(DeprecationWarning): # invalid escape sequence
Serhiy Storchaka0cd7a3f2017-05-25 13:33:55 +0300713 value = eval(r"f'\{6*7}'")
714 self.assertEqual(value, '\\42')
715 self.assertEqual(f'\\{6*7}', '\\42')
716 self.assertEqual(fr'\{6*7}', '\\42')
717
718 AMPERSAND = 'spam'
719 # Get the right unicode character (&), or pick up local variable
720 # depending on the number of backslashes.
721 self.assertEqual(f'\N{AMPERSAND}', '&')
722 self.assertEqual(f'\\N{AMPERSAND}', '\\Nspam')
723 self.assertEqual(fr'\N{AMPERSAND}', '\\Nspam')
724 self.assertEqual(f'\\\N{AMPERSAND}', '\\&')
725
Eric V. Smith451d0e32016-09-09 21:56:20 -0400726 def test_misformed_unicode_character_name(self):
727 # These test are needed because unicode names are parsed
728 # differently inside f-strings.
729 self.assertAllRaise(SyntaxError, r"\(unicode error\) 'unicodeescape' codec can't decode bytes in position .*: malformed \\N character escape",
730 [r"f'\N'",
731 r"f'\N{'",
732 r"f'\N{GREEK CAPITAL LETTER DELTA'",
733
734 # Here are the non-f-string versions,
735 # which should give the same errors.
736 r"'\N'",
737 r"'\N{'",
738 r"'\N{GREEK CAPITAL LETTER DELTA'",
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400739 ])
740
Eric V. Smith451d0e32016-09-09 21:56:20 -0400741 def test_no_backslashes_in_expression_part(self):
742 self.assertAllRaise(SyntaxError, 'f-string expression part cannot include a backslash',
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400743 [r"f'{\'a\'}'",
744 r"f'{\t3}'",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400745 r"f'{\}'",
746 r"rf'{\'a\'}'",
747 r"rf'{\t3}'",
748 r"rf'{\}'",
749 r"""rf'{"\N{LEFT CURLY BRACKET}"}'""",
Jason R. Coombs45cab8c2016-11-06 11:01:08 -0500750 r"f'{\n}'",
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400751 ])
752
Eric V. Smith451d0e32016-09-09 21:56:20 -0400753 def test_no_escapes_for_braces(self):
Jason R. Coombs1c92a762016-11-06 11:25:54 -0500754 """
755 Only literal curly braces begin an expression.
756 """
757 # \x7b is '{'.
758 self.assertEqual(f'\x7b1+1}}', '{1+1}')
759 self.assertEqual(f'\x7b1+1', '{1+1')
760 self.assertEqual(f'\u007b1+1', '{1+1')
761 self.assertEqual(f'\N{LEFT CURLY BRACKET}1+1\N{RIGHT CURLY BRACKET}', '{1+1}')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400762
Eric V. Smith235a6f02015-09-19 14:51:32 -0400763 def test_newlines_in_expressions(self):
764 self.assertEqual(f'{0}', '0')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400765 self.assertEqual(rf'''{3+
7664}''', '7')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400767
768 def test_lambda(self):
769 x = 5
770 self.assertEqual(f'{(lambda y:x*y)("8")!r}', "'88888'")
771 self.assertEqual(f'{(lambda y:x*y)("8")!r:10}', "'88888' ")
772 self.assertEqual(f'{(lambda y:x*y)("8"):10}', "88888 ")
773
774 # lambda doesn't work without parens, because the colon
775 # makes the parser think it's a format_spec
Lysandros Nikolaou2e0a9202020-06-26 14:24:05 +0300776 self.assertAllRaise(SyntaxError, 'f-string: invalid syntax',
Eric V. Smith235a6f02015-09-19 14:51:32 -0400777 ["f'{lambda x:x}'",
778 ])
779
780 def test_yield(self):
781 # Not terribly useful, but make sure the yield turns
782 # a function into a generator
783 def fn(y):
784 f'y:{yield y*2}'
Pablo Galindo972ab032020-06-08 01:47:37 +0100785 f'{yield}'
Eric V. Smith235a6f02015-09-19 14:51:32 -0400786
787 g = fn(4)
788 self.assertEqual(next(g), 8)
Pablo Galindo972ab032020-06-08 01:47:37 +0100789 self.assertEqual(next(g), None)
Eric V. Smith235a6f02015-09-19 14:51:32 -0400790
791 def test_yield_send(self):
792 def fn(x):
793 yield f'x:{yield (lambda i: x * i)}'
794
795 g = fn(10)
796 the_lambda = next(g)
797 self.assertEqual(the_lambda(4), 40)
798 self.assertEqual(g.send('string'), 'x:string')
799
800 def test_expressions_with_triple_quoted_strings(self):
801 self.assertEqual(f"{'''x'''}", 'x')
802 self.assertEqual(f"{'''eric's'''}", "eric's")
Eric V. Smith235a6f02015-09-19 14:51:32 -0400803
804 # Test concatenation within an expression
805 self.assertEqual(f'{"x" """eric"s""" "y"}', 'xeric"sy')
806 self.assertEqual(f'{"x" """eric"s"""}', 'xeric"s')
807 self.assertEqual(f'{"""eric"s""" "y"}', 'eric"sy')
808 self.assertEqual(f'{"""x""" """eric"s""" "y"}', 'xeric"sy')
809 self.assertEqual(f'{"""x""" """eric"s""" """y"""}', 'xeric"sy')
810 self.assertEqual(f'{r"""x""" """eric"s""" """y"""}', 'xeric"sy')
811
812 def test_multiple_vars(self):
813 x = 98
814 y = 'abc'
815 self.assertEqual(f'{x}{y}', '98abc')
816
817 self.assertEqual(f'X{x}{y}', 'X98abc')
818 self.assertEqual(f'{x}X{y}', '98Xabc')
819 self.assertEqual(f'{x}{y}X', '98abcX')
820
821 self.assertEqual(f'X{x}Y{y}', 'X98Yabc')
822 self.assertEqual(f'X{x}{y}Y', 'X98abcY')
823 self.assertEqual(f'{x}X{y}Y', '98XabcY')
824
825 self.assertEqual(f'X{x}Y{y}Z', 'X98YabcZ')
826
827 def test_closure(self):
828 def outer(x):
829 def inner():
830 return f'x:{x}'
831 return inner
832
833 self.assertEqual(outer('987')(), 'x:987')
834 self.assertEqual(outer(7)(), 'x:7')
835
836 def test_arguments(self):
837 y = 2
838 def f(x, width):
839 return f'x={x*y:{width}}'
840
841 self.assertEqual(f('foo', 10), 'x=foofoo ')
842 x = 'bar'
843 self.assertEqual(f(10, 10), 'x= 20')
844
845 def test_locals(self):
846 value = 123
847 self.assertEqual(f'v:{value}', 'v:123')
848
849 def test_missing_variable(self):
850 with self.assertRaises(NameError):
851 f'v:{value}'
852
853 def test_missing_format_spec(self):
854 class O:
855 def __format__(self, spec):
856 if not spec:
857 return '*'
858 return spec
859
860 self.assertEqual(f'{O():x}', 'x')
861 self.assertEqual(f'{O()}', '*')
862 self.assertEqual(f'{O():}', '*')
863
864 self.assertEqual(f'{3:}', '3')
865 self.assertEqual(f'{3!s:}', '3')
866
867 def test_global(self):
868 self.assertEqual(f'g:{a_global}', 'g:global variable')
869 self.assertEqual(f'g:{a_global!r}', "g:'global variable'")
870
871 a_local = 'local variable'
872 self.assertEqual(f'g:{a_global} l:{a_local}',
873 'g:global variable l:local variable')
874 self.assertEqual(f'g:{a_global!r}',
875 "g:'global variable'")
876 self.assertEqual(f'g:{a_global} l:{a_local!r}',
877 "g:global variable l:'local variable'")
878
879 self.assertIn("module 'unittest' from", f'{unittest}')
880
881 def test_shadowed_global(self):
882 a_global = 'really a local'
883 self.assertEqual(f'g:{a_global}', 'g:really a local')
884 self.assertEqual(f'g:{a_global!r}', "g:'really a local'")
885
886 a_local = 'local variable'
887 self.assertEqual(f'g:{a_global} l:{a_local}',
888 'g:really a local l:local variable')
889 self.assertEqual(f'g:{a_global!r}',
890 "g:'really a local'")
891 self.assertEqual(f'g:{a_global} l:{a_local!r}',
892 "g:really a local l:'local variable'")
893
894 def test_call(self):
895 def foo(x):
896 return 'x=' + str(x)
897
898 self.assertEqual(f'{foo(10)}', 'x=10')
899
900 def test_nested_fstrings(self):
901 y = 5
902 self.assertEqual(f'{f"{0}"*3}', '000')
903 self.assertEqual(f'{f"{y}"*3}', '555')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400904
905 def test_invalid_string_prefixes(self):
Pablo Galindo70c188e2020-04-13 02:47:35 +0100906 single_quote_cases = ["fu''",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400907 "uf''",
908 "Fu''",
909 "fU''",
910 "Uf''",
911 "uF''",
912 "ufr''",
913 "urf''",
914 "fur''",
915 "fru''",
916 "rfu''",
917 "ruf''",
918 "FUR''",
919 "Fur''",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400920 "fb''",
921 "fB''",
922 "Fb''",
923 "FB''",
924 "bf''",
925 "bF''",
926 "Bf''",
Pablo Galindo70c188e2020-04-13 02:47:35 +0100927 "BF''",]
928 double_quote_cases = [case.replace("'", '"') for case in single_quote_cases]
Lysandros Nikolaou846d8b22020-05-04 14:32:18 +0300929 self.assertAllRaise(SyntaxError, 'unexpected EOF while parsing',
Pablo Galindo70c188e2020-04-13 02:47:35 +0100930 single_quote_cases + double_quote_cases)
Eric V. Smith235a6f02015-09-19 14:51:32 -0400931
932 def test_leading_trailing_spaces(self):
933 self.assertEqual(f'{ 3}', '3')
934 self.assertEqual(f'{ 3}', '3')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400935 self.assertEqual(f'{3 }', '3')
936 self.assertEqual(f'{3 }', '3')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400937
938 self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]}}',
939 'expr={1: 2}')
940 self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]} }',
941 'expr={1: 2}')
942
Eric V. Smith235a6f02015-09-19 14:51:32 -0400943 def test_not_equal(self):
944 # There's a special test for this because there's a special
945 # case in the f-string parser to look for != as not ending an
946 # expression. Normally it would, while looking for !s or !r.
947
948 self.assertEqual(f'{3!=4}', 'True')
949 self.assertEqual(f'{3!=4:}', 'True')
950 self.assertEqual(f'{3!=4!s}', 'True')
951 self.assertEqual(f'{3!=4!s:.3}', 'Tru')
952
Eric V. Smith9a4135e2019-05-08 16:28:48 -0400953 def test_equal_equal(self):
954 # Because an expression ending in = has special meaning,
955 # there's a special test for ==. Make sure it works.
956
957 self.assertEqual(f'{0==1}', 'False')
958
Eric V. Smith235a6f02015-09-19 14:51:32 -0400959 def test_conversions(self):
960 self.assertEqual(f'{3.14:10.10}', ' 3.14')
961 self.assertEqual(f'{3.14!s:10.10}', '3.14 ')
962 self.assertEqual(f'{3.14!r:10.10}', '3.14 ')
963 self.assertEqual(f'{3.14!a:10.10}', '3.14 ')
964
965 self.assertEqual(f'{"a"}', 'a')
966 self.assertEqual(f'{"a"!r}', "'a'")
967 self.assertEqual(f'{"a"!a}', "'a'")
968
969 # Not a conversion.
970 self.assertEqual(f'{"a!r"}', "a!r")
971
972 # Not a conversion, but show that ! is allowed in a format spec.
973 self.assertEqual(f'{3.14:!<10.10}', '3.14!!!!!!')
974
Eric V. Smith235a6f02015-09-19 14:51:32 -0400975 self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character',
976 ["f'{3!g}'",
977 "f'{3!A}'",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400978 "f'{3!3}'",
979 "f'{3!G}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400980 "f'{3!!}'",
981 "f'{3!:}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400982 "f'{3! s}'", # no space before conversion char
Eric V. Smith235a6f02015-09-19 14:51:32 -0400983 ])
984
985 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
986 ["f'{x!s{y}}'",
987 "f'{3!ss}'",
988 "f'{3!ss:}'",
989 "f'{3!ss:s}'",
990 ])
991
992 def test_assignment(self):
993 self.assertAllRaise(SyntaxError, 'invalid syntax',
994 ["f'' = 3",
995 "f'{0}' = x",
996 "f'{x}' = x",
997 ])
998
999 def test_del(self):
1000 self.assertAllRaise(SyntaxError, 'invalid syntax',
1001 ["del f''",
1002 "del '' f''",
1003 ])
1004
1005 def test_mismatched_braces(self):
1006 self.assertAllRaise(SyntaxError, "f-string: single '}' is not allowed",
1007 ["f'{{}'",
1008 "f'{{}}}'",
1009 "f'}'",
1010 "f'x}'",
1011 "f'x}x'",
Jason R. Coombsda25abf72016-11-06 11:14:48 -05001012 r"f'\u007b}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -04001013
1014 # Can't have { or } in a format spec.
1015 "f'{3:}>10}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -04001016 "f'{3:}}>10}'",
1017 ])
1018
1019 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
1020 ["f'{3:{{>10}'",
1021 "f'{3'",
1022 "f'{3!'",
1023 "f'{3:'",
1024 "f'{3!s'",
1025 "f'{3!s:'",
1026 "f'{3!s:3'",
1027 "f'x{'",
1028 "f'x{x'",
Eric V. Smith451d0e32016-09-09 21:56:20 -04001029 "f'{x'",
Eric V. Smith235a6f02015-09-19 14:51:32 -04001030 "f'{3:s'",
1031 "f'{{{'",
1032 "f'{{}}{'",
1033 "f'{'",
1034 ])
1035
Eric V. Smith235a6f02015-09-19 14:51:32 -04001036 # But these are just normal strings.
1037 self.assertEqual(f'{"{"}', '{')
1038 self.assertEqual(f'{"}"}', '}')
1039 self.assertEqual(f'{3:{"}"}>10}', '}}}}}}}}}3')
1040 self.assertEqual(f'{2:{"{"}>10}', '{{{{{{{{{2')
1041
1042 def test_if_conditional(self):
1043 # There's special logic in compile.c to test if the
1044 # conditional for an if (and while) are constants. Exercise
1045 # that code.
1046
1047 def test_fstring(x, expected):
1048 flag = 0
1049 if f'{x}':
1050 flag = 1
1051 else:
1052 flag = 2
1053 self.assertEqual(flag, expected)
1054
1055 def test_concat_empty(x, expected):
1056 flag = 0
1057 if '' f'{x}':
1058 flag = 1
1059 else:
1060 flag = 2
1061 self.assertEqual(flag, expected)
1062
1063 def test_concat_non_empty(x, expected):
1064 flag = 0
1065 if ' ' f'{x}':
1066 flag = 1
1067 else:
1068 flag = 2
1069 self.assertEqual(flag, expected)
1070
1071 test_fstring('', 2)
1072 test_fstring(' ', 1)
1073
1074 test_concat_empty('', 2)
1075 test_concat_empty(' ', 1)
1076
1077 test_concat_non_empty('', 1)
1078 test_concat_non_empty(' ', 1)
1079
1080 def test_empty_format_specifier(self):
1081 x = 'test'
1082 self.assertEqual(f'{x}', 'test')
1083 self.assertEqual(f'{x:}', 'test')
1084 self.assertEqual(f'{x!s:}', 'test')
1085 self.assertEqual(f'{x!r:}', "'test'")
1086
1087 def test_str_format_differences(self):
1088 d = {'a': 'string',
1089 0: 'integer',
1090 }
1091 a = 0
1092 self.assertEqual(f'{d[0]}', 'integer')
1093 self.assertEqual(f'{d["a"]}', 'string')
1094 self.assertEqual(f'{d[a]}', 'integer')
1095 self.assertEqual('{d[a]}'.format(d=d), 'string')
1096 self.assertEqual('{d[0]}'.format(d=d), 'integer')
1097
Eric V. Smith135d5f42016-02-05 18:23:08 -05001098 def test_errors(self):
1099 # see issue 26287
Serhiy Storchaka13c8f322016-10-31 08:13:00 +02001100 self.assertAllRaise(TypeError, 'unsupported',
Eric V. Smith135d5f42016-02-05 18:23:08 -05001101 [r"f'{(lambda: 0):x}'",
1102 r"f'{(0,):x}'",
1103 ])
1104 self.assertAllRaise(ValueError, 'Unknown format code',
1105 [r"f'{1000:j}'",
1106 r"f'{1000:j}'",
1107 ])
1108
Lysandros Nikolaouf7b1e462020-05-26 03:32:18 +03001109 def test_filename_in_syntaxerror(self):
1110 # see issue 38964
1111 with temp_cwd() as cwd:
1112 file_path = os.path.join(cwd, 't.py')
Inada Naoki3caea9a2021-04-04 17:01:10 +09001113 with open(file_path, 'w', encoding="utf-8") as f:
Lysandros Nikolaouf7b1e462020-05-26 03:32:18 +03001114 f.write('f"{a b}"') # This generates a SyntaxError
Serhiy Storchaka700cfa82020-06-25 17:56:31 +03001115 _, _, stderr = assert_python_failure(file_path,
1116 PYTHONIOENCODING='ascii')
1117 self.assertIn(file_path.encode('ascii', 'backslashreplace'), stderr)
Lysandros Nikolaouf7b1e462020-05-26 03:32:18 +03001118
Eric V. Smith235a6f02015-09-19 14:51:32 -04001119 def test_loop(self):
1120 for i in range(1000):
1121 self.assertEqual(f'i:{i}', 'i:' + str(i))
1122
1123 def test_dict(self):
1124 d = {'"': 'dquote',
1125 "'": 'squote',
1126 'foo': 'bar',
1127 }
Eric V. Smith235a6f02015-09-19 14:51:32 -04001128 self.assertEqual(f'''{d["'"]}''', 'squote')
1129 self.assertEqual(f"""{d['"']}""", 'dquote')
1130
1131 self.assertEqual(f'{d["foo"]}', 'bar')
1132 self.assertEqual(f"{d['foo']}", 'bar')
Eric V. Smith235a6f02015-09-19 14:51:32 -04001133
ericvsmith11e97f22017-06-16 06:19:32 -04001134 def test_backslash_char(self):
1135 # Check eval of a backslash followed by a control char.
1136 # See bpo-30682: this used to raise an assert in pydebug mode.
1137 self.assertEqual(eval('f"\\\n"'), '')
1138 self.assertEqual(eval('f"\\\r"'), '')
1139
Eric V. Smith9a4135e2019-05-08 16:28:48 -04001140 def test_debug_conversion(self):
1141 x = 'A string'
1142 self.assertEqual(f'{x=}', 'x=' + repr(x))
1143 self.assertEqual(f'{x =}', 'x =' + repr(x))
1144 self.assertEqual(f'{x=!s}', 'x=' + str(x))
1145 self.assertEqual(f'{x=!r}', 'x=' + repr(x))
1146 self.assertEqual(f'{x=!a}', 'x=' + ascii(x))
1147
1148 x = 2.71828
1149 self.assertEqual(f'{x=:.2f}', 'x=' + format(x, '.2f'))
1150 self.assertEqual(f'{x=:}', 'x=' + format(x, ''))
1151 self.assertEqual(f'{x=!r:^20}', 'x=' + format(repr(x), '^20'))
1152 self.assertEqual(f'{x=!s:^20}', 'x=' + format(str(x), '^20'))
1153 self.assertEqual(f'{x=!a:^20}', 'x=' + format(ascii(x), '^20'))
1154
1155 x = 9
1156 self.assertEqual(f'{3*x+15=}', '3*x+15=42')
1157
1158 # There is code in ast.c that deals with non-ascii expression values. So,
1159 # use a unicode identifier to trigger that.
1160 tenπ = 31.4
1161 self.assertEqual(f'{tenπ=:.2f}', 'tenπ=31.40')
1162
1163 # Also test with Unicode in non-identifiers.
1164 self.assertEqual(f'{"Σ"=}', '"Σ"=\'Σ\'')
1165
1166 # Make sure nested fstrings still work.
1167 self.assertEqual(f'{f"{3.1415=:.1f}":*^20}', '*****3.1415=3.1*****')
1168
1169 # Make sure text before and after an expression with = works
1170 # correctly.
1171 pi = 'π'
1172 self.assertEqual(f'alpha α {pi=} ω omega', "alpha α pi='π' ω omega")
1173
1174 # Check multi-line expressions.
1175 self.assertEqual(f'''{
11763
1177=}''', '\n3\n=3')
1178
1179 # Since = is handled specially, make sure all existing uses of
1180 # it still work.
1181
1182 self.assertEqual(f'{0==1}', 'False')
1183 self.assertEqual(f'{0!=1}', 'True')
1184 self.assertEqual(f'{0<=1}', 'True')
1185 self.assertEqual(f'{0>=1}', 'False')
1186 self.assertEqual(f'{(x:="5")}', '5')
1187 self.assertEqual(x, '5')
1188 self.assertEqual(f'{(x:=5)}', '5')
1189 self.assertEqual(x, 5)
1190 self.assertEqual(f'{"="}', '=')
1191
1192 x = 20
1193 # This isn't an assignment expression, it's 'x', with a format
1194 # spec of '=10'. See test_walrus: you need to use parens.
1195 self.assertEqual(f'{x:=10}', ' 20')
1196
1197 # Test named function parameters, to make sure '=' parsing works
1198 # there.
1199 def f(a):
1200 nonlocal x
1201 oldx = x
1202 x = a
1203 return oldx
1204 x = 0
1205 self.assertEqual(f'{f(a="3=")}', '0')
1206 self.assertEqual(x, '3=')
1207 self.assertEqual(f'{f(a=4)}', '3=')
1208 self.assertEqual(x, 4)
1209
1210 # Make sure __format__ is being called.
1211 class C:
1212 def __format__(self, s):
1213 return f'FORMAT-{s}'
1214 def __repr__(self):
1215 return 'REPR'
1216
1217 self.assertEqual(f'{C()=}', 'C()=REPR')
1218 self.assertEqual(f'{C()=!r}', 'C()=REPR')
1219 self.assertEqual(f'{C()=:}', 'C()=FORMAT-')
1220 self.assertEqual(f'{C()=: }', 'C()=FORMAT- ')
1221 self.assertEqual(f'{C()=:x}', 'C()=FORMAT-x')
1222 self.assertEqual(f'{C()=!r:*^20}', 'C()=********REPR********')
1223
Pablo Galindo26f55c22019-05-12 01:43:04 +01001224 self.assertRaises(SyntaxError, eval, "f'{C=]'")
1225
Eric V. Smith6f6ff8a2019-05-27 15:31:52 -04001226 # Make sure leading and following text works.
1227 x = 'foo'
1228 self.assertEqual(f'X{x=}Y', 'Xx='+repr(x)+'Y')
1229
1230 # Make sure whitespace around the = works.
1231 self.assertEqual(f'X{x =}Y', 'Xx ='+repr(x)+'Y')
1232 self.assertEqual(f'X{x= }Y', 'Xx= '+repr(x)+'Y')
1233 self.assertEqual(f'X{x = }Y', 'Xx = '+repr(x)+'Y')
1234
1235 # These next lines contains tabs. Backslash escapes don't
1236 # work in f-strings.
Min ho Kim96e12d52019-07-22 06:12:33 +10001237 # patchcheck doesn't like these tabs. So the only way to test
Eric V. Smith6f6ff8a2019-05-27 15:31:52 -04001238 # this will be to dynamically created and exec the f-strings. But
1239 # that's such a hassle I'll save it for another day. For now, convert
1240 # the tabs to spaces just to shut up patchcheck.
1241 #self.assertEqual(f'X{x =}Y', 'Xx\t='+repr(x)+'Y')
1242 #self.assertEqual(f'X{x = }Y', 'Xx\t=\t'+repr(x)+'Y')
1243
Eric V. Smith9a4135e2019-05-08 16:28:48 -04001244 def test_walrus(self):
1245 x = 20
1246 # This isn't an assignment expression, it's 'x', with a format
1247 # spec of '=10'.
1248 self.assertEqual(f'{x:=10}', ' 20')
1249
1250 # This is an assignment expression, which requires parens.
1251 self.assertEqual(f'{(x:=10)}', '10')
1252 self.assertEqual(x, 10)
1253
Lysandros Nikolaou2e0a9202020-06-26 14:24:05 +03001254 def test_invalid_syntax_error_message(self):
1255 with self.assertRaisesRegex(SyntaxError, "f-string: invalid syntax"):
1256 compile("f'{a $ b}'", "?", "exec")
1257
han-solo0d6aa7f2020-09-01 10:34:29 -04001258 def test_with_two_commas_in_format_specifier(self):
1259 error_msg = re.escape("Cannot specify ',' with ','.")
1260 with self.assertRaisesRegex(ValueError, error_msg):
1261 f'{1:,,}'
1262
1263 def test_with_two_underscore_in_format_specifier(self):
1264 error_msg = re.escape("Cannot specify '_' with '_'.")
1265 with self.assertRaisesRegex(ValueError, error_msg):
1266 f'{1:__}'
1267
1268 def test_with_a_commas_and_an_underscore_in_format_specifier(self):
1269 error_msg = re.escape("Cannot specify both ',' and '_'.")
1270 with self.assertRaisesRegex(ValueError, error_msg):
1271 f'{1:,_}'
1272
1273 def test_with_an_underscore_and_a_comma_in_format_specifier(self):
1274 error_msg = re.escape("Cannot specify both ',' and '_'.")
1275 with self.assertRaisesRegex(ValueError, error_msg):
han-solo749ed852020-09-02 04:56:37 -04001276 f'{1:_,}'
Łukasz Langae7c566c2017-09-06 17:27:58 -07001277
Pablo Galindo8efad612021-03-24 19:34:17 +00001278 def test_syntax_error_for_starred_expressions(self):
1279 error_msg = re.escape("can't use starred expression here")
1280 with self.assertRaisesRegex(SyntaxError, error_msg):
1281 compile("f'{*a}'", "?", "exec")
1282
1283 error_msg = re.escape("can't use double starred expression here")
1284 with self.assertRaisesRegex(SyntaxError, error_msg):
1285 compile("f'{**a}'", "?", "exec")
1286
Eric V. Smith235a6f02015-09-19 14:51:32 -04001287if __name__ == '__main__':
1288 unittest.main()