blob: 7432ae96c82d610c28bcc451fe39dbe7a309dc8c [file] [log] [blame]
Eric V. Smith9a4135e2019-05-08 16:28:48 -04001# -*- coding: utf-8 -*-
2# There are tests here with unicode string literals and
3# identifiers. There's a code in ast.c that was added because of a
4# failure with a non-ascii-only expression. So, I have tests for
5# that. There are workarounds that would let me run tests for that
6# code without unicode identifiers and strings, but just using them
7# directly seems like the easiest and therefore safest thing to do.
8# Unicode identifiers in tests is allowed by PEP 3131.
9
Eric V. Smith235a6f02015-09-19 14:51:32 -040010import ast
Lysandros Nikolaou791a46e2020-05-26 04:24:31 +030011import os
Miss Islington (bot)c16a2a12020-09-01 08:45:59 -070012import re
Eric V. Smith235a6f02015-09-19 14:51:32 -040013import types
14import decimal
15import unittest
Lysandros Nikolaou791a46e2020-05-26 04:24:31 +030016from test.support import temp_cwd, use_old_parser
17from 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
Serhiy Storchaka4cc30ae2016-12-11 19:37:19 +0200335 def test_docstring(self):
336 def f():
337 f'''Not a docstring'''
338 self.assertIsNone(f.__doc__)
339 def g():
340 '''Not a docstring''' \
341 f''
342 self.assertIsNone(g.__doc__)
343
Eric V. Smith235a6f02015-09-19 14:51:32 -0400344 def test_literal_eval(self):
Eric V. Smith235a6f02015-09-19 14:51:32 -0400345 with self.assertRaisesRegex(ValueError, 'malformed node or string'):
Serhiy Storchaka4cc30ae2016-12-11 19:37:19 +0200346 ast.literal_eval("f'x'")
Eric V. Smith235a6f02015-09-19 14:51:32 -0400347
348 def test_ast_compile_time_concat(self):
349 x = ['']
350
351 expr = """x[0] = 'foo' f'{3}'"""
352 t = ast.parse(expr)
353 c = compile(t, '', 'exec')
354 exec(c)
355 self.assertEqual(x[0], 'foo3')
356
Eric V. Smith9b88fdf2016-11-07 17:54:01 -0500357 def test_compile_time_concat_errors(self):
358 self.assertAllRaise(SyntaxError,
359 'cannot mix bytes and nonbytes literals',
360 [r"""f'' b''""",
361 r"""b'' f''""",
362 ])
363
Eric V. Smith235a6f02015-09-19 14:51:32 -0400364 def test_literal(self):
365 self.assertEqual(f'', '')
366 self.assertEqual(f'a', 'a')
367 self.assertEqual(f' ', ' ')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400368
369 def test_unterminated_string(self):
370 self.assertAllRaise(SyntaxError, 'f-string: unterminated string',
371 [r"""f'{"x'""",
372 r"""f'{"x}'""",
373 r"""f'{("x'""",
374 r"""f'{("x}'""",
375 ])
376
377 def test_mismatched_parens(self):
Serhiy Storchaka58159ef2019-01-12 09:46:50 +0200378 self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\}' "
379 r"does not match opening parenthesis '\('",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400380 ["f'{((}'",
381 ])
Serhiy Storchaka58159ef2019-01-12 09:46:50 +0200382 self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\)' "
383 r"does not match opening parenthesis '\['",
384 ["f'{a[4)}'",
385 ])
386 self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\]' "
387 r"does not match opening parenthesis '\('",
388 ["f'{a(4]}'",
389 ])
390 self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\}' "
391 r"does not match opening parenthesis '\['",
392 ["f'{a[4}'",
393 ])
394 self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\}' "
395 r"does not match opening parenthesis '\('",
396 ["f'{a(4}'",
397 ])
398 self.assertRaises(SyntaxError, eval, "f'{" + "("*500 + "}'")
Eric V. Smith235a6f02015-09-19 14:51:32 -0400399
400 def test_double_braces(self):
401 self.assertEqual(f'{{', '{')
402 self.assertEqual(f'a{{', 'a{')
403 self.assertEqual(f'{{b', '{b')
404 self.assertEqual(f'a{{b', 'a{b')
405 self.assertEqual(f'}}', '}')
406 self.assertEqual(f'a}}', 'a}')
407 self.assertEqual(f'}}b', '}b')
408 self.assertEqual(f'a}}b', 'a}b')
Eric V. Smith451d0e32016-09-09 21:56:20 -0400409 self.assertEqual(f'{{}}', '{}')
410 self.assertEqual(f'a{{}}', 'a{}')
411 self.assertEqual(f'{{b}}', '{b}')
412 self.assertEqual(f'{{}}c', '{}c')
413 self.assertEqual(f'a{{b}}', 'a{b}')
414 self.assertEqual(f'a{{}}c', 'a{}c')
415 self.assertEqual(f'{{b}}c', '{b}c')
416 self.assertEqual(f'a{{b}}c', 'a{b}c')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400417
418 self.assertEqual(f'{{{10}', '{10')
419 self.assertEqual(f'}}{10}', '}10')
420 self.assertEqual(f'}}{{{10}', '}{10')
421 self.assertEqual(f'}}a{{{10}', '}a{10')
422
423 self.assertEqual(f'{10}{{', '10{')
424 self.assertEqual(f'{10}}}', '10}')
425 self.assertEqual(f'{10}}}{{', '10}{')
426 self.assertEqual(f'{10}}}a{{' '}', '10}a{}')
427
428 # Inside of strings, don't interpret doubled brackets.
429 self.assertEqual(f'{"{{}}"}', '{{}}')
430
431 self.assertAllRaise(TypeError, 'unhashable type',
432 ["f'{ {{}} }'", # dict in a set
433 ])
434
435 def test_compile_time_concat(self):
436 x = 'def'
437 self.assertEqual('abc' f'## {x}ghi', 'abc## defghi')
438 self.assertEqual('abc' f'{x}' 'ghi', 'abcdefghi')
439 self.assertEqual('abc' f'{x}' 'gh' f'i{x:4}', 'abcdefghidef ')
440 self.assertEqual('{x}' f'{x}', '{x}def')
441 self.assertEqual('{x' f'{x}', '{xdef')
442 self.assertEqual('{x}' f'{x}', '{x}def')
443 self.assertEqual('{{x}}' f'{x}', '{{x}}def')
444 self.assertEqual('{{x' f'{x}', '{{xdef')
445 self.assertEqual('x}}' f'{x}', 'x}}def')
446 self.assertEqual(f'{x}' 'x}}', 'defx}}')
447 self.assertEqual(f'{x}' '', 'def')
448 self.assertEqual('' f'{x}' '', 'def')
449 self.assertEqual('' f'{x}', 'def')
450 self.assertEqual(f'{x}' '2', 'def2')
451 self.assertEqual('1' f'{x}' '2', '1def2')
452 self.assertEqual('1' f'{x}', '1def')
453 self.assertEqual(f'{x}' f'-{x}', 'def-def')
454 self.assertEqual('' f'', '')
455 self.assertEqual('' f'' '', '')
456 self.assertEqual('' f'' '' f'', '')
457 self.assertEqual(f'', '')
458 self.assertEqual(f'' '', '')
459 self.assertEqual(f'' '' f'', '')
460 self.assertEqual(f'' '' f'' '', '')
461
462 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
463 ["f'{3' f'}'", # can't concat to get a valid f-string
464 ])
465
466 def test_comments(self):
467 # These aren't comments, since they're in strings.
468 d = {'#': 'hash'}
469 self.assertEqual(f'{"#"}', '#')
470 self.assertEqual(f'{d["#"]}', 'hash')
471
Eric V. Smith09835dc2016-09-11 18:58:20 -0400472 self.assertAllRaise(SyntaxError, "f-string expression part cannot include '#'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400473 ["f'{1#}'", # error because the expression becomes "(1#)"
474 "f'{3(#)}'",
Eric V. Smith09835dc2016-09-11 18:58:20 -0400475 "f'{#}'",
Serhiy Storchaka58159ef2019-01-12 09:46:50 +0200476 ])
477 self.assertAllRaise(SyntaxError, r"f-string: unmatched '\)'",
478 ["f'{)#}'", # When wrapped in parens, this becomes
Eric V. Smith35a24c52016-09-11 19:01:22 -0400479 # '()#)'. Make sure that doesn't compile.
Eric V. Smith235a6f02015-09-19 14:51:32 -0400480 ])
481
482 def test_many_expressions(self):
483 # Create a string with many expressions in it. Note that
484 # because we have a space in here as a literal, we're actually
485 # going to use twice as many ast nodes: one for each literal
486 # plus one for each expression.
487 def build_fstr(n, extra=''):
488 return "f'" + ('{x} ' * n) + extra + "'"
489
490 x = 'X'
491 width = 1
492
493 # Test around 256.
494 for i in range(250, 260):
495 self.assertEqual(eval(build_fstr(i)), (x+' ')*i)
496
497 # Test concatenating 2 largs fstrings.
498 self.assertEqual(eval(build_fstr(255)*256), (x+' ')*(255*256))
499
500 s = build_fstr(253, '{x:{width}} ')
501 self.assertEqual(eval(s), (x+' ')*254)
502
503 # Test lots of expressions and constants, concatenated.
504 s = "f'{1}' 'x' 'y'" * 1024
505 self.assertEqual(eval(s), '1xy' * 1024)
506
507 def test_format_specifier_expressions(self):
508 width = 10
509 precision = 4
510 value = decimal.Decimal('12.34567')
511 self.assertEqual(f'result: {value:{width}.{precision}}', 'result: 12.35')
512 self.assertEqual(f'result: {value:{width!r}.{precision}}', 'result: 12.35')
513 self.assertEqual(f'result: {value:{width:0}.{precision:1}}', 'result: 12.35')
514 self.assertEqual(f'result: {value:{1}{0:0}.{precision:1}}', 'result: 12.35')
515 self.assertEqual(f'result: {value:{ 1}{ 0:0}.{ precision:1}}', 'result: 12.35')
516 self.assertEqual(f'{10:#{1}0x}', ' 0xa')
517 self.assertEqual(f'{10:{"#"}1{0}{"x"}}', ' 0xa')
518 self.assertEqual(f'{-10:-{"#"}1{0}x}', ' -0xa')
519 self.assertEqual(f'{-10:{"-"}#{1}0{"x"}}', ' -0xa')
520 self.assertEqual(f'{10:#{3 != {4:5} and width}x}', ' 0xa')
521
522 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
523 ["""f'{"s"!r{":10"}}'""",
524
525 # This looks like a nested format spec.
526 ])
527
Lysandros Nikolaou990efe02020-06-29 15:55:57 +0300528 err_msg = "invalid syntax" if use_old_parser() else "f-string: invalid syntax"
529 self.assertAllRaise(SyntaxError, err_msg,
Martin Panter263893c2016-07-28 01:25:31 +0000530 [# Invalid syntax inside a nested spec.
Eric V. Smith235a6f02015-09-19 14:51:32 -0400531 "f'{4:{/5}}'",
532 ])
533
534 self.assertAllRaise(SyntaxError, "f-string: expressions nested too deeply",
535 [# Can't nest format specifiers.
536 "f'result: {value:{width:{0}}.{precision:1}}'",
537 ])
538
539 self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character',
540 [# No expansion inside conversion or for
541 # the : or ! itself.
542 """f'{"s"!{"r"}}'""",
543 ])
544
545 def test_side_effect_order(self):
546 class X:
547 def __init__(self):
548 self.i = 0
549 def __format__(self, spec):
550 self.i += 1
551 return str(self.i)
552
553 x = X()
554 self.assertEqual(f'{x} {x}', '1 2')
555
556 def test_missing_expression(self):
557 self.assertAllRaise(SyntaxError, 'f-string: empty expression not allowed',
558 ["f'{}'",
559 "f'{ }'"
560 "f' {} '",
561 "f'{!r}'",
562 "f'{ !r}'",
563 "f'{10:{ }}'",
564 "f' { } '",
Eric V. Smith1d44c412015-09-23 07:49:00 -0400565
Serhiy Storchaka2e9cd582017-06-08 23:43:54 +0300566 # The Python parser ignores also the following
567 # whitespace characters in additional to a space.
568 "f'''{\t\f\r\n}'''",
569
Eric V. Smith1d44c412015-09-23 07:49:00 -0400570 # Catch the empty expression before the
571 # invalid conversion.
572 "f'{!x}'",
573 "f'{ !xr}'",
574 "f'{!x:}'",
575 "f'{!x:a}'",
576 "f'{ !xr:}'",
577 "f'{ !xr:a}'",
Eric V. Smith548c4d32015-09-23 08:00:01 -0400578
579 "f'{!}'",
580 "f'{:}'",
Eric V. Smithb2080f62015-09-23 10:24:43 -0400581
582 # We find the empty expression before the
583 # missing closing brace.
584 "f'{!'",
585 "f'{!s:'",
586 "f'{:'",
587 "f'{:x'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400588 ])
589
Serhiy Storchaka2e9cd582017-06-08 23:43:54 +0300590 # Different error message is raised for other whitespace characters.
Serhiy Storchaka74ea6b52020-05-12 12:42:04 +0300591 self.assertAllRaise(SyntaxError, r"invalid non-printable character U\+00A0",
Serhiy Storchaka2e9cd582017-06-08 23:43:54 +0300592 ["f'''{\xa0}'''",
593 "\xa0",
594 ])
595
Eric V. Smith235a6f02015-09-19 14:51:32 -0400596 def test_parens_in_expressions(self):
597 self.assertEqual(f'{3,}', '(3,)')
598
599 # Add these because when an expression is evaluated, parens
600 # are added around it. But we shouldn't go from an invalid
601 # expression to a valid one. The added parens are just
602 # supposed to allow whitespace (including newlines).
Lysandros Nikolaou990efe02020-06-29 15:55:57 +0300603 err_msg = "invalid syntax" if use_old_parser() else "f-string: invalid syntax"
604 self.assertAllRaise(SyntaxError, err_msg,
Eric V. Smith235a6f02015-09-19 14:51:32 -0400605 ["f'{,}'",
606 "f'{,}'", # this is (,), which is an error
607 ])
608
Serhiy Storchaka58159ef2019-01-12 09:46:50 +0200609 self.assertAllRaise(SyntaxError, r"f-string: unmatched '\)'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400610 ["f'{3)+(4}'",
611 ])
612
613 self.assertAllRaise(SyntaxError, 'EOL while scanning string literal',
614 ["f'{\n}'",
615 ])
616
Eric V. Smith451d0e32016-09-09 21:56:20 -0400617 def test_backslashes_in_string_part(self):
618 self.assertEqual(f'\t', '\t')
619 self.assertEqual(r'\t', '\\t')
620 self.assertEqual(rf'\t', '\\t')
621 self.assertEqual(f'{2}\t', '2\t')
622 self.assertEqual(f'{2}\t{3}', '2\t3')
623 self.assertEqual(f'\t{3}', '\t3')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400624
Eric V. Smith451d0e32016-09-09 21:56:20 -0400625 self.assertEqual(f'\u0394', '\u0394')
626 self.assertEqual(r'\u0394', '\\u0394')
627 self.assertEqual(rf'\u0394', '\\u0394')
628 self.assertEqual(f'{2}\u0394', '2\u0394')
629 self.assertEqual(f'{2}\u0394{3}', '2\u03943')
630 self.assertEqual(f'\u0394{3}', '\u03943')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400631
Eric V. Smith451d0e32016-09-09 21:56:20 -0400632 self.assertEqual(f'\U00000394', '\u0394')
633 self.assertEqual(r'\U00000394', '\\U00000394')
634 self.assertEqual(rf'\U00000394', '\\U00000394')
635 self.assertEqual(f'{2}\U00000394', '2\u0394')
636 self.assertEqual(f'{2}\U00000394{3}', '2\u03943')
637 self.assertEqual(f'\U00000394{3}', '\u03943')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400638
Eric V. Smith451d0e32016-09-09 21:56:20 -0400639 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}', '\u0394')
640 self.assertEqual(f'{2}\N{GREEK CAPITAL LETTER DELTA}', '2\u0394')
641 self.assertEqual(f'{2}\N{GREEK CAPITAL LETTER DELTA}{3}', '2\u03943')
642 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}{3}', '\u03943')
643 self.assertEqual(f'2\N{GREEK CAPITAL LETTER DELTA}', '2\u0394')
644 self.assertEqual(f'2\N{GREEK CAPITAL LETTER DELTA}3', '2\u03943')
645 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}3', '\u03943')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400646
Eric V. Smith451d0e32016-09-09 21:56:20 -0400647 self.assertEqual(f'\x20', ' ')
648 self.assertEqual(r'\x20', '\\x20')
649 self.assertEqual(rf'\x20', '\\x20')
650 self.assertEqual(f'{2}\x20', '2 ')
651 self.assertEqual(f'{2}\x20{3}', '2 3')
652 self.assertEqual(f'\x20{3}', ' 3')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400653
Eric V. Smith451d0e32016-09-09 21:56:20 -0400654 self.assertEqual(f'2\x20', '2 ')
655 self.assertEqual(f'2\x203', '2 3')
656 self.assertEqual(f'\x203', ' 3')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400657
Gregory P. Smithb4be87a2019-08-10 00:19:07 -0700658 with self.assertWarns(DeprecationWarning): # invalid escape sequence
Serhiy Storchaka0cd7a3f2017-05-25 13:33:55 +0300659 value = eval(r"f'\{6*7}'")
660 self.assertEqual(value, '\\42')
661 self.assertEqual(f'\\{6*7}', '\\42')
662 self.assertEqual(fr'\{6*7}', '\\42')
663
664 AMPERSAND = 'spam'
665 # Get the right unicode character (&), or pick up local variable
666 # depending on the number of backslashes.
667 self.assertEqual(f'\N{AMPERSAND}', '&')
668 self.assertEqual(f'\\N{AMPERSAND}', '\\Nspam')
669 self.assertEqual(fr'\N{AMPERSAND}', '\\Nspam')
670 self.assertEqual(f'\\\N{AMPERSAND}', '\\&')
671
Eric V. Smith451d0e32016-09-09 21:56:20 -0400672 def test_misformed_unicode_character_name(self):
673 # These test are needed because unicode names are parsed
674 # differently inside f-strings.
675 self.assertAllRaise(SyntaxError, r"\(unicode error\) 'unicodeescape' codec can't decode bytes in position .*: malformed \\N character escape",
676 [r"f'\N'",
677 r"f'\N{'",
678 r"f'\N{GREEK CAPITAL LETTER DELTA'",
679
680 # Here are the non-f-string versions,
681 # which should give the same errors.
682 r"'\N'",
683 r"'\N{'",
684 r"'\N{GREEK CAPITAL LETTER DELTA'",
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400685 ])
686
Eric V. Smith451d0e32016-09-09 21:56:20 -0400687 def test_no_backslashes_in_expression_part(self):
688 self.assertAllRaise(SyntaxError, 'f-string expression part cannot include a backslash',
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400689 [r"f'{\'a\'}'",
690 r"f'{\t3}'",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400691 r"f'{\}'",
692 r"rf'{\'a\'}'",
693 r"rf'{\t3}'",
694 r"rf'{\}'",
695 r"""rf'{"\N{LEFT CURLY BRACKET}"}'""",
Jason R. Coombs45cab8c2016-11-06 11:01:08 -0500696 r"f'{\n}'",
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400697 ])
698
Eric V. Smith451d0e32016-09-09 21:56:20 -0400699 def test_no_escapes_for_braces(self):
Jason R. Coombs1c92a762016-11-06 11:25:54 -0500700 """
701 Only literal curly braces begin an expression.
702 """
703 # \x7b is '{'.
704 self.assertEqual(f'\x7b1+1}}', '{1+1}')
705 self.assertEqual(f'\x7b1+1', '{1+1')
706 self.assertEqual(f'\u007b1+1', '{1+1')
707 self.assertEqual(f'\N{LEFT CURLY BRACKET}1+1\N{RIGHT CURLY BRACKET}', '{1+1}')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400708
Eric V. Smith235a6f02015-09-19 14:51:32 -0400709 def test_newlines_in_expressions(self):
710 self.assertEqual(f'{0}', '0')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400711 self.assertEqual(rf'''{3+
7124}''', '7')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400713
714 def test_lambda(self):
715 x = 5
716 self.assertEqual(f'{(lambda y:x*y)("8")!r}', "'88888'")
717 self.assertEqual(f'{(lambda y:x*y)("8")!r:10}', "'88888' ")
718 self.assertEqual(f'{(lambda y:x*y)("8"):10}', "88888 ")
719
720 # lambda doesn't work without parens, because the colon
721 # makes the parser think it's a format_spec
Lysandros Nikolaou990efe02020-06-29 15:55:57 +0300722 err_msg = "invalid syntax" if use_old_parser() else "f-string: invalid syntax"
723 self.assertAllRaise(SyntaxError, err_msg,
Eric V. Smith235a6f02015-09-19 14:51:32 -0400724 ["f'{lambda x:x}'",
725 ])
726
727 def test_yield(self):
728 # Not terribly useful, but make sure the yield turns
729 # a function into a generator
730 def fn(y):
731 f'y:{yield y*2}'
Miss Islington (bot)64409112020-06-07 18:08:53 -0700732 f'{yield}'
Eric V. Smith235a6f02015-09-19 14:51:32 -0400733
734 g = fn(4)
735 self.assertEqual(next(g), 8)
Miss Islington (bot)64409112020-06-07 18:08:53 -0700736 self.assertEqual(next(g), None)
Eric V. Smith235a6f02015-09-19 14:51:32 -0400737
738 def test_yield_send(self):
739 def fn(x):
740 yield f'x:{yield (lambda i: x * i)}'
741
742 g = fn(10)
743 the_lambda = next(g)
744 self.assertEqual(the_lambda(4), 40)
745 self.assertEqual(g.send('string'), 'x:string')
746
747 def test_expressions_with_triple_quoted_strings(self):
748 self.assertEqual(f"{'''x'''}", 'x')
749 self.assertEqual(f"{'''eric's'''}", "eric's")
Eric V. Smith235a6f02015-09-19 14:51:32 -0400750
751 # Test concatenation within an expression
752 self.assertEqual(f'{"x" """eric"s""" "y"}', 'xeric"sy')
753 self.assertEqual(f'{"x" """eric"s"""}', 'xeric"s')
754 self.assertEqual(f'{"""eric"s""" "y"}', 'eric"sy')
755 self.assertEqual(f'{"""x""" """eric"s""" "y"}', 'xeric"sy')
756 self.assertEqual(f'{"""x""" """eric"s""" """y"""}', 'xeric"sy')
757 self.assertEqual(f'{r"""x""" """eric"s""" """y"""}', 'xeric"sy')
758
759 def test_multiple_vars(self):
760 x = 98
761 y = 'abc'
762 self.assertEqual(f'{x}{y}', '98abc')
763
764 self.assertEqual(f'X{x}{y}', 'X98abc')
765 self.assertEqual(f'{x}X{y}', '98Xabc')
766 self.assertEqual(f'{x}{y}X', '98abcX')
767
768 self.assertEqual(f'X{x}Y{y}', 'X98Yabc')
769 self.assertEqual(f'X{x}{y}Y', 'X98abcY')
770 self.assertEqual(f'{x}X{y}Y', '98XabcY')
771
772 self.assertEqual(f'X{x}Y{y}Z', 'X98YabcZ')
773
774 def test_closure(self):
775 def outer(x):
776 def inner():
777 return f'x:{x}'
778 return inner
779
780 self.assertEqual(outer('987')(), 'x:987')
781 self.assertEqual(outer(7)(), 'x:7')
782
783 def test_arguments(self):
784 y = 2
785 def f(x, width):
786 return f'x={x*y:{width}}'
787
788 self.assertEqual(f('foo', 10), 'x=foofoo ')
789 x = 'bar'
790 self.assertEqual(f(10, 10), 'x= 20')
791
792 def test_locals(self):
793 value = 123
794 self.assertEqual(f'v:{value}', 'v:123')
795
796 def test_missing_variable(self):
797 with self.assertRaises(NameError):
798 f'v:{value}'
799
800 def test_missing_format_spec(self):
801 class O:
802 def __format__(self, spec):
803 if not spec:
804 return '*'
805 return spec
806
807 self.assertEqual(f'{O():x}', 'x')
808 self.assertEqual(f'{O()}', '*')
809 self.assertEqual(f'{O():}', '*')
810
811 self.assertEqual(f'{3:}', '3')
812 self.assertEqual(f'{3!s:}', '3')
813
814 def test_global(self):
815 self.assertEqual(f'g:{a_global}', 'g:global variable')
816 self.assertEqual(f'g:{a_global!r}', "g:'global variable'")
817
818 a_local = 'local variable'
819 self.assertEqual(f'g:{a_global} l:{a_local}',
820 'g:global variable l:local variable')
821 self.assertEqual(f'g:{a_global!r}',
822 "g:'global variable'")
823 self.assertEqual(f'g:{a_global} l:{a_local!r}',
824 "g:global variable l:'local variable'")
825
826 self.assertIn("module 'unittest' from", f'{unittest}')
827
828 def test_shadowed_global(self):
829 a_global = 'really a local'
830 self.assertEqual(f'g:{a_global}', 'g:really a local')
831 self.assertEqual(f'g:{a_global!r}', "g:'really a local'")
832
833 a_local = 'local variable'
834 self.assertEqual(f'g:{a_global} l:{a_local}',
835 'g:really a local l:local variable')
836 self.assertEqual(f'g:{a_global!r}',
837 "g:'really a local'")
838 self.assertEqual(f'g:{a_global} l:{a_local!r}',
839 "g:really a local l:'local variable'")
840
841 def test_call(self):
842 def foo(x):
843 return 'x=' + str(x)
844
845 self.assertEqual(f'{foo(10)}', 'x=10')
846
847 def test_nested_fstrings(self):
848 y = 5
849 self.assertEqual(f'{f"{0}"*3}', '000')
850 self.assertEqual(f'{f"{y}"*3}', '555')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400851
852 def test_invalid_string_prefixes(self):
Pablo Galindo70c188e2020-04-13 02:47:35 +0100853 single_quote_cases = ["fu''",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400854 "uf''",
855 "Fu''",
856 "fU''",
857 "Uf''",
858 "uF''",
859 "ufr''",
860 "urf''",
861 "fur''",
862 "fru''",
863 "rfu''",
864 "ruf''",
865 "FUR''",
866 "Fur''",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400867 "fb''",
868 "fB''",
869 "Fb''",
870 "FB''",
871 "bf''",
872 "bF''",
873 "Bf''",
Pablo Galindo70c188e2020-04-13 02:47:35 +0100874 "BF''",]
875 double_quote_cases = [case.replace("'", '"') for case in single_quote_cases]
Lysandros Nikolaou6cb0ad22020-05-26 03:10:00 +0300876 error_msg = (
877 'invalid syntax'
878 if use_old_parser()
879 else 'unexpected EOF while parsing'
880 )
881 self.assertAllRaise(SyntaxError, error_msg,
Pablo Galindo70c188e2020-04-13 02:47:35 +0100882 single_quote_cases + double_quote_cases)
Eric V. Smith235a6f02015-09-19 14:51:32 -0400883
884 def test_leading_trailing_spaces(self):
885 self.assertEqual(f'{ 3}', '3')
886 self.assertEqual(f'{ 3}', '3')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400887 self.assertEqual(f'{3 }', '3')
888 self.assertEqual(f'{3 }', '3')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400889
890 self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]}}',
891 'expr={1: 2}')
892 self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]} }',
893 'expr={1: 2}')
894
Eric V. Smith235a6f02015-09-19 14:51:32 -0400895 def test_not_equal(self):
896 # There's a special test for this because there's a special
897 # case in the f-string parser to look for != as not ending an
898 # expression. Normally it would, while looking for !s or !r.
899
900 self.assertEqual(f'{3!=4}', 'True')
901 self.assertEqual(f'{3!=4:}', 'True')
902 self.assertEqual(f'{3!=4!s}', 'True')
903 self.assertEqual(f'{3!=4!s:.3}', 'Tru')
904
Eric V. Smith9a4135e2019-05-08 16:28:48 -0400905 def test_equal_equal(self):
906 # Because an expression ending in = has special meaning,
907 # there's a special test for ==. Make sure it works.
908
909 self.assertEqual(f'{0==1}', 'False')
910
Eric V. Smith235a6f02015-09-19 14:51:32 -0400911 def test_conversions(self):
912 self.assertEqual(f'{3.14:10.10}', ' 3.14')
913 self.assertEqual(f'{3.14!s:10.10}', '3.14 ')
914 self.assertEqual(f'{3.14!r:10.10}', '3.14 ')
915 self.assertEqual(f'{3.14!a:10.10}', '3.14 ')
916
917 self.assertEqual(f'{"a"}', 'a')
918 self.assertEqual(f'{"a"!r}', "'a'")
919 self.assertEqual(f'{"a"!a}', "'a'")
920
921 # Not a conversion.
922 self.assertEqual(f'{"a!r"}', "a!r")
923
924 # Not a conversion, but show that ! is allowed in a format spec.
925 self.assertEqual(f'{3.14:!<10.10}', '3.14!!!!!!')
926
Eric V. Smith235a6f02015-09-19 14:51:32 -0400927 self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character',
928 ["f'{3!g}'",
929 "f'{3!A}'",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400930 "f'{3!3}'",
931 "f'{3!G}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400932 "f'{3!!}'",
933 "f'{3!:}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400934 "f'{3! s}'", # no space before conversion char
Eric V. Smith235a6f02015-09-19 14:51:32 -0400935 ])
936
937 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
938 ["f'{x!s{y}}'",
939 "f'{3!ss}'",
940 "f'{3!ss:}'",
941 "f'{3!ss:s}'",
942 ])
943
944 def test_assignment(self):
945 self.assertAllRaise(SyntaxError, 'invalid syntax',
946 ["f'' = 3",
947 "f'{0}' = x",
948 "f'{x}' = x",
949 ])
950
951 def test_del(self):
952 self.assertAllRaise(SyntaxError, 'invalid syntax',
953 ["del f''",
954 "del '' f''",
955 ])
956
957 def test_mismatched_braces(self):
958 self.assertAllRaise(SyntaxError, "f-string: single '}' is not allowed",
959 ["f'{{}'",
960 "f'{{}}}'",
961 "f'}'",
962 "f'x}'",
963 "f'x}x'",
Jason R. Coombsda25abf72016-11-06 11:14:48 -0500964 r"f'\u007b}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400965
966 # Can't have { or } in a format spec.
967 "f'{3:}>10}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400968 "f'{3:}}>10}'",
969 ])
970
971 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
972 ["f'{3:{{>10}'",
973 "f'{3'",
974 "f'{3!'",
975 "f'{3:'",
976 "f'{3!s'",
977 "f'{3!s:'",
978 "f'{3!s:3'",
979 "f'x{'",
980 "f'x{x'",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400981 "f'{x'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400982 "f'{3:s'",
983 "f'{{{'",
984 "f'{{}}{'",
985 "f'{'",
986 ])
987
Eric V. Smith235a6f02015-09-19 14:51:32 -0400988 # But these are just normal strings.
989 self.assertEqual(f'{"{"}', '{')
990 self.assertEqual(f'{"}"}', '}')
991 self.assertEqual(f'{3:{"}"}>10}', '}}}}}}}}}3')
992 self.assertEqual(f'{2:{"{"}>10}', '{{{{{{{{{2')
993
994 def test_if_conditional(self):
995 # There's special logic in compile.c to test if the
996 # conditional for an if (and while) are constants. Exercise
997 # that code.
998
999 def test_fstring(x, expected):
1000 flag = 0
1001 if f'{x}':
1002 flag = 1
1003 else:
1004 flag = 2
1005 self.assertEqual(flag, expected)
1006
1007 def test_concat_empty(x, expected):
1008 flag = 0
1009 if '' f'{x}':
1010 flag = 1
1011 else:
1012 flag = 2
1013 self.assertEqual(flag, expected)
1014
1015 def test_concat_non_empty(x, expected):
1016 flag = 0
1017 if ' ' f'{x}':
1018 flag = 1
1019 else:
1020 flag = 2
1021 self.assertEqual(flag, expected)
1022
1023 test_fstring('', 2)
1024 test_fstring(' ', 1)
1025
1026 test_concat_empty('', 2)
1027 test_concat_empty(' ', 1)
1028
1029 test_concat_non_empty('', 1)
1030 test_concat_non_empty(' ', 1)
1031
1032 def test_empty_format_specifier(self):
1033 x = 'test'
1034 self.assertEqual(f'{x}', 'test')
1035 self.assertEqual(f'{x:}', 'test')
1036 self.assertEqual(f'{x!s:}', 'test')
1037 self.assertEqual(f'{x!r:}', "'test'")
1038
1039 def test_str_format_differences(self):
1040 d = {'a': 'string',
1041 0: 'integer',
1042 }
1043 a = 0
1044 self.assertEqual(f'{d[0]}', 'integer')
1045 self.assertEqual(f'{d["a"]}', 'string')
1046 self.assertEqual(f'{d[a]}', 'integer')
1047 self.assertEqual('{d[a]}'.format(d=d), 'string')
1048 self.assertEqual('{d[0]}'.format(d=d), 'integer')
1049
Eric V. Smith135d5f42016-02-05 18:23:08 -05001050 def test_errors(self):
1051 # see issue 26287
Serhiy Storchaka13c8f322016-10-31 08:13:00 +02001052 self.assertAllRaise(TypeError, 'unsupported',
Eric V. Smith135d5f42016-02-05 18:23:08 -05001053 [r"f'{(lambda: 0):x}'",
1054 r"f'{(0,):x}'",
1055 ])
1056 self.assertAllRaise(ValueError, 'Unknown format code',
1057 [r"f'{1000:j}'",
1058 r"f'{1000:j}'",
1059 ])
1060
Lysandros Nikolaou791a46e2020-05-26 04:24:31 +03001061 @unittest.skipIf(use_old_parser(), "The old parser only supports <fstring> as the filename")
1062 def test_filename_in_syntaxerror(self):
1063 # see issue 38964
1064 with temp_cwd() as cwd:
1065 file_path = os.path.join(cwd, 't.py')
1066 with open(file_path, 'w') as f:
1067 f.write('f"{a b}"') # This generates a SyntaxError
Serhiy Storchakaf9254072020-06-25 20:39:12 +03001068 _, _, stderr = assert_python_failure(file_path,
1069 PYTHONIOENCODING='ascii')
1070 self.assertIn(file_path.encode('ascii', 'backslashreplace'), stderr)
Lysandros Nikolaou791a46e2020-05-26 04:24:31 +03001071
Eric V. Smith235a6f02015-09-19 14:51:32 -04001072 def test_loop(self):
1073 for i in range(1000):
1074 self.assertEqual(f'i:{i}', 'i:' + str(i))
1075
1076 def test_dict(self):
1077 d = {'"': 'dquote',
1078 "'": 'squote',
1079 'foo': 'bar',
1080 }
Eric V. Smith235a6f02015-09-19 14:51:32 -04001081 self.assertEqual(f'''{d["'"]}''', 'squote')
1082 self.assertEqual(f"""{d['"']}""", 'dquote')
1083
1084 self.assertEqual(f'{d["foo"]}', 'bar')
1085 self.assertEqual(f"{d['foo']}", 'bar')
Eric V. Smith235a6f02015-09-19 14:51:32 -04001086
ericvsmith11e97f22017-06-16 06:19:32 -04001087 def test_backslash_char(self):
1088 # Check eval of a backslash followed by a control char.
1089 # See bpo-30682: this used to raise an assert in pydebug mode.
1090 self.assertEqual(eval('f"\\\n"'), '')
1091 self.assertEqual(eval('f"\\\r"'), '')
1092
Eric V. Smith9a4135e2019-05-08 16:28:48 -04001093 def test_debug_conversion(self):
1094 x = 'A string'
1095 self.assertEqual(f'{x=}', 'x=' + repr(x))
1096 self.assertEqual(f'{x =}', 'x =' + repr(x))
1097 self.assertEqual(f'{x=!s}', 'x=' + str(x))
1098 self.assertEqual(f'{x=!r}', 'x=' + repr(x))
1099 self.assertEqual(f'{x=!a}', 'x=' + ascii(x))
1100
1101 x = 2.71828
1102 self.assertEqual(f'{x=:.2f}', 'x=' + format(x, '.2f'))
1103 self.assertEqual(f'{x=:}', 'x=' + format(x, ''))
1104 self.assertEqual(f'{x=!r:^20}', 'x=' + format(repr(x), '^20'))
1105 self.assertEqual(f'{x=!s:^20}', 'x=' + format(str(x), '^20'))
1106 self.assertEqual(f'{x=!a:^20}', 'x=' + format(ascii(x), '^20'))
1107
1108 x = 9
1109 self.assertEqual(f'{3*x+15=}', '3*x+15=42')
1110
1111 # There is code in ast.c that deals with non-ascii expression values. So,
1112 # use a unicode identifier to trigger that.
1113 tenπ = 31.4
1114 self.assertEqual(f'{tenπ=:.2f}', 'tenπ=31.40')
1115
1116 # Also test with Unicode in non-identifiers.
1117 self.assertEqual(f'{"Σ"=}', '"Σ"=\'Σ\'')
1118
1119 # Make sure nested fstrings still work.
1120 self.assertEqual(f'{f"{3.1415=:.1f}":*^20}', '*****3.1415=3.1*****')
1121
1122 # Make sure text before and after an expression with = works
1123 # correctly.
1124 pi = 'π'
1125 self.assertEqual(f'alpha α {pi=} ω omega', "alpha α pi='π' ω omega")
1126
1127 # Check multi-line expressions.
1128 self.assertEqual(f'''{
11293
1130=}''', '\n3\n=3')
1131
1132 # Since = is handled specially, make sure all existing uses of
1133 # it still work.
1134
1135 self.assertEqual(f'{0==1}', 'False')
1136 self.assertEqual(f'{0!=1}', 'True')
1137 self.assertEqual(f'{0<=1}', 'True')
1138 self.assertEqual(f'{0>=1}', 'False')
1139 self.assertEqual(f'{(x:="5")}', '5')
1140 self.assertEqual(x, '5')
1141 self.assertEqual(f'{(x:=5)}', '5')
1142 self.assertEqual(x, 5)
1143 self.assertEqual(f'{"="}', '=')
1144
1145 x = 20
1146 # This isn't an assignment expression, it's 'x', with a format
1147 # spec of '=10'. See test_walrus: you need to use parens.
1148 self.assertEqual(f'{x:=10}', ' 20')
1149
1150 # Test named function parameters, to make sure '=' parsing works
1151 # there.
1152 def f(a):
1153 nonlocal x
1154 oldx = x
1155 x = a
1156 return oldx
1157 x = 0
1158 self.assertEqual(f'{f(a="3=")}', '0')
1159 self.assertEqual(x, '3=')
1160 self.assertEqual(f'{f(a=4)}', '3=')
1161 self.assertEqual(x, 4)
1162
1163 # Make sure __format__ is being called.
1164 class C:
1165 def __format__(self, s):
1166 return f'FORMAT-{s}'
1167 def __repr__(self):
1168 return 'REPR'
1169
1170 self.assertEqual(f'{C()=}', 'C()=REPR')
1171 self.assertEqual(f'{C()=!r}', 'C()=REPR')
1172 self.assertEqual(f'{C()=:}', 'C()=FORMAT-')
1173 self.assertEqual(f'{C()=: }', 'C()=FORMAT- ')
1174 self.assertEqual(f'{C()=:x}', 'C()=FORMAT-x')
1175 self.assertEqual(f'{C()=!r:*^20}', 'C()=********REPR********')
1176
Pablo Galindo26f55c22019-05-12 01:43:04 +01001177 self.assertRaises(SyntaxError, eval, "f'{C=]'")
1178
Eric V. Smith6f6ff8a2019-05-27 15:31:52 -04001179 # Make sure leading and following text works.
1180 x = 'foo'
1181 self.assertEqual(f'X{x=}Y', 'Xx='+repr(x)+'Y')
1182
1183 # Make sure whitespace around the = works.
1184 self.assertEqual(f'X{x =}Y', 'Xx ='+repr(x)+'Y')
1185 self.assertEqual(f'X{x= }Y', 'Xx= '+repr(x)+'Y')
1186 self.assertEqual(f'X{x = }Y', 'Xx = '+repr(x)+'Y')
1187
1188 # These next lines contains tabs. Backslash escapes don't
1189 # work in f-strings.
Min ho Kim96e12d52019-07-22 06:12:33 +10001190 # patchcheck doesn't like these tabs. So the only way to test
Eric V. Smith6f6ff8a2019-05-27 15:31:52 -04001191 # this will be to dynamically created and exec the f-strings. But
1192 # that's such a hassle I'll save it for another day. For now, convert
1193 # the tabs to spaces just to shut up patchcheck.
1194 #self.assertEqual(f'X{x =}Y', 'Xx\t='+repr(x)+'Y')
1195 #self.assertEqual(f'X{x = }Y', 'Xx\t=\t'+repr(x)+'Y')
1196
Eric V. Smith9a4135e2019-05-08 16:28:48 -04001197 def test_walrus(self):
1198 x = 20
1199 # This isn't an assignment expression, it's 'x', with a format
1200 # spec of '=10'.
1201 self.assertEqual(f'{x:=10}', ' 20')
1202
1203 # This is an assignment expression, which requires parens.
1204 self.assertEqual(f'{(x:=10)}', '10')
1205 self.assertEqual(x, 10)
1206
Miss Islington (bot)cb0dc522020-06-27 12:43:49 -07001207 def test_invalid_syntax_error_message(self):
Lysandros Nikolaou990efe02020-06-29 15:55:57 +03001208 err_msg = "invalid syntax" if use_old_parser() else "f-string: invalid syntax"
1209 with self.assertRaisesRegex(SyntaxError, err_msg):
Miss Islington (bot)cb0dc522020-06-27 12:43:49 -07001210 compile("f'{a $ b}'", "?", "exec")
1211
Miss Islington (bot)c16a2a12020-09-01 08:45:59 -07001212 def test_with_two_commas_in_format_specifier(self):
1213 error_msg = re.escape("Cannot specify ',' with ','.")
1214 with self.assertRaisesRegex(ValueError, error_msg):
1215 f'{1:,,}'
1216
1217 def test_with_two_underscore_in_format_specifier(self):
1218 error_msg = re.escape("Cannot specify '_' with '_'.")
1219 with self.assertRaisesRegex(ValueError, error_msg):
1220 f'{1:__}'
1221
1222 def test_with_a_commas_and_an_underscore_in_format_specifier(self):
1223 error_msg = re.escape("Cannot specify both ',' and '_'.")
1224 with self.assertRaisesRegex(ValueError, error_msg):
1225 f'{1:,_}'
1226
1227 def test_with_an_underscore_and_a_comma_in_format_specifier(self):
1228 error_msg = re.escape("Cannot specify both ',' and '_'.")
1229 with self.assertRaisesRegex(ValueError, error_msg):
1230 f'{1:,_}'
Łukasz Langae7c566c2017-09-06 17:27:58 -07001231
Eric V. Smith235a6f02015-09-19 14:51:32 -04001232if __name__ == '__main__':
1233 unittest.main()