blob: b9bede0d9b800ea4e08dfb3c96e8257942f4f9c8 [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
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 Nikolaou2e0a9202020-06-26 14:24:05 +0300528 self.assertAllRaise(SyntaxError, "f-string: invalid syntax",
Martin Panter263893c2016-07-28 01:25:31 +0000529 [# Invalid syntax inside a nested spec.
Eric V. Smith235a6f02015-09-19 14:51:32 -0400530 "f'{4:{/5}}'",
531 ])
532
533 self.assertAllRaise(SyntaxError, "f-string: expressions nested too deeply",
534 [# Can't nest format specifiers.
535 "f'result: {value:{width:{0}}.{precision:1}}'",
536 ])
537
538 self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character',
539 [# No expansion inside conversion or for
540 # the : or ! itself.
541 """f'{"s"!{"r"}}'""",
542 ])
543
544 def test_side_effect_order(self):
545 class X:
546 def __init__(self):
547 self.i = 0
548 def __format__(self, spec):
549 self.i += 1
550 return str(self.i)
551
552 x = X()
553 self.assertEqual(f'{x} {x}', '1 2')
554
555 def test_missing_expression(self):
556 self.assertAllRaise(SyntaxError, 'f-string: empty expression not allowed',
557 ["f'{}'",
558 "f'{ }'"
559 "f' {} '",
560 "f'{!r}'",
561 "f'{ !r}'",
562 "f'{10:{ }}'",
563 "f' { } '",
Eric V. Smith1d44c412015-09-23 07:49:00 -0400564
Serhiy Storchaka2e9cd582017-06-08 23:43:54 +0300565 # The Python parser ignores also the following
566 # whitespace characters in additional to a space.
567 "f'''{\t\f\r\n}'''",
568
Eric V. Smith1d44c412015-09-23 07:49:00 -0400569 # Catch the empty expression before the
570 # invalid conversion.
571 "f'{!x}'",
572 "f'{ !xr}'",
573 "f'{!x:}'",
574 "f'{!x:a}'",
575 "f'{ !xr:}'",
576 "f'{ !xr:a}'",
Eric V. Smith548c4d32015-09-23 08:00:01 -0400577
578 "f'{!}'",
579 "f'{:}'",
Eric V. Smithb2080f62015-09-23 10:24:43 -0400580
581 # We find the empty expression before the
582 # missing closing brace.
583 "f'{!'",
584 "f'{!s:'",
585 "f'{:'",
586 "f'{:x'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400587 ])
588
Serhiy Storchaka2e9cd582017-06-08 23:43:54 +0300589 # Different error message is raised for other whitespace characters.
Serhiy Storchaka74ea6b52020-05-12 12:42:04 +0300590 self.assertAllRaise(SyntaxError, r"invalid non-printable character U\+00A0",
Serhiy Storchaka2e9cd582017-06-08 23:43:54 +0300591 ["f'''{\xa0}'''",
592 "\xa0",
593 ])
594
Eric V. Smith235a6f02015-09-19 14:51:32 -0400595 def test_parens_in_expressions(self):
596 self.assertEqual(f'{3,}', '(3,)')
597
598 # Add these because when an expression is evaluated, parens
599 # are added around it. But we shouldn't go from an invalid
600 # expression to a valid one. The added parens are just
601 # supposed to allow whitespace (including newlines).
Lysandros Nikolaou2e0a9202020-06-26 14:24:05 +0300602 self.assertAllRaise(SyntaxError, 'f-string: invalid syntax',
Eric V. Smith235a6f02015-09-19 14:51:32 -0400603 ["f'{,}'",
604 "f'{,}'", # this is (,), which is an error
605 ])
606
Serhiy Storchaka58159ef2019-01-12 09:46:50 +0200607 self.assertAllRaise(SyntaxError, r"f-string: unmatched '\)'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400608 ["f'{3)+(4}'",
609 ])
610
611 self.assertAllRaise(SyntaxError, 'EOL while scanning string literal',
612 ["f'{\n}'",
613 ])
614
Eric V. Smith451d0e32016-09-09 21:56:20 -0400615 def test_backslashes_in_string_part(self):
616 self.assertEqual(f'\t', '\t')
617 self.assertEqual(r'\t', '\\t')
618 self.assertEqual(rf'\t', '\\t')
619 self.assertEqual(f'{2}\t', '2\t')
620 self.assertEqual(f'{2}\t{3}', '2\t3')
621 self.assertEqual(f'\t{3}', '\t3')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400622
Eric V. Smith451d0e32016-09-09 21:56:20 -0400623 self.assertEqual(f'\u0394', '\u0394')
624 self.assertEqual(r'\u0394', '\\u0394')
625 self.assertEqual(rf'\u0394', '\\u0394')
626 self.assertEqual(f'{2}\u0394', '2\u0394')
627 self.assertEqual(f'{2}\u0394{3}', '2\u03943')
628 self.assertEqual(f'\u0394{3}', '\u03943')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400629
Eric V. Smith451d0e32016-09-09 21:56:20 -0400630 self.assertEqual(f'\U00000394', '\u0394')
631 self.assertEqual(r'\U00000394', '\\U00000394')
632 self.assertEqual(rf'\U00000394', '\\U00000394')
633 self.assertEqual(f'{2}\U00000394', '2\u0394')
634 self.assertEqual(f'{2}\U00000394{3}', '2\u03943')
635 self.assertEqual(f'\U00000394{3}', '\u03943')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400636
Eric V. Smith451d0e32016-09-09 21:56:20 -0400637 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}', '\u0394')
638 self.assertEqual(f'{2}\N{GREEK CAPITAL LETTER DELTA}', '2\u0394')
639 self.assertEqual(f'{2}\N{GREEK CAPITAL LETTER DELTA}{3}', '2\u03943')
640 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}{3}', '\u03943')
641 self.assertEqual(f'2\N{GREEK CAPITAL LETTER DELTA}', '2\u0394')
642 self.assertEqual(f'2\N{GREEK CAPITAL LETTER DELTA}3', '2\u03943')
643 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}3', '\u03943')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400644
Eric V. Smith451d0e32016-09-09 21:56:20 -0400645 self.assertEqual(f'\x20', ' ')
646 self.assertEqual(r'\x20', '\\x20')
647 self.assertEqual(rf'\x20', '\\x20')
648 self.assertEqual(f'{2}\x20', '2 ')
649 self.assertEqual(f'{2}\x20{3}', '2 3')
650 self.assertEqual(f'\x20{3}', ' 3')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400651
Eric V. Smith451d0e32016-09-09 21:56:20 -0400652 self.assertEqual(f'2\x20', '2 ')
653 self.assertEqual(f'2\x203', '2 3')
654 self.assertEqual(f'\x203', ' 3')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400655
Gregory P. Smithb4be87a2019-08-10 00:19:07 -0700656 with self.assertWarns(DeprecationWarning): # invalid escape sequence
Serhiy Storchaka0cd7a3f2017-05-25 13:33:55 +0300657 value = eval(r"f'\{6*7}'")
658 self.assertEqual(value, '\\42')
659 self.assertEqual(f'\\{6*7}', '\\42')
660 self.assertEqual(fr'\{6*7}', '\\42')
661
662 AMPERSAND = 'spam'
663 # Get the right unicode character (&), or pick up local variable
664 # depending on the number of backslashes.
665 self.assertEqual(f'\N{AMPERSAND}', '&')
666 self.assertEqual(f'\\N{AMPERSAND}', '\\Nspam')
667 self.assertEqual(fr'\N{AMPERSAND}', '\\Nspam')
668 self.assertEqual(f'\\\N{AMPERSAND}', '\\&')
669
Eric V. Smith451d0e32016-09-09 21:56:20 -0400670 def test_misformed_unicode_character_name(self):
671 # These test are needed because unicode names are parsed
672 # differently inside f-strings.
673 self.assertAllRaise(SyntaxError, r"\(unicode error\) 'unicodeescape' codec can't decode bytes in position .*: malformed \\N character escape",
674 [r"f'\N'",
675 r"f'\N{'",
676 r"f'\N{GREEK CAPITAL LETTER DELTA'",
677
678 # Here are the non-f-string versions,
679 # which should give the same errors.
680 r"'\N'",
681 r"'\N{'",
682 r"'\N{GREEK CAPITAL LETTER DELTA'",
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400683 ])
684
Eric V. Smith451d0e32016-09-09 21:56:20 -0400685 def test_no_backslashes_in_expression_part(self):
686 self.assertAllRaise(SyntaxError, 'f-string expression part cannot include a backslash',
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400687 [r"f'{\'a\'}'",
688 r"f'{\t3}'",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400689 r"f'{\}'",
690 r"rf'{\'a\'}'",
691 r"rf'{\t3}'",
692 r"rf'{\}'",
693 r"""rf'{"\N{LEFT CURLY BRACKET}"}'""",
Jason R. Coombs45cab8c2016-11-06 11:01:08 -0500694 r"f'{\n}'",
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400695 ])
696
Eric V. Smith451d0e32016-09-09 21:56:20 -0400697 def test_no_escapes_for_braces(self):
Jason R. Coombs1c92a762016-11-06 11:25:54 -0500698 """
699 Only literal curly braces begin an expression.
700 """
701 # \x7b is '{'.
702 self.assertEqual(f'\x7b1+1}}', '{1+1}')
703 self.assertEqual(f'\x7b1+1', '{1+1')
704 self.assertEqual(f'\u007b1+1', '{1+1')
705 self.assertEqual(f'\N{LEFT CURLY BRACKET}1+1\N{RIGHT CURLY BRACKET}', '{1+1}')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400706
Eric V. Smith235a6f02015-09-19 14:51:32 -0400707 def test_newlines_in_expressions(self):
708 self.assertEqual(f'{0}', '0')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400709 self.assertEqual(rf'''{3+
7104}''', '7')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400711
712 def test_lambda(self):
713 x = 5
714 self.assertEqual(f'{(lambda y:x*y)("8")!r}', "'88888'")
715 self.assertEqual(f'{(lambda y:x*y)("8")!r:10}', "'88888' ")
716 self.assertEqual(f'{(lambda y:x*y)("8"):10}', "88888 ")
717
718 # lambda doesn't work without parens, because the colon
719 # makes the parser think it's a format_spec
Lysandros Nikolaou2e0a9202020-06-26 14:24:05 +0300720 self.assertAllRaise(SyntaxError, 'f-string: invalid syntax',
Eric V. Smith235a6f02015-09-19 14:51:32 -0400721 ["f'{lambda x:x}'",
722 ])
723
724 def test_yield(self):
725 # Not terribly useful, but make sure the yield turns
726 # a function into a generator
727 def fn(y):
728 f'y:{yield y*2}'
Pablo Galindo972ab032020-06-08 01:47:37 +0100729 f'{yield}'
Eric V. Smith235a6f02015-09-19 14:51:32 -0400730
731 g = fn(4)
732 self.assertEqual(next(g), 8)
Pablo Galindo972ab032020-06-08 01:47:37 +0100733 self.assertEqual(next(g), None)
Eric V. Smith235a6f02015-09-19 14:51:32 -0400734
735 def test_yield_send(self):
736 def fn(x):
737 yield f'x:{yield (lambda i: x * i)}'
738
739 g = fn(10)
740 the_lambda = next(g)
741 self.assertEqual(the_lambda(4), 40)
742 self.assertEqual(g.send('string'), 'x:string')
743
744 def test_expressions_with_triple_quoted_strings(self):
745 self.assertEqual(f"{'''x'''}", 'x')
746 self.assertEqual(f"{'''eric's'''}", "eric's")
Eric V. Smith235a6f02015-09-19 14:51:32 -0400747
748 # Test concatenation within an expression
749 self.assertEqual(f'{"x" """eric"s""" "y"}', 'xeric"sy')
750 self.assertEqual(f'{"x" """eric"s"""}', 'xeric"s')
751 self.assertEqual(f'{"""eric"s""" "y"}', 'eric"sy')
752 self.assertEqual(f'{"""x""" """eric"s""" "y"}', 'xeric"sy')
753 self.assertEqual(f'{"""x""" """eric"s""" """y"""}', 'xeric"sy')
754 self.assertEqual(f'{r"""x""" """eric"s""" """y"""}', 'xeric"sy')
755
756 def test_multiple_vars(self):
757 x = 98
758 y = 'abc'
759 self.assertEqual(f'{x}{y}', '98abc')
760
761 self.assertEqual(f'X{x}{y}', 'X98abc')
762 self.assertEqual(f'{x}X{y}', '98Xabc')
763 self.assertEqual(f'{x}{y}X', '98abcX')
764
765 self.assertEqual(f'X{x}Y{y}', 'X98Yabc')
766 self.assertEqual(f'X{x}{y}Y', 'X98abcY')
767 self.assertEqual(f'{x}X{y}Y', '98XabcY')
768
769 self.assertEqual(f'X{x}Y{y}Z', 'X98YabcZ')
770
771 def test_closure(self):
772 def outer(x):
773 def inner():
774 return f'x:{x}'
775 return inner
776
777 self.assertEqual(outer('987')(), 'x:987')
778 self.assertEqual(outer(7)(), 'x:7')
779
780 def test_arguments(self):
781 y = 2
782 def f(x, width):
783 return f'x={x*y:{width}}'
784
785 self.assertEqual(f('foo', 10), 'x=foofoo ')
786 x = 'bar'
787 self.assertEqual(f(10, 10), 'x= 20')
788
789 def test_locals(self):
790 value = 123
791 self.assertEqual(f'v:{value}', 'v:123')
792
793 def test_missing_variable(self):
794 with self.assertRaises(NameError):
795 f'v:{value}'
796
797 def test_missing_format_spec(self):
798 class O:
799 def __format__(self, spec):
800 if not spec:
801 return '*'
802 return spec
803
804 self.assertEqual(f'{O():x}', 'x')
805 self.assertEqual(f'{O()}', '*')
806 self.assertEqual(f'{O():}', '*')
807
808 self.assertEqual(f'{3:}', '3')
809 self.assertEqual(f'{3!s:}', '3')
810
811 def test_global(self):
812 self.assertEqual(f'g:{a_global}', 'g:global variable')
813 self.assertEqual(f'g:{a_global!r}', "g:'global variable'")
814
815 a_local = 'local variable'
816 self.assertEqual(f'g:{a_global} l:{a_local}',
817 'g:global variable l:local variable')
818 self.assertEqual(f'g:{a_global!r}',
819 "g:'global variable'")
820 self.assertEqual(f'g:{a_global} l:{a_local!r}',
821 "g:global variable l:'local variable'")
822
823 self.assertIn("module 'unittest' from", f'{unittest}')
824
825 def test_shadowed_global(self):
826 a_global = 'really a local'
827 self.assertEqual(f'g:{a_global}', 'g:really a local')
828 self.assertEqual(f'g:{a_global!r}', "g:'really a local'")
829
830 a_local = 'local variable'
831 self.assertEqual(f'g:{a_global} l:{a_local}',
832 'g:really a local l:local variable')
833 self.assertEqual(f'g:{a_global!r}',
834 "g:'really a local'")
835 self.assertEqual(f'g:{a_global} l:{a_local!r}',
836 "g:really a local l:'local variable'")
837
838 def test_call(self):
839 def foo(x):
840 return 'x=' + str(x)
841
842 self.assertEqual(f'{foo(10)}', 'x=10')
843
844 def test_nested_fstrings(self):
845 y = 5
846 self.assertEqual(f'{f"{0}"*3}', '000')
847 self.assertEqual(f'{f"{y}"*3}', '555')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400848
849 def test_invalid_string_prefixes(self):
Pablo Galindo70c188e2020-04-13 02:47:35 +0100850 single_quote_cases = ["fu''",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400851 "uf''",
852 "Fu''",
853 "fU''",
854 "Uf''",
855 "uF''",
856 "ufr''",
857 "urf''",
858 "fur''",
859 "fru''",
860 "rfu''",
861 "ruf''",
862 "FUR''",
863 "Fur''",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400864 "fb''",
865 "fB''",
866 "Fb''",
867 "FB''",
868 "bf''",
869 "bF''",
870 "Bf''",
Pablo Galindo70c188e2020-04-13 02:47:35 +0100871 "BF''",]
872 double_quote_cases = [case.replace("'", '"') for case in single_quote_cases]
Lysandros Nikolaou846d8b22020-05-04 14:32:18 +0300873 self.assertAllRaise(SyntaxError, 'unexpected EOF while parsing',
Pablo Galindo70c188e2020-04-13 02:47:35 +0100874 single_quote_cases + double_quote_cases)
Eric V. Smith235a6f02015-09-19 14:51:32 -0400875
876 def test_leading_trailing_spaces(self):
877 self.assertEqual(f'{ 3}', '3')
878 self.assertEqual(f'{ 3}', '3')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400879 self.assertEqual(f'{3 }', '3')
880 self.assertEqual(f'{3 }', '3')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400881
882 self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]}}',
883 'expr={1: 2}')
884 self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]} }',
885 'expr={1: 2}')
886
Eric V. Smith235a6f02015-09-19 14:51:32 -0400887 def test_not_equal(self):
888 # There's a special test for this because there's a special
889 # case in the f-string parser to look for != as not ending an
890 # expression. Normally it would, while looking for !s or !r.
891
892 self.assertEqual(f'{3!=4}', 'True')
893 self.assertEqual(f'{3!=4:}', 'True')
894 self.assertEqual(f'{3!=4!s}', 'True')
895 self.assertEqual(f'{3!=4!s:.3}', 'Tru')
896
Eric V. Smith9a4135e2019-05-08 16:28:48 -0400897 def test_equal_equal(self):
898 # Because an expression ending in = has special meaning,
899 # there's a special test for ==. Make sure it works.
900
901 self.assertEqual(f'{0==1}', 'False')
902
Eric V. Smith235a6f02015-09-19 14:51:32 -0400903 def test_conversions(self):
904 self.assertEqual(f'{3.14:10.10}', ' 3.14')
905 self.assertEqual(f'{3.14!s:10.10}', '3.14 ')
906 self.assertEqual(f'{3.14!r:10.10}', '3.14 ')
907 self.assertEqual(f'{3.14!a:10.10}', '3.14 ')
908
909 self.assertEqual(f'{"a"}', 'a')
910 self.assertEqual(f'{"a"!r}', "'a'")
911 self.assertEqual(f'{"a"!a}', "'a'")
912
913 # Not a conversion.
914 self.assertEqual(f'{"a!r"}', "a!r")
915
916 # Not a conversion, but show that ! is allowed in a format spec.
917 self.assertEqual(f'{3.14:!<10.10}', '3.14!!!!!!')
918
Eric V. Smith235a6f02015-09-19 14:51:32 -0400919 self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character',
920 ["f'{3!g}'",
921 "f'{3!A}'",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400922 "f'{3!3}'",
923 "f'{3!G}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400924 "f'{3!!}'",
925 "f'{3!:}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400926 "f'{3! s}'", # no space before conversion char
Eric V. Smith235a6f02015-09-19 14:51:32 -0400927 ])
928
929 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
930 ["f'{x!s{y}}'",
931 "f'{3!ss}'",
932 "f'{3!ss:}'",
933 "f'{3!ss:s}'",
934 ])
935
936 def test_assignment(self):
937 self.assertAllRaise(SyntaxError, 'invalid syntax',
938 ["f'' = 3",
939 "f'{0}' = x",
940 "f'{x}' = x",
941 ])
942
943 def test_del(self):
944 self.assertAllRaise(SyntaxError, 'invalid syntax',
945 ["del f''",
946 "del '' f''",
947 ])
948
949 def test_mismatched_braces(self):
950 self.assertAllRaise(SyntaxError, "f-string: single '}' is not allowed",
951 ["f'{{}'",
952 "f'{{}}}'",
953 "f'}'",
954 "f'x}'",
955 "f'x}x'",
Jason R. Coombsda25abf72016-11-06 11:14:48 -0500956 r"f'\u007b}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400957
958 # Can't have { or } in a format spec.
959 "f'{3:}>10}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400960 "f'{3:}}>10}'",
961 ])
962
963 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
964 ["f'{3:{{>10}'",
965 "f'{3'",
966 "f'{3!'",
967 "f'{3:'",
968 "f'{3!s'",
969 "f'{3!s:'",
970 "f'{3!s:3'",
971 "f'x{'",
972 "f'x{x'",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400973 "f'{x'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400974 "f'{3:s'",
975 "f'{{{'",
976 "f'{{}}{'",
977 "f'{'",
978 ])
979
Eric V. Smith235a6f02015-09-19 14:51:32 -0400980 # But these are just normal strings.
981 self.assertEqual(f'{"{"}', '{')
982 self.assertEqual(f'{"}"}', '}')
983 self.assertEqual(f'{3:{"}"}>10}', '}}}}}}}}}3')
984 self.assertEqual(f'{2:{"{"}>10}', '{{{{{{{{{2')
985
986 def test_if_conditional(self):
987 # There's special logic in compile.c to test if the
988 # conditional for an if (and while) are constants. Exercise
989 # that code.
990
991 def test_fstring(x, expected):
992 flag = 0
993 if f'{x}':
994 flag = 1
995 else:
996 flag = 2
997 self.assertEqual(flag, expected)
998
999 def test_concat_empty(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_non_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 test_fstring('', 2)
1016 test_fstring(' ', 1)
1017
1018 test_concat_empty('', 2)
1019 test_concat_empty(' ', 1)
1020
1021 test_concat_non_empty('', 1)
1022 test_concat_non_empty(' ', 1)
1023
1024 def test_empty_format_specifier(self):
1025 x = 'test'
1026 self.assertEqual(f'{x}', 'test')
1027 self.assertEqual(f'{x:}', 'test')
1028 self.assertEqual(f'{x!s:}', 'test')
1029 self.assertEqual(f'{x!r:}', "'test'")
1030
1031 def test_str_format_differences(self):
1032 d = {'a': 'string',
1033 0: 'integer',
1034 }
1035 a = 0
1036 self.assertEqual(f'{d[0]}', 'integer')
1037 self.assertEqual(f'{d["a"]}', 'string')
1038 self.assertEqual(f'{d[a]}', 'integer')
1039 self.assertEqual('{d[a]}'.format(d=d), 'string')
1040 self.assertEqual('{d[0]}'.format(d=d), 'integer')
1041
Eric V. Smith135d5f42016-02-05 18:23:08 -05001042 def test_errors(self):
1043 # see issue 26287
Serhiy Storchaka13c8f322016-10-31 08:13:00 +02001044 self.assertAllRaise(TypeError, 'unsupported',
Eric V. Smith135d5f42016-02-05 18:23:08 -05001045 [r"f'{(lambda: 0):x}'",
1046 r"f'{(0,):x}'",
1047 ])
1048 self.assertAllRaise(ValueError, 'Unknown format code',
1049 [r"f'{1000:j}'",
1050 r"f'{1000:j}'",
1051 ])
1052
Lysandros Nikolaouf7b1e462020-05-26 03:32:18 +03001053 def test_filename_in_syntaxerror(self):
1054 # see issue 38964
1055 with temp_cwd() as cwd:
1056 file_path = os.path.join(cwd, 't.py')
1057 with open(file_path, 'w') as f:
1058 f.write('f"{a b}"') # This generates a SyntaxError
Serhiy Storchaka700cfa82020-06-25 17:56:31 +03001059 _, _, stderr = assert_python_failure(file_path,
1060 PYTHONIOENCODING='ascii')
1061 self.assertIn(file_path.encode('ascii', 'backslashreplace'), stderr)
Lysandros Nikolaouf7b1e462020-05-26 03:32:18 +03001062
Eric V. Smith235a6f02015-09-19 14:51:32 -04001063 def test_loop(self):
1064 for i in range(1000):
1065 self.assertEqual(f'i:{i}', 'i:' + str(i))
1066
1067 def test_dict(self):
1068 d = {'"': 'dquote',
1069 "'": 'squote',
1070 'foo': 'bar',
1071 }
Eric V. Smith235a6f02015-09-19 14:51:32 -04001072 self.assertEqual(f'''{d["'"]}''', 'squote')
1073 self.assertEqual(f"""{d['"']}""", 'dquote')
1074
1075 self.assertEqual(f'{d["foo"]}', 'bar')
1076 self.assertEqual(f"{d['foo']}", 'bar')
Eric V. Smith235a6f02015-09-19 14:51:32 -04001077
ericvsmith11e97f22017-06-16 06:19:32 -04001078 def test_backslash_char(self):
1079 # Check eval of a backslash followed by a control char.
1080 # See bpo-30682: this used to raise an assert in pydebug mode.
1081 self.assertEqual(eval('f"\\\n"'), '')
1082 self.assertEqual(eval('f"\\\r"'), '')
1083
Eric V. Smith9a4135e2019-05-08 16:28:48 -04001084 def test_debug_conversion(self):
1085 x = 'A string'
1086 self.assertEqual(f'{x=}', 'x=' + repr(x))
1087 self.assertEqual(f'{x =}', 'x =' + repr(x))
1088 self.assertEqual(f'{x=!s}', 'x=' + str(x))
1089 self.assertEqual(f'{x=!r}', 'x=' + repr(x))
1090 self.assertEqual(f'{x=!a}', 'x=' + ascii(x))
1091
1092 x = 2.71828
1093 self.assertEqual(f'{x=:.2f}', 'x=' + format(x, '.2f'))
1094 self.assertEqual(f'{x=:}', 'x=' + format(x, ''))
1095 self.assertEqual(f'{x=!r:^20}', 'x=' + format(repr(x), '^20'))
1096 self.assertEqual(f'{x=!s:^20}', 'x=' + format(str(x), '^20'))
1097 self.assertEqual(f'{x=!a:^20}', 'x=' + format(ascii(x), '^20'))
1098
1099 x = 9
1100 self.assertEqual(f'{3*x+15=}', '3*x+15=42')
1101
1102 # There is code in ast.c that deals with non-ascii expression values. So,
1103 # use a unicode identifier to trigger that.
1104 tenπ = 31.4
1105 self.assertEqual(f'{tenπ=:.2f}', 'tenπ=31.40')
1106
1107 # Also test with Unicode in non-identifiers.
1108 self.assertEqual(f'{"Σ"=}', '"Σ"=\'Σ\'')
1109
1110 # Make sure nested fstrings still work.
1111 self.assertEqual(f'{f"{3.1415=:.1f}":*^20}', '*****3.1415=3.1*****')
1112
1113 # Make sure text before and after an expression with = works
1114 # correctly.
1115 pi = 'π'
1116 self.assertEqual(f'alpha α {pi=} ω omega', "alpha α pi='π' ω omega")
1117
1118 # Check multi-line expressions.
1119 self.assertEqual(f'''{
11203
1121=}''', '\n3\n=3')
1122
1123 # Since = is handled specially, make sure all existing uses of
1124 # it still work.
1125
1126 self.assertEqual(f'{0==1}', 'False')
1127 self.assertEqual(f'{0!=1}', 'True')
1128 self.assertEqual(f'{0<=1}', 'True')
1129 self.assertEqual(f'{0>=1}', 'False')
1130 self.assertEqual(f'{(x:="5")}', '5')
1131 self.assertEqual(x, '5')
1132 self.assertEqual(f'{(x:=5)}', '5')
1133 self.assertEqual(x, 5)
1134 self.assertEqual(f'{"="}', '=')
1135
1136 x = 20
1137 # This isn't an assignment expression, it's 'x', with a format
1138 # spec of '=10'. See test_walrus: you need to use parens.
1139 self.assertEqual(f'{x:=10}', ' 20')
1140
1141 # Test named function parameters, to make sure '=' parsing works
1142 # there.
1143 def f(a):
1144 nonlocal x
1145 oldx = x
1146 x = a
1147 return oldx
1148 x = 0
1149 self.assertEqual(f'{f(a="3=")}', '0')
1150 self.assertEqual(x, '3=')
1151 self.assertEqual(f'{f(a=4)}', '3=')
1152 self.assertEqual(x, 4)
1153
1154 # Make sure __format__ is being called.
1155 class C:
1156 def __format__(self, s):
1157 return f'FORMAT-{s}'
1158 def __repr__(self):
1159 return 'REPR'
1160
1161 self.assertEqual(f'{C()=}', 'C()=REPR')
1162 self.assertEqual(f'{C()=!r}', 'C()=REPR')
1163 self.assertEqual(f'{C()=:}', 'C()=FORMAT-')
1164 self.assertEqual(f'{C()=: }', 'C()=FORMAT- ')
1165 self.assertEqual(f'{C()=:x}', 'C()=FORMAT-x')
1166 self.assertEqual(f'{C()=!r:*^20}', 'C()=********REPR********')
1167
Pablo Galindo26f55c22019-05-12 01:43:04 +01001168 self.assertRaises(SyntaxError, eval, "f'{C=]'")
1169
Eric V. Smith6f6ff8a2019-05-27 15:31:52 -04001170 # Make sure leading and following text works.
1171 x = 'foo'
1172 self.assertEqual(f'X{x=}Y', 'Xx='+repr(x)+'Y')
1173
1174 # Make sure whitespace around the = works.
1175 self.assertEqual(f'X{x =}Y', 'Xx ='+repr(x)+'Y')
1176 self.assertEqual(f'X{x= }Y', 'Xx= '+repr(x)+'Y')
1177 self.assertEqual(f'X{x = }Y', 'Xx = '+repr(x)+'Y')
1178
1179 # These next lines contains tabs. Backslash escapes don't
1180 # work in f-strings.
Min ho Kim96e12d52019-07-22 06:12:33 +10001181 # patchcheck doesn't like these tabs. So the only way to test
Eric V. Smith6f6ff8a2019-05-27 15:31:52 -04001182 # this will be to dynamically created and exec the f-strings. But
1183 # that's such a hassle I'll save it for another day. For now, convert
1184 # the tabs to spaces just to shut up patchcheck.
1185 #self.assertEqual(f'X{x =}Y', 'Xx\t='+repr(x)+'Y')
1186 #self.assertEqual(f'X{x = }Y', 'Xx\t=\t'+repr(x)+'Y')
1187
Eric V. Smith9a4135e2019-05-08 16:28:48 -04001188 def test_walrus(self):
1189 x = 20
1190 # This isn't an assignment expression, it's 'x', with a format
1191 # spec of '=10'.
1192 self.assertEqual(f'{x:=10}', ' 20')
1193
1194 # This is an assignment expression, which requires parens.
1195 self.assertEqual(f'{(x:=10)}', '10')
1196 self.assertEqual(x, 10)
1197
Lysandros Nikolaou2e0a9202020-06-26 14:24:05 +03001198 def test_invalid_syntax_error_message(self):
1199 with self.assertRaisesRegex(SyntaxError, "f-string: invalid syntax"):
1200 compile("f'{a $ b}'", "?", "exec")
1201
han-solo0d6aa7f2020-09-01 10:34:29 -04001202 def test_with_two_commas_in_format_specifier(self):
1203 error_msg = re.escape("Cannot specify ',' with ','.")
1204 with self.assertRaisesRegex(ValueError, error_msg):
1205 f'{1:,,}'
1206
1207 def test_with_two_underscore_in_format_specifier(self):
1208 error_msg = re.escape("Cannot specify '_' with '_'.")
1209 with self.assertRaisesRegex(ValueError, error_msg):
1210 f'{1:__}'
1211
1212 def test_with_a_commas_and_an_underscore_in_format_specifier(self):
1213 error_msg = re.escape("Cannot specify both ',' and '_'.")
1214 with self.assertRaisesRegex(ValueError, error_msg):
1215 f'{1:,_}'
1216
1217 def test_with_an_underscore_and_a_comma_in_format_specifier(self):
1218 error_msg = re.escape("Cannot specify both ',' and '_'.")
1219 with self.assertRaisesRegex(ValueError, error_msg):
1220 f'{1:,_}'
Łukasz Langae7c566c2017-09-06 17:27:58 -07001221
Eric V. Smith235a6f02015-09-19 14:51:32 -04001222if __name__ == '__main__':
1223 unittest.main()