blob: 7ca1512ebbf1bf4eac83cc3be4966a495890431a [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 ])
667
Eric V. Smith451d0e32016-09-09 21:56:20 -0400668 def test_backslashes_in_string_part(self):
669 self.assertEqual(f'\t', '\t')
670 self.assertEqual(r'\t', '\\t')
671 self.assertEqual(rf'\t', '\\t')
672 self.assertEqual(f'{2}\t', '2\t')
673 self.assertEqual(f'{2}\t{3}', '2\t3')
674 self.assertEqual(f'\t{3}', '\t3')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400675
Eric V. Smith451d0e32016-09-09 21:56:20 -0400676 self.assertEqual(f'\u0394', '\u0394')
677 self.assertEqual(r'\u0394', '\\u0394')
678 self.assertEqual(rf'\u0394', '\\u0394')
679 self.assertEqual(f'{2}\u0394', '2\u0394')
680 self.assertEqual(f'{2}\u0394{3}', '2\u03943')
681 self.assertEqual(f'\u0394{3}', '\u03943')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400682
Eric V. Smith451d0e32016-09-09 21:56:20 -0400683 self.assertEqual(f'\U00000394', '\u0394')
684 self.assertEqual(r'\U00000394', '\\U00000394')
685 self.assertEqual(rf'\U00000394', '\\U00000394')
686 self.assertEqual(f'{2}\U00000394', '2\u0394')
687 self.assertEqual(f'{2}\U00000394{3}', '2\u03943')
688 self.assertEqual(f'\U00000394{3}', '\u03943')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400689
Eric V. Smith451d0e32016-09-09 21:56:20 -0400690 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}', '\u0394')
691 self.assertEqual(f'{2}\N{GREEK CAPITAL LETTER DELTA}', '2\u0394')
692 self.assertEqual(f'{2}\N{GREEK CAPITAL LETTER DELTA}{3}', '2\u03943')
693 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}{3}', '\u03943')
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')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400697
Eric V. Smith451d0e32016-09-09 21:56:20 -0400698 self.assertEqual(f'\x20', ' ')
699 self.assertEqual(r'\x20', '\\x20')
700 self.assertEqual(rf'\x20', '\\x20')
701 self.assertEqual(f'{2}\x20', '2 ')
702 self.assertEqual(f'{2}\x20{3}', '2 3')
703 self.assertEqual(f'\x20{3}', ' 3')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400704
Eric V. Smith451d0e32016-09-09 21:56:20 -0400705 self.assertEqual(f'2\x20', '2 ')
706 self.assertEqual(f'2\x203', '2 3')
707 self.assertEqual(f'\x203', ' 3')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400708
Gregory P. Smithb4be87a2019-08-10 00:19:07 -0700709 with self.assertWarns(DeprecationWarning): # invalid escape sequence
Serhiy Storchaka0cd7a3f2017-05-25 13:33:55 +0300710 value = eval(r"f'\{6*7}'")
711 self.assertEqual(value, '\\42')
712 self.assertEqual(f'\\{6*7}', '\\42')
713 self.assertEqual(fr'\{6*7}', '\\42')
714
715 AMPERSAND = 'spam'
716 # Get the right unicode character (&), or pick up local variable
717 # depending on the number of backslashes.
718 self.assertEqual(f'\N{AMPERSAND}', '&')
719 self.assertEqual(f'\\N{AMPERSAND}', '\\Nspam')
720 self.assertEqual(fr'\N{AMPERSAND}', '\\Nspam')
721 self.assertEqual(f'\\\N{AMPERSAND}', '\\&')
722
Eric V. Smith451d0e32016-09-09 21:56:20 -0400723 def test_misformed_unicode_character_name(self):
724 # These test are needed because unicode names are parsed
725 # differently inside f-strings.
726 self.assertAllRaise(SyntaxError, r"\(unicode error\) 'unicodeescape' codec can't decode bytes in position .*: malformed \\N character escape",
727 [r"f'\N'",
728 r"f'\N{'",
729 r"f'\N{GREEK CAPITAL LETTER DELTA'",
730
731 # Here are the non-f-string versions,
732 # which should give the same errors.
733 r"'\N'",
734 r"'\N{'",
735 r"'\N{GREEK CAPITAL LETTER DELTA'",
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400736 ])
737
Eric V. Smith451d0e32016-09-09 21:56:20 -0400738 def test_no_backslashes_in_expression_part(self):
739 self.assertAllRaise(SyntaxError, 'f-string expression part cannot include a backslash',
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400740 [r"f'{\'a\'}'",
741 r"f'{\t3}'",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400742 r"f'{\}'",
743 r"rf'{\'a\'}'",
744 r"rf'{\t3}'",
745 r"rf'{\}'",
746 r"""rf'{"\N{LEFT CURLY BRACKET}"}'""",
Jason R. Coombs45cab8c2016-11-06 11:01:08 -0500747 r"f'{\n}'",
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400748 ])
749
Eric V. Smith451d0e32016-09-09 21:56:20 -0400750 def test_no_escapes_for_braces(self):
Jason R. Coombs1c92a762016-11-06 11:25:54 -0500751 """
752 Only literal curly braces begin an expression.
753 """
754 # \x7b is '{'.
755 self.assertEqual(f'\x7b1+1}}', '{1+1}')
756 self.assertEqual(f'\x7b1+1', '{1+1')
757 self.assertEqual(f'\u007b1+1', '{1+1')
758 self.assertEqual(f'\N{LEFT CURLY BRACKET}1+1\N{RIGHT CURLY BRACKET}', '{1+1}')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400759
Eric V. Smith235a6f02015-09-19 14:51:32 -0400760 def test_newlines_in_expressions(self):
761 self.assertEqual(f'{0}', '0')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400762 self.assertEqual(rf'''{3+
7634}''', '7')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400764
765 def test_lambda(self):
766 x = 5
767 self.assertEqual(f'{(lambda y:x*y)("8")!r}', "'88888'")
768 self.assertEqual(f'{(lambda y:x*y)("8")!r:10}', "'88888' ")
769 self.assertEqual(f'{(lambda y:x*y)("8"):10}', "88888 ")
770
771 # lambda doesn't work without parens, because the colon
772 # makes the parser think it's a format_spec
Lysandros Nikolaou2e0a9202020-06-26 14:24:05 +0300773 self.assertAllRaise(SyntaxError, 'f-string: invalid syntax',
Eric V. Smith235a6f02015-09-19 14:51:32 -0400774 ["f'{lambda x:x}'",
775 ])
776
777 def test_yield(self):
778 # Not terribly useful, but make sure the yield turns
779 # a function into a generator
780 def fn(y):
781 f'y:{yield y*2}'
Pablo Galindo972ab032020-06-08 01:47:37 +0100782 f'{yield}'
Eric V. Smith235a6f02015-09-19 14:51:32 -0400783
784 g = fn(4)
785 self.assertEqual(next(g), 8)
Pablo Galindo972ab032020-06-08 01:47:37 +0100786 self.assertEqual(next(g), None)
Eric V. Smith235a6f02015-09-19 14:51:32 -0400787
788 def test_yield_send(self):
789 def fn(x):
790 yield f'x:{yield (lambda i: x * i)}'
791
792 g = fn(10)
793 the_lambda = next(g)
794 self.assertEqual(the_lambda(4), 40)
795 self.assertEqual(g.send('string'), 'x:string')
796
797 def test_expressions_with_triple_quoted_strings(self):
798 self.assertEqual(f"{'''x'''}", 'x')
799 self.assertEqual(f"{'''eric's'''}", "eric's")
Eric V. Smith235a6f02015-09-19 14:51:32 -0400800
801 # Test concatenation within an expression
802 self.assertEqual(f'{"x" """eric"s""" "y"}', 'xeric"sy')
803 self.assertEqual(f'{"x" """eric"s"""}', 'xeric"s')
804 self.assertEqual(f'{"""eric"s""" "y"}', 'eric"sy')
805 self.assertEqual(f'{"""x""" """eric"s""" "y"}', 'xeric"sy')
806 self.assertEqual(f'{"""x""" """eric"s""" """y"""}', 'xeric"sy')
807 self.assertEqual(f'{r"""x""" """eric"s""" """y"""}', 'xeric"sy')
808
809 def test_multiple_vars(self):
810 x = 98
811 y = 'abc'
812 self.assertEqual(f'{x}{y}', '98abc')
813
814 self.assertEqual(f'X{x}{y}', 'X98abc')
815 self.assertEqual(f'{x}X{y}', '98Xabc')
816 self.assertEqual(f'{x}{y}X', '98abcX')
817
818 self.assertEqual(f'X{x}Y{y}', 'X98Yabc')
819 self.assertEqual(f'X{x}{y}Y', 'X98abcY')
820 self.assertEqual(f'{x}X{y}Y', '98XabcY')
821
822 self.assertEqual(f'X{x}Y{y}Z', 'X98YabcZ')
823
824 def test_closure(self):
825 def outer(x):
826 def inner():
827 return f'x:{x}'
828 return inner
829
830 self.assertEqual(outer('987')(), 'x:987')
831 self.assertEqual(outer(7)(), 'x:7')
832
833 def test_arguments(self):
834 y = 2
835 def f(x, width):
836 return f'x={x*y:{width}}'
837
838 self.assertEqual(f('foo', 10), 'x=foofoo ')
839 x = 'bar'
840 self.assertEqual(f(10, 10), 'x= 20')
841
842 def test_locals(self):
843 value = 123
844 self.assertEqual(f'v:{value}', 'v:123')
845
846 def test_missing_variable(self):
847 with self.assertRaises(NameError):
848 f'v:{value}'
849
850 def test_missing_format_spec(self):
851 class O:
852 def __format__(self, spec):
853 if not spec:
854 return '*'
855 return spec
856
857 self.assertEqual(f'{O():x}', 'x')
858 self.assertEqual(f'{O()}', '*')
859 self.assertEqual(f'{O():}', '*')
860
861 self.assertEqual(f'{3:}', '3')
862 self.assertEqual(f'{3!s:}', '3')
863
864 def test_global(self):
865 self.assertEqual(f'g:{a_global}', 'g:global variable')
866 self.assertEqual(f'g:{a_global!r}', "g:'global variable'")
867
868 a_local = 'local variable'
869 self.assertEqual(f'g:{a_global} l:{a_local}',
870 'g:global variable l:local variable')
871 self.assertEqual(f'g:{a_global!r}',
872 "g:'global variable'")
873 self.assertEqual(f'g:{a_global} l:{a_local!r}',
874 "g:global variable l:'local variable'")
875
876 self.assertIn("module 'unittest' from", f'{unittest}')
877
878 def test_shadowed_global(self):
879 a_global = 'really a local'
880 self.assertEqual(f'g:{a_global}', 'g:really a local')
881 self.assertEqual(f'g:{a_global!r}', "g:'really a local'")
882
883 a_local = 'local variable'
884 self.assertEqual(f'g:{a_global} l:{a_local}',
885 'g:really a local l:local variable')
886 self.assertEqual(f'g:{a_global!r}',
887 "g:'really a local'")
888 self.assertEqual(f'g:{a_global} l:{a_local!r}',
889 "g:really a local l:'local variable'")
890
891 def test_call(self):
892 def foo(x):
893 return 'x=' + str(x)
894
895 self.assertEqual(f'{foo(10)}', 'x=10')
896
897 def test_nested_fstrings(self):
898 y = 5
899 self.assertEqual(f'{f"{0}"*3}', '000')
900 self.assertEqual(f'{f"{y}"*3}', '555')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400901
902 def test_invalid_string_prefixes(self):
Pablo Galindo70c188e2020-04-13 02:47:35 +0100903 single_quote_cases = ["fu''",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400904 "uf''",
905 "Fu''",
906 "fU''",
907 "Uf''",
908 "uF''",
909 "ufr''",
910 "urf''",
911 "fur''",
912 "fru''",
913 "rfu''",
914 "ruf''",
915 "FUR''",
916 "Fur''",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400917 "fb''",
918 "fB''",
919 "Fb''",
920 "FB''",
921 "bf''",
922 "bF''",
923 "Bf''",
Pablo Galindo70c188e2020-04-13 02:47:35 +0100924 "BF''",]
925 double_quote_cases = [case.replace("'", '"') for case in single_quote_cases]
Lysandros Nikolaou846d8b22020-05-04 14:32:18 +0300926 self.assertAllRaise(SyntaxError, 'unexpected EOF while parsing',
Pablo Galindo70c188e2020-04-13 02:47:35 +0100927 single_quote_cases + double_quote_cases)
Eric V. Smith235a6f02015-09-19 14:51:32 -0400928
929 def test_leading_trailing_spaces(self):
930 self.assertEqual(f'{ 3}', '3')
931 self.assertEqual(f'{ 3}', '3')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400932 self.assertEqual(f'{3 }', '3')
933 self.assertEqual(f'{3 }', '3')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400934
935 self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]}}',
936 'expr={1: 2}')
937 self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]} }',
938 'expr={1: 2}')
939
Eric V. Smith235a6f02015-09-19 14:51:32 -0400940 def test_not_equal(self):
941 # There's a special test for this because there's a special
942 # case in the f-string parser to look for != as not ending an
943 # expression. Normally it would, while looking for !s or !r.
944
945 self.assertEqual(f'{3!=4}', 'True')
946 self.assertEqual(f'{3!=4:}', 'True')
947 self.assertEqual(f'{3!=4!s}', 'True')
948 self.assertEqual(f'{3!=4!s:.3}', 'Tru')
949
Eric V. Smith9a4135e2019-05-08 16:28:48 -0400950 def test_equal_equal(self):
951 # Because an expression ending in = has special meaning,
952 # there's a special test for ==. Make sure it works.
953
954 self.assertEqual(f'{0==1}', 'False')
955
Eric V. Smith235a6f02015-09-19 14:51:32 -0400956 def test_conversions(self):
957 self.assertEqual(f'{3.14:10.10}', ' 3.14')
958 self.assertEqual(f'{3.14!s:10.10}', '3.14 ')
959 self.assertEqual(f'{3.14!r:10.10}', '3.14 ')
960 self.assertEqual(f'{3.14!a:10.10}', '3.14 ')
961
962 self.assertEqual(f'{"a"}', 'a')
963 self.assertEqual(f'{"a"!r}', "'a'")
964 self.assertEqual(f'{"a"!a}', "'a'")
965
966 # Not a conversion.
967 self.assertEqual(f'{"a!r"}', "a!r")
968
969 # Not a conversion, but show that ! is allowed in a format spec.
970 self.assertEqual(f'{3.14:!<10.10}', '3.14!!!!!!')
971
Eric V. Smith235a6f02015-09-19 14:51:32 -0400972 self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character',
973 ["f'{3!g}'",
974 "f'{3!A}'",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400975 "f'{3!3}'",
976 "f'{3!G}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400977 "f'{3!!}'",
978 "f'{3!:}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400979 "f'{3! s}'", # no space before conversion char
Eric V. Smith235a6f02015-09-19 14:51:32 -0400980 ])
981
982 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
983 ["f'{x!s{y}}'",
984 "f'{3!ss}'",
985 "f'{3!ss:}'",
986 "f'{3!ss:s}'",
987 ])
988
989 def test_assignment(self):
990 self.assertAllRaise(SyntaxError, 'invalid syntax',
991 ["f'' = 3",
992 "f'{0}' = x",
993 "f'{x}' = x",
994 ])
995
996 def test_del(self):
997 self.assertAllRaise(SyntaxError, 'invalid syntax',
998 ["del f''",
999 "del '' f''",
1000 ])
1001
1002 def test_mismatched_braces(self):
1003 self.assertAllRaise(SyntaxError, "f-string: single '}' is not allowed",
1004 ["f'{{}'",
1005 "f'{{}}}'",
1006 "f'}'",
1007 "f'x}'",
1008 "f'x}x'",
Jason R. Coombsda25abf72016-11-06 11:14:48 -05001009 r"f'\u007b}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -04001010
1011 # Can't have { or } in a format spec.
1012 "f'{3:}>10}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -04001013 "f'{3:}}>10}'",
1014 ])
1015
1016 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
1017 ["f'{3:{{>10}'",
1018 "f'{3'",
1019 "f'{3!'",
1020 "f'{3:'",
1021 "f'{3!s'",
1022 "f'{3!s:'",
1023 "f'{3!s:3'",
1024 "f'x{'",
1025 "f'x{x'",
Eric V. Smith451d0e32016-09-09 21:56:20 -04001026 "f'{x'",
Eric V. Smith235a6f02015-09-19 14:51:32 -04001027 "f'{3:s'",
1028 "f'{{{'",
1029 "f'{{}}{'",
1030 "f'{'",
1031 ])
1032
Eric V. Smith235a6f02015-09-19 14:51:32 -04001033 # But these are just normal strings.
1034 self.assertEqual(f'{"{"}', '{')
1035 self.assertEqual(f'{"}"}', '}')
1036 self.assertEqual(f'{3:{"}"}>10}', '}}}}}}}}}3')
1037 self.assertEqual(f'{2:{"{"}>10}', '{{{{{{{{{2')
1038
1039 def test_if_conditional(self):
1040 # There's special logic in compile.c to test if the
1041 # conditional for an if (and while) are constants. Exercise
1042 # that code.
1043
1044 def test_fstring(x, expected):
1045 flag = 0
1046 if f'{x}':
1047 flag = 1
1048 else:
1049 flag = 2
1050 self.assertEqual(flag, expected)
1051
1052 def test_concat_empty(x, expected):
1053 flag = 0
1054 if '' f'{x}':
1055 flag = 1
1056 else:
1057 flag = 2
1058 self.assertEqual(flag, expected)
1059
1060 def test_concat_non_empty(x, expected):
1061 flag = 0
1062 if ' ' f'{x}':
1063 flag = 1
1064 else:
1065 flag = 2
1066 self.assertEqual(flag, expected)
1067
1068 test_fstring('', 2)
1069 test_fstring(' ', 1)
1070
1071 test_concat_empty('', 2)
1072 test_concat_empty(' ', 1)
1073
1074 test_concat_non_empty('', 1)
1075 test_concat_non_empty(' ', 1)
1076
1077 def test_empty_format_specifier(self):
1078 x = 'test'
1079 self.assertEqual(f'{x}', 'test')
1080 self.assertEqual(f'{x:}', 'test')
1081 self.assertEqual(f'{x!s:}', 'test')
1082 self.assertEqual(f'{x!r:}', "'test'")
1083
1084 def test_str_format_differences(self):
1085 d = {'a': 'string',
1086 0: 'integer',
1087 }
1088 a = 0
1089 self.assertEqual(f'{d[0]}', 'integer')
1090 self.assertEqual(f'{d["a"]}', 'string')
1091 self.assertEqual(f'{d[a]}', 'integer')
1092 self.assertEqual('{d[a]}'.format(d=d), 'string')
1093 self.assertEqual('{d[0]}'.format(d=d), 'integer')
1094
Eric V. Smith135d5f42016-02-05 18:23:08 -05001095 def test_errors(self):
1096 # see issue 26287
Serhiy Storchaka13c8f322016-10-31 08:13:00 +02001097 self.assertAllRaise(TypeError, 'unsupported',
Eric V. Smith135d5f42016-02-05 18:23:08 -05001098 [r"f'{(lambda: 0):x}'",
1099 r"f'{(0,):x}'",
1100 ])
1101 self.assertAllRaise(ValueError, 'Unknown format code',
1102 [r"f'{1000:j}'",
1103 r"f'{1000:j}'",
1104 ])
1105
Lysandros Nikolaouf7b1e462020-05-26 03:32:18 +03001106 def test_filename_in_syntaxerror(self):
1107 # see issue 38964
1108 with temp_cwd() as cwd:
1109 file_path = os.path.join(cwd, 't.py')
1110 with open(file_path, 'w') as f:
1111 f.write('f"{a b}"') # This generates a SyntaxError
Serhiy Storchaka700cfa82020-06-25 17:56:31 +03001112 _, _, stderr = assert_python_failure(file_path,
1113 PYTHONIOENCODING='ascii')
1114 self.assertIn(file_path.encode('ascii', 'backslashreplace'), stderr)
Lysandros Nikolaouf7b1e462020-05-26 03:32:18 +03001115
Eric V. Smith235a6f02015-09-19 14:51:32 -04001116 def test_loop(self):
1117 for i in range(1000):
1118 self.assertEqual(f'i:{i}', 'i:' + str(i))
1119
1120 def test_dict(self):
1121 d = {'"': 'dquote',
1122 "'": 'squote',
1123 'foo': 'bar',
1124 }
Eric V. Smith235a6f02015-09-19 14:51:32 -04001125 self.assertEqual(f'''{d["'"]}''', 'squote')
1126 self.assertEqual(f"""{d['"']}""", 'dquote')
1127
1128 self.assertEqual(f'{d["foo"]}', 'bar')
1129 self.assertEqual(f"{d['foo']}", 'bar')
Eric V. Smith235a6f02015-09-19 14:51:32 -04001130
ericvsmith11e97f22017-06-16 06:19:32 -04001131 def test_backslash_char(self):
1132 # Check eval of a backslash followed by a control char.
1133 # See bpo-30682: this used to raise an assert in pydebug mode.
1134 self.assertEqual(eval('f"\\\n"'), '')
1135 self.assertEqual(eval('f"\\\r"'), '')
1136
Eric V. Smith9a4135e2019-05-08 16:28:48 -04001137 def test_debug_conversion(self):
1138 x = 'A string'
1139 self.assertEqual(f'{x=}', 'x=' + repr(x))
1140 self.assertEqual(f'{x =}', 'x =' + repr(x))
1141 self.assertEqual(f'{x=!s}', 'x=' + str(x))
1142 self.assertEqual(f'{x=!r}', 'x=' + repr(x))
1143 self.assertEqual(f'{x=!a}', 'x=' + ascii(x))
1144
1145 x = 2.71828
1146 self.assertEqual(f'{x=:.2f}', 'x=' + format(x, '.2f'))
1147 self.assertEqual(f'{x=:}', 'x=' + format(x, ''))
1148 self.assertEqual(f'{x=!r:^20}', 'x=' + format(repr(x), '^20'))
1149 self.assertEqual(f'{x=!s:^20}', 'x=' + format(str(x), '^20'))
1150 self.assertEqual(f'{x=!a:^20}', 'x=' + format(ascii(x), '^20'))
1151
1152 x = 9
1153 self.assertEqual(f'{3*x+15=}', '3*x+15=42')
1154
1155 # There is code in ast.c that deals with non-ascii expression values. So,
1156 # use a unicode identifier to trigger that.
1157 tenπ = 31.4
1158 self.assertEqual(f'{tenπ=:.2f}', 'tenπ=31.40')
1159
1160 # Also test with Unicode in non-identifiers.
1161 self.assertEqual(f'{"Σ"=}', '"Σ"=\'Σ\'')
1162
1163 # Make sure nested fstrings still work.
1164 self.assertEqual(f'{f"{3.1415=:.1f}":*^20}', '*****3.1415=3.1*****')
1165
1166 # Make sure text before and after an expression with = works
1167 # correctly.
1168 pi = 'π'
1169 self.assertEqual(f'alpha α {pi=} ω omega', "alpha α pi='π' ω omega")
1170
1171 # Check multi-line expressions.
1172 self.assertEqual(f'''{
11733
1174=}''', '\n3\n=3')
1175
1176 # Since = is handled specially, make sure all existing uses of
1177 # it still work.
1178
1179 self.assertEqual(f'{0==1}', 'False')
1180 self.assertEqual(f'{0!=1}', 'True')
1181 self.assertEqual(f'{0<=1}', 'True')
1182 self.assertEqual(f'{0>=1}', 'False')
1183 self.assertEqual(f'{(x:="5")}', '5')
1184 self.assertEqual(x, '5')
1185 self.assertEqual(f'{(x:=5)}', '5')
1186 self.assertEqual(x, 5)
1187 self.assertEqual(f'{"="}', '=')
1188
1189 x = 20
1190 # This isn't an assignment expression, it's 'x', with a format
1191 # spec of '=10'. See test_walrus: you need to use parens.
1192 self.assertEqual(f'{x:=10}', ' 20')
1193
1194 # Test named function parameters, to make sure '=' parsing works
1195 # there.
1196 def f(a):
1197 nonlocal x
1198 oldx = x
1199 x = a
1200 return oldx
1201 x = 0
1202 self.assertEqual(f'{f(a="3=")}', '0')
1203 self.assertEqual(x, '3=')
1204 self.assertEqual(f'{f(a=4)}', '3=')
1205 self.assertEqual(x, 4)
1206
1207 # Make sure __format__ is being called.
1208 class C:
1209 def __format__(self, s):
1210 return f'FORMAT-{s}'
1211 def __repr__(self):
1212 return 'REPR'
1213
1214 self.assertEqual(f'{C()=}', 'C()=REPR')
1215 self.assertEqual(f'{C()=!r}', 'C()=REPR')
1216 self.assertEqual(f'{C()=:}', 'C()=FORMAT-')
1217 self.assertEqual(f'{C()=: }', 'C()=FORMAT- ')
1218 self.assertEqual(f'{C()=:x}', 'C()=FORMAT-x')
1219 self.assertEqual(f'{C()=!r:*^20}', 'C()=********REPR********')
1220
Pablo Galindo26f55c22019-05-12 01:43:04 +01001221 self.assertRaises(SyntaxError, eval, "f'{C=]'")
1222
Eric V. Smith6f6ff8a2019-05-27 15:31:52 -04001223 # Make sure leading and following text works.
1224 x = 'foo'
1225 self.assertEqual(f'X{x=}Y', 'Xx='+repr(x)+'Y')
1226
1227 # Make sure whitespace around the = works.
1228 self.assertEqual(f'X{x =}Y', 'Xx ='+repr(x)+'Y')
1229 self.assertEqual(f'X{x= }Y', 'Xx= '+repr(x)+'Y')
1230 self.assertEqual(f'X{x = }Y', 'Xx = '+repr(x)+'Y')
1231
1232 # These next lines contains tabs. Backslash escapes don't
1233 # work in f-strings.
Min ho Kim96e12d52019-07-22 06:12:33 +10001234 # patchcheck doesn't like these tabs. So the only way to test
Eric V. Smith6f6ff8a2019-05-27 15:31:52 -04001235 # this will be to dynamically created and exec the f-strings. But
1236 # that's such a hassle I'll save it for another day. For now, convert
1237 # the tabs to spaces just to shut up patchcheck.
1238 #self.assertEqual(f'X{x =}Y', 'Xx\t='+repr(x)+'Y')
1239 #self.assertEqual(f'X{x = }Y', 'Xx\t=\t'+repr(x)+'Y')
1240
Eric V. Smith9a4135e2019-05-08 16:28:48 -04001241 def test_walrus(self):
1242 x = 20
1243 # This isn't an assignment expression, it's 'x', with a format
1244 # spec of '=10'.
1245 self.assertEqual(f'{x:=10}', ' 20')
1246
1247 # This is an assignment expression, which requires parens.
1248 self.assertEqual(f'{(x:=10)}', '10')
1249 self.assertEqual(x, 10)
1250
Lysandros Nikolaou2e0a9202020-06-26 14:24:05 +03001251 def test_invalid_syntax_error_message(self):
1252 with self.assertRaisesRegex(SyntaxError, "f-string: invalid syntax"):
1253 compile("f'{a $ b}'", "?", "exec")
1254
han-solo0d6aa7f2020-09-01 10:34:29 -04001255 def test_with_two_commas_in_format_specifier(self):
1256 error_msg = re.escape("Cannot specify ',' with ','.")
1257 with self.assertRaisesRegex(ValueError, error_msg):
1258 f'{1:,,}'
1259
1260 def test_with_two_underscore_in_format_specifier(self):
1261 error_msg = re.escape("Cannot specify '_' with '_'.")
1262 with self.assertRaisesRegex(ValueError, error_msg):
1263 f'{1:__}'
1264
1265 def test_with_a_commas_and_an_underscore_in_format_specifier(self):
1266 error_msg = re.escape("Cannot specify both ',' and '_'.")
1267 with self.assertRaisesRegex(ValueError, error_msg):
1268 f'{1:,_}'
1269
1270 def test_with_an_underscore_and_a_comma_in_format_specifier(self):
1271 error_msg = re.escape("Cannot specify both ',' and '_'.")
1272 with self.assertRaisesRegex(ValueError, error_msg):
han-solo749ed852020-09-02 04:56:37 -04001273 f'{1:_,}'
Łukasz Langae7c566c2017-09-06 17:27:58 -07001274
Eric V. Smith235a6f02015-09-19 14:51:32 -04001275if __name__ == '__main__':
1276 unittest.main()