blob: 4c240f34a3543043a36bc816ba9696955eb8ec6b [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
11import types
12import decimal
Pablo Galindoc5fc1562020-04-22 23:29:27 +010013import sys
Eric V. Smith235a6f02015-09-19 14:51:32 -040014import unittest
Victor Stinner1def7752020-04-23 03:03:24 +020015from test import support
Eric V. Smith235a6f02015-09-19 14:51:32 -040016
17a_global = 'global variable'
18
19# You could argue that I'm too strict in looking for specific error
20# values with assertRaisesRegex, but without it it's way too easy to
21# make a syntax error in the test strings. Especially with all of the
22# triple quotes, raw strings, backslashes, etc. I think it's a
23# worthwhile tradeoff. When I switched to this method, I found many
24# examples where I wasn't testing what I thought I was.
25
26class TestCase(unittest.TestCase):
27 def assertAllRaise(self, exception_type, regex, error_strings):
28 for str in error_strings:
29 with self.subTest(str=str):
30 with self.assertRaisesRegex(exception_type, regex):
31 eval(str)
32
33 def test__format__lookup(self):
34 # Make sure __format__ is looked up on the type, not the instance.
35 class X:
36 def __format__(self, spec):
37 return 'class'
38
39 x = X()
40
41 # Add a bound __format__ method to the 'y' instance, but not
42 # the 'x' instance.
43 y = X()
44 y.__format__ = types.MethodType(lambda self, spec: 'instance', y)
45
46 self.assertEqual(f'{y}', format(y))
47 self.assertEqual(f'{y}', 'class')
48 self.assertEqual(format(x), format(y))
49
50 # __format__ is not called this way, but still make sure it
51 # returns what we expect (so we can make sure we're bypassing
52 # it).
53 self.assertEqual(x.__format__(''), 'class')
54 self.assertEqual(y.__format__(''), 'instance')
55
56 # This is how __format__ is actually called.
57 self.assertEqual(type(x).__format__(x, ''), 'class')
58 self.assertEqual(type(y).__format__(y, ''), 'class')
59
60 def test_ast(self):
61 # Inspired by http://bugs.python.org/issue24975
62 class X:
63 def __init__(self):
64 self.called = False
65 def __call__(self):
66 self.called = True
67 return 4
68 x = X()
69 expr = """
70a = 10
71f'{a * x()}'"""
72 t = ast.parse(expr)
73 c = compile(t, '', 'exec')
74
75 # Make sure x was not called.
76 self.assertFalse(x.called)
77
78 # Actually run the code.
79 exec(c)
80
81 # Make sure x was called.
82 self.assertTrue(x.called)
83
Łukasz Langae7c566c2017-09-06 17:27:58 -070084 def test_ast_line_numbers(self):
85 expr = """
86a = 10
87f'{a * x()}'"""
88 t = ast.parse(expr)
89 self.assertEqual(type(t), ast.Module)
90 self.assertEqual(len(t.body), 2)
91 # check `a = 10`
92 self.assertEqual(type(t.body[0]), ast.Assign)
93 self.assertEqual(t.body[0].lineno, 2)
94 # check `f'...'`
95 self.assertEqual(type(t.body[1]), ast.Expr)
96 self.assertEqual(type(t.body[1].value), ast.JoinedStr)
97 self.assertEqual(len(t.body[1].value.values), 1)
98 self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)
99 self.assertEqual(t.body[1].lineno, 3)
100 self.assertEqual(t.body[1].value.lineno, 3)
101 self.assertEqual(t.body[1].value.values[0].lineno, 3)
102 # check the binop location
103 binop = t.body[1].value.values[0].value
104 self.assertEqual(type(binop), ast.BinOp)
105 self.assertEqual(type(binop.left), ast.Name)
106 self.assertEqual(type(binop.op), ast.Mult)
107 self.assertEqual(type(binop.right), ast.Call)
108 self.assertEqual(binop.lineno, 3)
109 self.assertEqual(binop.left.lineno, 3)
110 self.assertEqual(binop.right.lineno, 3)
111 self.assertEqual(binop.col_offset, 3)
112 self.assertEqual(binop.left.col_offset, 3)
113 self.assertEqual(binop.right.col_offset, 7)
114
115 def test_ast_line_numbers_multiple_formattedvalues(self):
116 expr = """
117f'no formatted values'
118f'eggs {a * x()} spam {b + y()}'"""
119 t = ast.parse(expr)
120 self.assertEqual(type(t), ast.Module)
121 self.assertEqual(len(t.body), 2)
122 # check `f'no formatted value'`
123 self.assertEqual(type(t.body[0]), ast.Expr)
124 self.assertEqual(type(t.body[0].value), ast.JoinedStr)
125 self.assertEqual(t.body[0].lineno, 2)
126 # check `f'...'`
127 self.assertEqual(type(t.body[1]), ast.Expr)
128 self.assertEqual(type(t.body[1].value), ast.JoinedStr)
129 self.assertEqual(len(t.body[1].value.values), 4)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300130 self.assertEqual(type(t.body[1].value.values[0]), ast.Constant)
131 self.assertEqual(type(t.body[1].value.values[0].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700132 self.assertEqual(type(t.body[1].value.values[1]), ast.FormattedValue)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300133 self.assertEqual(type(t.body[1].value.values[2]), ast.Constant)
134 self.assertEqual(type(t.body[1].value.values[2].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700135 self.assertEqual(type(t.body[1].value.values[3]), ast.FormattedValue)
136 self.assertEqual(t.body[1].lineno, 3)
137 self.assertEqual(t.body[1].value.lineno, 3)
138 self.assertEqual(t.body[1].value.values[0].lineno, 3)
139 self.assertEqual(t.body[1].value.values[1].lineno, 3)
140 self.assertEqual(t.body[1].value.values[2].lineno, 3)
141 self.assertEqual(t.body[1].value.values[3].lineno, 3)
142 # check the first binop location
143 binop1 = t.body[1].value.values[1].value
144 self.assertEqual(type(binop1), ast.BinOp)
145 self.assertEqual(type(binop1.left), ast.Name)
146 self.assertEqual(type(binop1.op), ast.Mult)
147 self.assertEqual(type(binop1.right), ast.Call)
148 self.assertEqual(binop1.lineno, 3)
149 self.assertEqual(binop1.left.lineno, 3)
150 self.assertEqual(binop1.right.lineno, 3)
151 self.assertEqual(binop1.col_offset, 8)
152 self.assertEqual(binop1.left.col_offset, 8)
153 self.assertEqual(binop1.right.col_offset, 12)
154 # check the second binop location
155 binop2 = t.body[1].value.values[3].value
156 self.assertEqual(type(binop2), ast.BinOp)
157 self.assertEqual(type(binop2.left), ast.Name)
158 self.assertEqual(type(binop2.op), ast.Add)
159 self.assertEqual(type(binop2.right), ast.Call)
160 self.assertEqual(binop2.lineno, 3)
161 self.assertEqual(binop2.left.lineno, 3)
162 self.assertEqual(binop2.right.lineno, 3)
163 self.assertEqual(binop2.col_offset, 23)
164 self.assertEqual(binop2.left.col_offset, 23)
165 self.assertEqual(binop2.right.col_offset, 27)
166
167 def test_ast_line_numbers_nested(self):
168 expr = """
169a = 10
170f'{a * f"-{x()}-"}'"""
171 t = ast.parse(expr)
172 self.assertEqual(type(t), ast.Module)
173 self.assertEqual(len(t.body), 2)
174 # check `a = 10`
175 self.assertEqual(type(t.body[0]), ast.Assign)
176 self.assertEqual(t.body[0].lineno, 2)
177 # check `f'...'`
178 self.assertEqual(type(t.body[1]), ast.Expr)
179 self.assertEqual(type(t.body[1].value), ast.JoinedStr)
180 self.assertEqual(len(t.body[1].value.values), 1)
181 self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)
182 self.assertEqual(t.body[1].lineno, 3)
183 self.assertEqual(t.body[1].value.lineno, 3)
184 self.assertEqual(t.body[1].value.values[0].lineno, 3)
185 # check the binop location
186 binop = t.body[1].value.values[0].value
187 self.assertEqual(type(binop), ast.BinOp)
188 self.assertEqual(type(binop.left), ast.Name)
189 self.assertEqual(type(binop.op), ast.Mult)
190 self.assertEqual(type(binop.right), ast.JoinedStr)
191 self.assertEqual(binop.lineno, 3)
192 self.assertEqual(binop.left.lineno, 3)
193 self.assertEqual(binop.right.lineno, 3)
194 self.assertEqual(binop.col_offset, 3)
195 self.assertEqual(binop.left.col_offset, 3)
196 self.assertEqual(binop.right.col_offset, 7)
197 # check the nested call location
198 self.assertEqual(len(binop.right.values), 3)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300199 self.assertEqual(type(binop.right.values[0]), ast.Constant)
200 self.assertEqual(type(binop.right.values[0].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700201 self.assertEqual(type(binop.right.values[1]), ast.FormattedValue)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300202 self.assertEqual(type(binop.right.values[2]), ast.Constant)
203 self.assertEqual(type(binop.right.values[2].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700204 self.assertEqual(binop.right.values[0].lineno, 3)
205 self.assertEqual(binop.right.values[1].lineno, 3)
206 self.assertEqual(binop.right.values[2].lineno, 3)
207 call = binop.right.values[1].value
208 self.assertEqual(type(call), ast.Call)
209 self.assertEqual(call.lineno, 3)
Lysandros Nikolaou37af21b2020-04-29 03:43:50 +0300210 self.assertEqual(call.col_offset, 11)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700211
212 def test_ast_line_numbers_duplicate_expression(self):
213 """Duplicate expression
214
215 NOTE: this is currently broken, always sets location of the first
216 expression.
217 """
218 expr = """
219a = 10
220f'{a * x()} {a * x()} {a * x()}'
221"""
222 t = ast.parse(expr)
223 self.assertEqual(type(t), ast.Module)
224 self.assertEqual(len(t.body), 2)
225 # check `a = 10`
226 self.assertEqual(type(t.body[0]), ast.Assign)
227 self.assertEqual(t.body[0].lineno, 2)
228 # check `f'...'`
229 self.assertEqual(type(t.body[1]), ast.Expr)
230 self.assertEqual(type(t.body[1].value), ast.JoinedStr)
231 self.assertEqual(len(t.body[1].value.values), 5)
232 self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300233 self.assertEqual(type(t.body[1].value.values[1]), ast.Constant)
234 self.assertEqual(type(t.body[1].value.values[1].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700235 self.assertEqual(type(t.body[1].value.values[2]), ast.FormattedValue)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300236 self.assertEqual(type(t.body[1].value.values[3]), ast.Constant)
237 self.assertEqual(type(t.body[1].value.values[3].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700238 self.assertEqual(type(t.body[1].value.values[4]), ast.FormattedValue)
239 self.assertEqual(t.body[1].lineno, 3)
240 self.assertEqual(t.body[1].value.lineno, 3)
241 self.assertEqual(t.body[1].value.values[0].lineno, 3)
242 self.assertEqual(t.body[1].value.values[1].lineno, 3)
243 self.assertEqual(t.body[1].value.values[2].lineno, 3)
244 self.assertEqual(t.body[1].value.values[3].lineno, 3)
245 self.assertEqual(t.body[1].value.values[4].lineno, 3)
246 # check the first binop location
247 binop = t.body[1].value.values[0].value
248 self.assertEqual(type(binop), ast.BinOp)
249 self.assertEqual(type(binop.left), ast.Name)
250 self.assertEqual(type(binop.op), ast.Mult)
251 self.assertEqual(type(binop.right), ast.Call)
252 self.assertEqual(binop.lineno, 3)
253 self.assertEqual(binop.left.lineno, 3)
254 self.assertEqual(binop.right.lineno, 3)
255 self.assertEqual(binop.col_offset, 3)
256 self.assertEqual(binop.left.col_offset, 3)
257 self.assertEqual(binop.right.col_offset, 7)
258 # check the second binop location
259 binop = t.body[1].value.values[2].value
260 self.assertEqual(type(binop), ast.BinOp)
261 self.assertEqual(type(binop.left), ast.Name)
262 self.assertEqual(type(binop.op), ast.Mult)
263 self.assertEqual(type(binop.right), ast.Call)
264 self.assertEqual(binop.lineno, 3)
265 self.assertEqual(binop.left.lineno, 3)
266 self.assertEqual(binop.right.lineno, 3)
267 self.assertEqual(binop.col_offset, 3) # FIXME: this is wrong
268 self.assertEqual(binop.left.col_offset, 3) # FIXME: this is wrong
269 self.assertEqual(binop.right.col_offset, 7) # FIXME: this is wrong
270 # check the third binop location
271 binop = t.body[1].value.values[4].value
272 self.assertEqual(type(binop), ast.BinOp)
273 self.assertEqual(type(binop.left), ast.Name)
274 self.assertEqual(type(binop.op), ast.Mult)
275 self.assertEqual(type(binop.right), ast.Call)
276 self.assertEqual(binop.lineno, 3)
277 self.assertEqual(binop.left.lineno, 3)
278 self.assertEqual(binop.right.lineno, 3)
279 self.assertEqual(binop.col_offset, 3) # FIXME: this is wrong
280 self.assertEqual(binop.left.col_offset, 3) # FIXME: this is wrong
281 self.assertEqual(binop.right.col_offset, 7) # FIXME: this is wrong
282
283 def test_ast_line_numbers_multiline_fstring(self):
Anthony Sottile995d9b92019-01-12 20:05:13 -0800284 # See bpo-30465 for details.
Łukasz Langae7c566c2017-09-06 17:27:58 -0700285 expr = """
286a = 10
287f'''
288 {a
289 *
290 x()}
291non-important content
292'''
293"""
294 t = ast.parse(expr)
295 self.assertEqual(type(t), ast.Module)
296 self.assertEqual(len(t.body), 2)
297 # check `a = 10`
298 self.assertEqual(type(t.body[0]), ast.Assign)
299 self.assertEqual(t.body[0].lineno, 2)
300 # check `f'...'`
301 self.assertEqual(type(t.body[1]), ast.Expr)
302 self.assertEqual(type(t.body[1].value), ast.JoinedStr)
303 self.assertEqual(len(t.body[1].value.values), 3)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300304 self.assertEqual(type(t.body[1].value.values[0]), ast.Constant)
305 self.assertEqual(type(t.body[1].value.values[0].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700306 self.assertEqual(type(t.body[1].value.values[1]), ast.FormattedValue)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300307 self.assertEqual(type(t.body[1].value.values[2]), ast.Constant)
308 self.assertEqual(type(t.body[1].value.values[2].value), str)
Anthony Sottile995d9b92019-01-12 20:05:13 -0800309 self.assertEqual(t.body[1].lineno, 3)
310 self.assertEqual(t.body[1].value.lineno, 3)
311 self.assertEqual(t.body[1].value.values[0].lineno, 3)
312 self.assertEqual(t.body[1].value.values[1].lineno, 3)
313 self.assertEqual(t.body[1].value.values[2].lineno, 3)
314 self.assertEqual(t.body[1].col_offset, 0)
315 self.assertEqual(t.body[1].value.col_offset, 0)
316 self.assertEqual(t.body[1].value.values[0].col_offset, 0)
317 self.assertEqual(t.body[1].value.values[1].col_offset, 0)
318 self.assertEqual(t.body[1].value.values[2].col_offset, 0)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700319 # NOTE: the following lineno information and col_offset is correct for
320 # expressions within FormattedValues.
321 binop = t.body[1].value.values[1].value
322 self.assertEqual(type(binop), ast.BinOp)
323 self.assertEqual(type(binop.left), ast.Name)
324 self.assertEqual(type(binop.op), ast.Mult)
325 self.assertEqual(type(binop.right), ast.Call)
326 self.assertEqual(binop.lineno, 4)
327 self.assertEqual(binop.left.lineno, 4)
328 self.assertEqual(binop.right.lineno, 6)
Anthony Sottile995d9b92019-01-12 20:05:13 -0800329 self.assertEqual(binop.col_offset, 4)
330 self.assertEqual(binop.left.col_offset, 4)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700331 self.assertEqual(binop.right.col_offset, 7)
332
Serhiy Storchaka4cc30ae2016-12-11 19:37:19 +0200333 def test_docstring(self):
334 def f():
335 f'''Not a docstring'''
336 self.assertIsNone(f.__doc__)
337 def g():
338 '''Not a docstring''' \
339 f''
340 self.assertIsNone(g.__doc__)
341
Eric V. Smith235a6f02015-09-19 14:51:32 -0400342 def test_literal_eval(self):
Eric V. Smith235a6f02015-09-19 14:51:32 -0400343 with self.assertRaisesRegex(ValueError, 'malformed node or string'):
Serhiy Storchaka4cc30ae2016-12-11 19:37:19 +0200344 ast.literal_eval("f'x'")
Eric V. Smith235a6f02015-09-19 14:51:32 -0400345
346 def test_ast_compile_time_concat(self):
347 x = ['']
348
349 expr = """x[0] = 'foo' f'{3}'"""
350 t = ast.parse(expr)
351 c = compile(t, '', 'exec')
352 exec(c)
353 self.assertEqual(x[0], 'foo3')
354
Eric V. Smith9b88fdf2016-11-07 17:54:01 -0500355 def test_compile_time_concat_errors(self):
356 self.assertAllRaise(SyntaxError,
357 'cannot mix bytes and nonbytes literals',
358 [r"""f'' b''""",
359 r"""b'' f''""",
360 ])
361
Eric V. Smith235a6f02015-09-19 14:51:32 -0400362 def test_literal(self):
363 self.assertEqual(f'', '')
364 self.assertEqual(f'a', 'a')
365 self.assertEqual(f' ', ' ')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400366
367 def test_unterminated_string(self):
368 self.assertAllRaise(SyntaxError, 'f-string: unterminated string',
369 [r"""f'{"x'""",
370 r"""f'{"x}'""",
371 r"""f'{("x'""",
372 r"""f'{("x}'""",
373 ])
374
375 def test_mismatched_parens(self):
Serhiy Storchaka58159ef2019-01-12 09:46:50 +0200376 self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\}' "
377 r"does not match opening parenthesis '\('",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400378 ["f'{((}'",
379 ])
Serhiy Storchaka58159ef2019-01-12 09:46:50 +0200380 self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\)' "
381 r"does not match opening parenthesis '\['",
382 ["f'{a[4)}'",
383 ])
384 self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\]' "
385 r"does not match opening parenthesis '\('",
386 ["f'{a(4]}'",
387 ])
388 self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\}' "
389 r"does not match opening parenthesis '\['",
390 ["f'{a[4}'",
391 ])
392 self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\}' "
393 r"does not match opening parenthesis '\('",
394 ["f'{a(4}'",
395 ])
396 self.assertRaises(SyntaxError, eval, "f'{" + "("*500 + "}'")
Eric V. Smith235a6f02015-09-19 14:51:32 -0400397
398 def test_double_braces(self):
399 self.assertEqual(f'{{', '{')
400 self.assertEqual(f'a{{', 'a{')
401 self.assertEqual(f'{{b', '{b')
402 self.assertEqual(f'a{{b', 'a{b')
403 self.assertEqual(f'}}', '}')
404 self.assertEqual(f'a}}', 'a}')
405 self.assertEqual(f'}}b', '}b')
406 self.assertEqual(f'a}}b', 'a}b')
Eric V. Smith451d0e32016-09-09 21:56:20 -0400407 self.assertEqual(f'{{}}', '{}')
408 self.assertEqual(f'a{{}}', 'a{}')
409 self.assertEqual(f'{{b}}', '{b}')
410 self.assertEqual(f'{{}}c', '{}c')
411 self.assertEqual(f'a{{b}}', 'a{b}')
412 self.assertEqual(f'a{{}}c', 'a{}c')
413 self.assertEqual(f'{{b}}c', '{b}c')
414 self.assertEqual(f'a{{b}}c', 'a{b}c')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400415
416 self.assertEqual(f'{{{10}', '{10')
417 self.assertEqual(f'}}{10}', '}10')
418 self.assertEqual(f'}}{{{10}', '}{10')
419 self.assertEqual(f'}}a{{{10}', '}a{10')
420
421 self.assertEqual(f'{10}{{', '10{')
422 self.assertEqual(f'{10}}}', '10}')
423 self.assertEqual(f'{10}}}{{', '10}{')
424 self.assertEqual(f'{10}}}a{{' '}', '10}a{}')
425
426 # Inside of strings, don't interpret doubled brackets.
427 self.assertEqual(f'{"{{}}"}', '{{}}')
428
429 self.assertAllRaise(TypeError, 'unhashable type',
430 ["f'{ {{}} }'", # dict in a set
431 ])
432
433 def test_compile_time_concat(self):
434 x = 'def'
435 self.assertEqual('abc' f'## {x}ghi', 'abc## defghi')
436 self.assertEqual('abc' f'{x}' 'ghi', 'abcdefghi')
437 self.assertEqual('abc' f'{x}' 'gh' f'i{x:4}', 'abcdefghidef ')
438 self.assertEqual('{x}' f'{x}', '{x}def')
439 self.assertEqual('{x' f'{x}', '{xdef')
440 self.assertEqual('{x}' f'{x}', '{x}def')
441 self.assertEqual('{{x}}' f'{x}', '{{x}}def')
442 self.assertEqual('{{x' f'{x}', '{{xdef')
443 self.assertEqual('x}}' f'{x}', 'x}}def')
444 self.assertEqual(f'{x}' 'x}}', 'defx}}')
445 self.assertEqual(f'{x}' '', 'def')
446 self.assertEqual('' f'{x}' '', 'def')
447 self.assertEqual('' f'{x}', 'def')
448 self.assertEqual(f'{x}' '2', 'def2')
449 self.assertEqual('1' f'{x}' '2', '1def2')
450 self.assertEqual('1' f'{x}', '1def')
451 self.assertEqual(f'{x}' f'-{x}', 'def-def')
452 self.assertEqual('' f'', '')
453 self.assertEqual('' f'' '', '')
454 self.assertEqual('' f'' '' f'', '')
455 self.assertEqual(f'', '')
456 self.assertEqual(f'' '', '')
457 self.assertEqual(f'' '' f'', '')
458 self.assertEqual(f'' '' f'' '', '')
459
460 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
461 ["f'{3' f'}'", # can't concat to get a valid f-string
462 ])
463
464 def test_comments(self):
465 # These aren't comments, since they're in strings.
466 d = {'#': 'hash'}
467 self.assertEqual(f'{"#"}', '#')
468 self.assertEqual(f'{d["#"]}', 'hash')
469
Eric V. Smith09835dc2016-09-11 18:58:20 -0400470 self.assertAllRaise(SyntaxError, "f-string expression part cannot include '#'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400471 ["f'{1#}'", # error because the expression becomes "(1#)"
472 "f'{3(#)}'",
Eric V. Smith09835dc2016-09-11 18:58:20 -0400473 "f'{#}'",
Serhiy Storchaka58159ef2019-01-12 09:46:50 +0200474 ])
475 self.assertAllRaise(SyntaxError, r"f-string: unmatched '\)'",
476 ["f'{)#}'", # When wrapped in parens, this becomes
Eric V. Smith35a24c52016-09-11 19:01:22 -0400477 # '()#)'. Make sure that doesn't compile.
Eric V. Smith235a6f02015-09-19 14:51:32 -0400478 ])
479
480 def test_many_expressions(self):
481 # Create a string with many expressions in it. Note that
482 # because we have a space in here as a literal, we're actually
483 # going to use twice as many ast nodes: one for each literal
484 # plus one for each expression.
485 def build_fstr(n, extra=''):
486 return "f'" + ('{x} ' * n) + extra + "'"
487
488 x = 'X'
489 width = 1
490
491 # Test around 256.
492 for i in range(250, 260):
493 self.assertEqual(eval(build_fstr(i)), (x+' ')*i)
494
495 # Test concatenating 2 largs fstrings.
496 self.assertEqual(eval(build_fstr(255)*256), (x+' ')*(255*256))
497
498 s = build_fstr(253, '{x:{width}} ')
499 self.assertEqual(eval(s), (x+' ')*254)
500
501 # Test lots of expressions and constants, concatenated.
502 s = "f'{1}' 'x' 'y'" * 1024
503 self.assertEqual(eval(s), '1xy' * 1024)
504
505 def test_format_specifier_expressions(self):
506 width = 10
507 precision = 4
508 value = decimal.Decimal('12.34567')
509 self.assertEqual(f'result: {value:{width}.{precision}}', 'result: 12.35')
510 self.assertEqual(f'result: {value:{width!r}.{precision}}', 'result: 12.35')
511 self.assertEqual(f'result: {value:{width:0}.{precision:1}}', 'result: 12.35')
512 self.assertEqual(f'result: {value:{1}{0:0}.{precision:1}}', 'result: 12.35')
513 self.assertEqual(f'result: {value:{ 1}{ 0:0}.{ precision:1}}', 'result: 12.35')
514 self.assertEqual(f'{10:#{1}0x}', ' 0xa')
515 self.assertEqual(f'{10:{"#"}1{0}{"x"}}', ' 0xa')
516 self.assertEqual(f'{-10:-{"#"}1{0}x}', ' -0xa')
517 self.assertEqual(f'{-10:{"-"}#{1}0{"x"}}', ' -0xa')
518 self.assertEqual(f'{10:#{3 != {4:5} and width}x}', ' 0xa')
519
520 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
521 ["""f'{"s"!r{":10"}}'""",
522
523 # This looks like a nested format spec.
524 ])
525
526 self.assertAllRaise(SyntaxError, "invalid syntax",
Martin Panter263893c2016-07-28 01:25:31 +0000527 [# Invalid syntax inside a nested spec.
Eric V. Smith235a6f02015-09-19 14:51:32 -0400528 "f'{4:{/5}}'",
529 ])
530
531 self.assertAllRaise(SyntaxError, "f-string: expressions nested too deeply",
532 [# Can't nest format specifiers.
533 "f'result: {value:{width:{0}}.{precision:1}}'",
534 ])
535
536 self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character',
537 [# No expansion inside conversion or for
538 # the : or ! itself.
539 """f'{"s"!{"r"}}'""",
540 ])
541
542 def test_side_effect_order(self):
543 class X:
544 def __init__(self):
545 self.i = 0
546 def __format__(self, spec):
547 self.i += 1
548 return str(self.i)
549
550 x = X()
551 self.assertEqual(f'{x} {x}', '1 2')
552
553 def test_missing_expression(self):
554 self.assertAllRaise(SyntaxError, 'f-string: empty expression not allowed',
555 ["f'{}'",
556 "f'{ }'"
557 "f' {} '",
558 "f'{!r}'",
559 "f'{ !r}'",
560 "f'{10:{ }}'",
561 "f' { } '",
Eric V. Smith1d44c412015-09-23 07:49:00 -0400562
Serhiy Storchaka2e9cd582017-06-08 23:43:54 +0300563 # The Python parser ignores also the following
564 # whitespace characters in additional to a space.
565 "f'''{\t\f\r\n}'''",
566
Eric V. Smith1d44c412015-09-23 07:49:00 -0400567 # Catch the empty expression before the
568 # invalid conversion.
569 "f'{!x}'",
570 "f'{ !xr}'",
571 "f'{!x:}'",
572 "f'{!x:a}'",
573 "f'{ !xr:}'",
574 "f'{ !xr:a}'",
Eric V. Smith548c4d32015-09-23 08:00:01 -0400575
576 "f'{!}'",
577 "f'{:}'",
Eric V. Smithb2080f62015-09-23 10:24:43 -0400578
579 # We find the empty expression before the
580 # missing closing brace.
581 "f'{!'",
582 "f'{!s:'",
583 "f'{:'",
584 "f'{:x'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400585 ])
586
Serhiy Storchaka2e9cd582017-06-08 23:43:54 +0300587 # Different error message is raised for other whitespace characters.
588 self.assertAllRaise(SyntaxError, 'invalid character in identifier',
589 ["f'''{\xa0}'''",
590 "\xa0",
591 ])
592
Eric V. Smith235a6f02015-09-19 14:51:32 -0400593 def test_parens_in_expressions(self):
594 self.assertEqual(f'{3,}', '(3,)')
595
596 # Add these because when an expression is evaluated, parens
597 # are added around it. But we shouldn't go from an invalid
598 # expression to a valid one. The added parens are just
599 # supposed to allow whitespace (including newlines).
600 self.assertAllRaise(SyntaxError, 'invalid syntax',
601 ["f'{,}'",
602 "f'{,}'", # this is (,), which is an error
603 ])
604
Serhiy Storchaka58159ef2019-01-12 09:46:50 +0200605 self.assertAllRaise(SyntaxError, r"f-string: unmatched '\)'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400606 ["f'{3)+(4}'",
607 ])
608
609 self.assertAllRaise(SyntaxError, 'EOL while scanning string literal',
610 ["f'{\n}'",
611 ])
612
Eric V. Smith451d0e32016-09-09 21:56:20 -0400613 def test_backslashes_in_string_part(self):
614 self.assertEqual(f'\t', '\t')
615 self.assertEqual(r'\t', '\\t')
616 self.assertEqual(rf'\t', '\\t')
617 self.assertEqual(f'{2}\t', '2\t')
618 self.assertEqual(f'{2}\t{3}', '2\t3')
619 self.assertEqual(f'\t{3}', '\t3')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400620
Eric V. Smith451d0e32016-09-09 21:56:20 -0400621 self.assertEqual(f'\u0394', '\u0394')
622 self.assertEqual(r'\u0394', '\\u0394')
623 self.assertEqual(rf'\u0394', '\\u0394')
624 self.assertEqual(f'{2}\u0394', '2\u0394')
625 self.assertEqual(f'{2}\u0394{3}', '2\u03943')
626 self.assertEqual(f'\u0394{3}', '\u03943')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400627
Eric V. Smith451d0e32016-09-09 21:56:20 -0400628 self.assertEqual(f'\U00000394', '\u0394')
629 self.assertEqual(r'\U00000394', '\\U00000394')
630 self.assertEqual(rf'\U00000394', '\\U00000394')
631 self.assertEqual(f'{2}\U00000394', '2\u0394')
632 self.assertEqual(f'{2}\U00000394{3}', '2\u03943')
633 self.assertEqual(f'\U00000394{3}', '\u03943')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400634
Eric V. Smith451d0e32016-09-09 21:56:20 -0400635 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}', '\u0394')
636 self.assertEqual(f'{2}\N{GREEK CAPITAL LETTER DELTA}', '2\u0394')
637 self.assertEqual(f'{2}\N{GREEK CAPITAL LETTER DELTA}{3}', '2\u03943')
638 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}{3}', '\u03943')
639 self.assertEqual(f'2\N{GREEK CAPITAL LETTER DELTA}', '2\u0394')
640 self.assertEqual(f'2\N{GREEK CAPITAL LETTER DELTA}3', '2\u03943')
641 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}3', '\u03943')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400642
Eric V. Smith451d0e32016-09-09 21:56:20 -0400643 self.assertEqual(f'\x20', ' ')
644 self.assertEqual(r'\x20', '\\x20')
645 self.assertEqual(rf'\x20', '\\x20')
646 self.assertEqual(f'{2}\x20', '2 ')
647 self.assertEqual(f'{2}\x20{3}', '2 3')
648 self.assertEqual(f'\x20{3}', ' 3')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400649
Eric V. Smith451d0e32016-09-09 21:56:20 -0400650 self.assertEqual(f'2\x20', '2 ')
651 self.assertEqual(f'2\x203', '2 3')
652 self.assertEqual(f'\x203', ' 3')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400653
Gregory P. Smithb4be87a2019-08-10 00:19:07 -0700654 with self.assertWarns(DeprecationWarning): # invalid escape sequence
Serhiy Storchaka0cd7a3f2017-05-25 13:33:55 +0300655 value = eval(r"f'\{6*7}'")
656 self.assertEqual(value, '\\42')
657 self.assertEqual(f'\\{6*7}', '\\42')
658 self.assertEqual(fr'\{6*7}', '\\42')
659
660 AMPERSAND = 'spam'
661 # Get the right unicode character (&), or pick up local variable
662 # depending on the number of backslashes.
663 self.assertEqual(f'\N{AMPERSAND}', '&')
664 self.assertEqual(f'\\N{AMPERSAND}', '\\Nspam')
665 self.assertEqual(fr'\N{AMPERSAND}', '\\Nspam')
666 self.assertEqual(f'\\\N{AMPERSAND}', '\\&')
667
Eric V. Smith451d0e32016-09-09 21:56:20 -0400668 def test_misformed_unicode_character_name(self):
669 # These test are needed because unicode names are parsed
670 # differently inside f-strings.
671 self.assertAllRaise(SyntaxError, r"\(unicode error\) 'unicodeescape' codec can't decode bytes in position .*: malformed \\N character escape",
672 [r"f'\N'",
673 r"f'\N{'",
674 r"f'\N{GREEK CAPITAL LETTER DELTA'",
675
676 # Here are the non-f-string versions,
677 # which should give the same errors.
678 r"'\N'",
679 r"'\N{'",
680 r"'\N{GREEK CAPITAL LETTER DELTA'",
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400681 ])
682
Eric V. Smith451d0e32016-09-09 21:56:20 -0400683 def test_no_backslashes_in_expression_part(self):
684 self.assertAllRaise(SyntaxError, 'f-string expression part cannot include a backslash',
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400685 [r"f'{\'a\'}'",
686 r"f'{\t3}'",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400687 r"f'{\}'",
688 r"rf'{\'a\'}'",
689 r"rf'{\t3}'",
690 r"rf'{\}'",
691 r"""rf'{"\N{LEFT CURLY BRACKET}"}'""",
Jason R. Coombs45cab8c2016-11-06 11:01:08 -0500692 r"f'{\n}'",
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400693 ])
694
Eric V. Smith451d0e32016-09-09 21:56:20 -0400695 def test_no_escapes_for_braces(self):
Jason R. Coombs1c92a762016-11-06 11:25:54 -0500696 """
697 Only literal curly braces begin an expression.
698 """
699 # \x7b is '{'.
700 self.assertEqual(f'\x7b1+1}}', '{1+1}')
701 self.assertEqual(f'\x7b1+1', '{1+1')
702 self.assertEqual(f'\u007b1+1', '{1+1')
703 self.assertEqual(f'\N{LEFT CURLY BRACKET}1+1\N{RIGHT CURLY BRACKET}', '{1+1}')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400704
Eric V. Smith235a6f02015-09-19 14:51:32 -0400705 def test_newlines_in_expressions(self):
706 self.assertEqual(f'{0}', '0')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400707 self.assertEqual(rf'''{3+
7084}''', '7')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400709
710 def test_lambda(self):
711 x = 5
712 self.assertEqual(f'{(lambda y:x*y)("8")!r}', "'88888'")
713 self.assertEqual(f'{(lambda y:x*y)("8")!r:10}', "'88888' ")
714 self.assertEqual(f'{(lambda y:x*y)("8"):10}', "88888 ")
715
716 # lambda doesn't work without parens, because the colon
717 # makes the parser think it's a format_spec
Lysandros Nikolaou9a4b38f2020-04-15 21:22:10 +0300718 self.assertAllRaise(SyntaxError, 'invalid syntax',
Eric V. Smith235a6f02015-09-19 14:51:32 -0400719 ["f'{lambda x:x}'",
720 ])
721
722 def test_yield(self):
723 # Not terribly useful, but make sure the yield turns
724 # a function into a generator
725 def fn(y):
726 f'y:{yield y*2}'
727
728 g = fn(4)
729 self.assertEqual(next(g), 8)
730
731 def test_yield_send(self):
732 def fn(x):
733 yield f'x:{yield (lambda i: x * i)}'
734
735 g = fn(10)
736 the_lambda = next(g)
737 self.assertEqual(the_lambda(4), 40)
738 self.assertEqual(g.send('string'), 'x:string')
739
740 def test_expressions_with_triple_quoted_strings(self):
741 self.assertEqual(f"{'''x'''}", 'x')
742 self.assertEqual(f"{'''eric's'''}", "eric's")
Eric V. Smith235a6f02015-09-19 14:51:32 -0400743
744 # Test concatenation within an expression
745 self.assertEqual(f'{"x" """eric"s""" "y"}', 'xeric"sy')
746 self.assertEqual(f'{"x" """eric"s"""}', 'xeric"s')
747 self.assertEqual(f'{"""eric"s""" "y"}', 'eric"sy')
748 self.assertEqual(f'{"""x""" """eric"s""" "y"}', 'xeric"sy')
749 self.assertEqual(f'{"""x""" """eric"s""" """y"""}', 'xeric"sy')
750 self.assertEqual(f'{r"""x""" """eric"s""" """y"""}', 'xeric"sy')
751
752 def test_multiple_vars(self):
753 x = 98
754 y = 'abc'
755 self.assertEqual(f'{x}{y}', '98abc')
756
757 self.assertEqual(f'X{x}{y}', 'X98abc')
758 self.assertEqual(f'{x}X{y}', '98Xabc')
759 self.assertEqual(f'{x}{y}X', '98abcX')
760
761 self.assertEqual(f'X{x}Y{y}', 'X98Yabc')
762 self.assertEqual(f'X{x}{y}Y', 'X98abcY')
763 self.assertEqual(f'{x}X{y}Y', '98XabcY')
764
765 self.assertEqual(f'X{x}Y{y}Z', 'X98YabcZ')
766
767 def test_closure(self):
768 def outer(x):
769 def inner():
770 return f'x:{x}'
771 return inner
772
773 self.assertEqual(outer('987')(), 'x:987')
774 self.assertEqual(outer(7)(), 'x:7')
775
776 def test_arguments(self):
777 y = 2
778 def f(x, width):
779 return f'x={x*y:{width}}'
780
781 self.assertEqual(f('foo', 10), 'x=foofoo ')
782 x = 'bar'
783 self.assertEqual(f(10, 10), 'x= 20')
784
785 def test_locals(self):
786 value = 123
787 self.assertEqual(f'v:{value}', 'v:123')
788
789 def test_missing_variable(self):
790 with self.assertRaises(NameError):
791 f'v:{value}'
792
793 def test_missing_format_spec(self):
794 class O:
795 def __format__(self, spec):
796 if not spec:
797 return '*'
798 return spec
799
800 self.assertEqual(f'{O():x}', 'x')
801 self.assertEqual(f'{O()}', '*')
802 self.assertEqual(f'{O():}', '*')
803
804 self.assertEqual(f'{3:}', '3')
805 self.assertEqual(f'{3!s:}', '3')
806
807 def test_global(self):
808 self.assertEqual(f'g:{a_global}', 'g:global variable')
809 self.assertEqual(f'g:{a_global!r}', "g:'global variable'")
810
811 a_local = 'local variable'
812 self.assertEqual(f'g:{a_global} l:{a_local}',
813 'g:global variable l:local variable')
814 self.assertEqual(f'g:{a_global!r}',
815 "g:'global variable'")
816 self.assertEqual(f'g:{a_global} l:{a_local!r}',
817 "g:global variable l:'local variable'")
818
819 self.assertIn("module 'unittest' from", f'{unittest}')
820
821 def test_shadowed_global(self):
822 a_global = 'really a local'
823 self.assertEqual(f'g:{a_global}', 'g:really a local')
824 self.assertEqual(f'g:{a_global!r}', "g:'really a local'")
825
826 a_local = 'local variable'
827 self.assertEqual(f'g:{a_global} l:{a_local}',
828 'g:really a local l:local variable')
829 self.assertEqual(f'g:{a_global!r}',
830 "g:'really a local'")
831 self.assertEqual(f'g:{a_global} l:{a_local!r}',
832 "g:really a local l:'local variable'")
833
834 def test_call(self):
835 def foo(x):
836 return 'x=' + str(x)
837
838 self.assertEqual(f'{foo(10)}', 'x=10')
839
840 def test_nested_fstrings(self):
841 y = 5
842 self.assertEqual(f'{f"{0}"*3}', '000')
843 self.assertEqual(f'{f"{y}"*3}', '555')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400844
845 def test_invalid_string_prefixes(self):
Pablo Galindo70c188e2020-04-13 02:47:35 +0100846 single_quote_cases = ["fu''",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400847 "uf''",
848 "Fu''",
849 "fU''",
850 "Uf''",
851 "uF''",
852 "ufr''",
853 "urf''",
854 "fur''",
855 "fru''",
856 "rfu''",
857 "ruf''",
858 "FUR''",
859 "Fur''",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400860 "fb''",
861 "fB''",
862 "Fb''",
863 "FB''",
864 "bf''",
865 "bF''",
866 "Bf''",
Pablo Galindo70c188e2020-04-13 02:47:35 +0100867 "BF''",]
868 double_quote_cases = [case.replace("'", '"') for case in single_quote_cases]
869 self.assertAllRaise(SyntaxError, 'invalid string prefix',
870 single_quote_cases + double_quote_cases)
Eric V. Smith235a6f02015-09-19 14:51:32 -0400871
872 def test_leading_trailing_spaces(self):
873 self.assertEqual(f'{ 3}', '3')
874 self.assertEqual(f'{ 3}', '3')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400875 self.assertEqual(f'{3 }', '3')
876 self.assertEqual(f'{3 }', '3')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400877
878 self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]}}',
879 'expr={1: 2}')
880 self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]} }',
881 'expr={1: 2}')
882
Eric V. Smith235a6f02015-09-19 14:51:32 -0400883 def test_not_equal(self):
884 # There's a special test for this because there's a special
885 # case in the f-string parser to look for != as not ending an
886 # expression. Normally it would, while looking for !s or !r.
887
888 self.assertEqual(f'{3!=4}', 'True')
889 self.assertEqual(f'{3!=4:}', 'True')
890 self.assertEqual(f'{3!=4!s}', 'True')
891 self.assertEqual(f'{3!=4!s:.3}', 'Tru')
892
Eric V. Smith9a4135e2019-05-08 16:28:48 -0400893 def test_equal_equal(self):
894 # Because an expression ending in = has special meaning,
895 # there's a special test for ==. Make sure it works.
896
897 self.assertEqual(f'{0==1}', 'False')
898
Eric V. Smith235a6f02015-09-19 14:51:32 -0400899 def test_conversions(self):
900 self.assertEqual(f'{3.14:10.10}', ' 3.14')
901 self.assertEqual(f'{3.14!s:10.10}', '3.14 ')
902 self.assertEqual(f'{3.14!r:10.10}', '3.14 ')
903 self.assertEqual(f'{3.14!a:10.10}', '3.14 ')
904
905 self.assertEqual(f'{"a"}', 'a')
906 self.assertEqual(f'{"a"!r}', "'a'")
907 self.assertEqual(f'{"a"!a}', "'a'")
908
909 # Not a conversion.
910 self.assertEqual(f'{"a!r"}', "a!r")
911
912 # Not a conversion, but show that ! is allowed in a format spec.
913 self.assertEqual(f'{3.14:!<10.10}', '3.14!!!!!!')
914
Eric V. Smith235a6f02015-09-19 14:51:32 -0400915 self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character',
916 ["f'{3!g}'",
917 "f'{3!A}'",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400918 "f'{3!3}'",
919 "f'{3!G}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400920 "f'{3!!}'",
921 "f'{3!:}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400922 "f'{3! s}'", # no space before conversion char
Eric V. Smith235a6f02015-09-19 14:51:32 -0400923 ])
924
925 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
926 ["f'{x!s{y}}'",
927 "f'{3!ss}'",
928 "f'{3!ss:}'",
929 "f'{3!ss:s}'",
930 ])
931
932 def test_assignment(self):
933 self.assertAllRaise(SyntaxError, 'invalid syntax',
934 ["f'' = 3",
935 "f'{0}' = x",
936 "f'{x}' = x",
937 ])
938
939 def test_del(self):
940 self.assertAllRaise(SyntaxError, 'invalid syntax',
941 ["del f''",
942 "del '' f''",
943 ])
944
945 def test_mismatched_braces(self):
946 self.assertAllRaise(SyntaxError, "f-string: single '}' is not allowed",
947 ["f'{{}'",
948 "f'{{}}}'",
949 "f'}'",
950 "f'x}'",
951 "f'x}x'",
Jason R. Coombsda25abf72016-11-06 11:14:48 -0500952 r"f'\u007b}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400953
954 # Can't have { or } in a format spec.
955 "f'{3:}>10}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400956 "f'{3:}}>10}'",
957 ])
958
959 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
960 ["f'{3:{{>10}'",
961 "f'{3'",
962 "f'{3!'",
963 "f'{3:'",
964 "f'{3!s'",
965 "f'{3!s:'",
966 "f'{3!s:3'",
967 "f'x{'",
968 "f'x{x'",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400969 "f'{x'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400970 "f'{3:s'",
971 "f'{{{'",
972 "f'{{}}{'",
973 "f'{'",
974 ])
975
Eric V. Smith235a6f02015-09-19 14:51:32 -0400976 # But these are just normal strings.
977 self.assertEqual(f'{"{"}', '{')
978 self.assertEqual(f'{"}"}', '}')
979 self.assertEqual(f'{3:{"}"}>10}', '}}}}}}}}}3')
980 self.assertEqual(f'{2:{"{"}>10}', '{{{{{{{{{2')
981
982 def test_if_conditional(self):
983 # There's special logic in compile.c to test if the
984 # conditional for an if (and while) are constants. Exercise
985 # that code.
986
987 def test_fstring(x, expected):
988 flag = 0
989 if f'{x}':
990 flag = 1
991 else:
992 flag = 2
993 self.assertEqual(flag, expected)
994
995 def test_concat_empty(x, expected):
996 flag = 0
997 if '' f'{x}':
998 flag = 1
999 else:
1000 flag = 2
1001 self.assertEqual(flag, expected)
1002
1003 def test_concat_non_empty(x, expected):
1004 flag = 0
1005 if ' ' f'{x}':
1006 flag = 1
1007 else:
1008 flag = 2
1009 self.assertEqual(flag, expected)
1010
1011 test_fstring('', 2)
1012 test_fstring(' ', 1)
1013
1014 test_concat_empty('', 2)
1015 test_concat_empty(' ', 1)
1016
1017 test_concat_non_empty('', 1)
1018 test_concat_non_empty(' ', 1)
1019
1020 def test_empty_format_specifier(self):
1021 x = 'test'
1022 self.assertEqual(f'{x}', 'test')
1023 self.assertEqual(f'{x:}', 'test')
1024 self.assertEqual(f'{x!s:}', 'test')
1025 self.assertEqual(f'{x!r:}', "'test'")
1026
1027 def test_str_format_differences(self):
1028 d = {'a': 'string',
1029 0: 'integer',
1030 }
1031 a = 0
1032 self.assertEqual(f'{d[0]}', 'integer')
1033 self.assertEqual(f'{d["a"]}', 'string')
1034 self.assertEqual(f'{d[a]}', 'integer')
1035 self.assertEqual('{d[a]}'.format(d=d), 'string')
1036 self.assertEqual('{d[0]}'.format(d=d), 'integer')
1037
Eric V. Smith135d5f42016-02-05 18:23:08 -05001038 def test_errors(self):
1039 # see issue 26287
Serhiy Storchaka13c8f322016-10-31 08:13:00 +02001040 self.assertAllRaise(TypeError, 'unsupported',
Eric V. Smith135d5f42016-02-05 18:23:08 -05001041 [r"f'{(lambda: 0):x}'",
1042 r"f'{(0,):x}'",
1043 ])
1044 self.assertAllRaise(ValueError, 'Unknown format code',
1045 [r"f'{1000:j}'",
1046 r"f'{1000:j}'",
1047 ])
1048
Eric V. Smith235a6f02015-09-19 14:51:32 -04001049 def test_loop(self):
1050 for i in range(1000):
1051 self.assertEqual(f'i:{i}', 'i:' + str(i))
1052
1053 def test_dict(self):
1054 d = {'"': 'dquote',
1055 "'": 'squote',
1056 'foo': 'bar',
1057 }
Eric V. Smith235a6f02015-09-19 14:51:32 -04001058 self.assertEqual(f'''{d["'"]}''', 'squote')
1059 self.assertEqual(f"""{d['"']}""", 'dquote')
1060
1061 self.assertEqual(f'{d["foo"]}', 'bar')
1062 self.assertEqual(f"{d['foo']}", 'bar')
Eric V. Smith235a6f02015-09-19 14:51:32 -04001063
ericvsmith11e97f22017-06-16 06:19:32 -04001064 def test_backslash_char(self):
1065 # Check eval of a backslash followed by a control char.
1066 # See bpo-30682: this used to raise an assert in pydebug mode.
1067 self.assertEqual(eval('f"\\\n"'), '')
1068 self.assertEqual(eval('f"\\\r"'), '')
1069
Eric V. Smith9a4135e2019-05-08 16:28:48 -04001070 def test_debug_conversion(self):
1071 x = 'A string'
1072 self.assertEqual(f'{x=}', 'x=' + repr(x))
1073 self.assertEqual(f'{x =}', 'x =' + repr(x))
1074 self.assertEqual(f'{x=!s}', 'x=' + str(x))
1075 self.assertEqual(f'{x=!r}', 'x=' + repr(x))
1076 self.assertEqual(f'{x=!a}', 'x=' + ascii(x))
1077
1078 x = 2.71828
1079 self.assertEqual(f'{x=:.2f}', 'x=' + format(x, '.2f'))
1080 self.assertEqual(f'{x=:}', 'x=' + format(x, ''))
1081 self.assertEqual(f'{x=!r:^20}', 'x=' + format(repr(x), '^20'))
1082 self.assertEqual(f'{x=!s:^20}', 'x=' + format(str(x), '^20'))
1083 self.assertEqual(f'{x=!a:^20}', 'x=' + format(ascii(x), '^20'))
1084
1085 x = 9
1086 self.assertEqual(f'{3*x+15=}', '3*x+15=42')
1087
1088 # There is code in ast.c that deals with non-ascii expression values. So,
1089 # use a unicode identifier to trigger that.
1090 tenπ = 31.4
1091 self.assertEqual(f'{tenπ=:.2f}', 'tenπ=31.40')
1092
1093 # Also test with Unicode in non-identifiers.
1094 self.assertEqual(f'{"Σ"=}', '"Σ"=\'Σ\'')
1095
1096 # Make sure nested fstrings still work.
1097 self.assertEqual(f'{f"{3.1415=:.1f}":*^20}', '*****3.1415=3.1*****')
1098
1099 # Make sure text before and after an expression with = works
1100 # correctly.
1101 pi = 'π'
1102 self.assertEqual(f'alpha α {pi=} ω omega', "alpha α pi='π' ω omega")
1103
1104 # Check multi-line expressions.
1105 self.assertEqual(f'''{
11063
1107=}''', '\n3\n=3')
1108
1109 # Since = is handled specially, make sure all existing uses of
1110 # it still work.
1111
1112 self.assertEqual(f'{0==1}', 'False')
1113 self.assertEqual(f'{0!=1}', 'True')
1114 self.assertEqual(f'{0<=1}', 'True')
1115 self.assertEqual(f'{0>=1}', 'False')
1116 self.assertEqual(f'{(x:="5")}', '5')
1117 self.assertEqual(x, '5')
1118 self.assertEqual(f'{(x:=5)}', '5')
1119 self.assertEqual(x, 5)
1120 self.assertEqual(f'{"="}', '=')
1121
1122 x = 20
1123 # This isn't an assignment expression, it's 'x', with a format
1124 # spec of '=10'. See test_walrus: you need to use parens.
1125 self.assertEqual(f'{x:=10}', ' 20')
1126
1127 # Test named function parameters, to make sure '=' parsing works
1128 # there.
1129 def f(a):
1130 nonlocal x
1131 oldx = x
1132 x = a
1133 return oldx
1134 x = 0
1135 self.assertEqual(f'{f(a="3=")}', '0')
1136 self.assertEqual(x, '3=')
1137 self.assertEqual(f'{f(a=4)}', '3=')
1138 self.assertEqual(x, 4)
1139
1140 # Make sure __format__ is being called.
1141 class C:
1142 def __format__(self, s):
1143 return f'FORMAT-{s}'
1144 def __repr__(self):
1145 return 'REPR'
1146
1147 self.assertEqual(f'{C()=}', 'C()=REPR')
1148 self.assertEqual(f'{C()=!r}', 'C()=REPR')
1149 self.assertEqual(f'{C()=:}', 'C()=FORMAT-')
1150 self.assertEqual(f'{C()=: }', 'C()=FORMAT- ')
1151 self.assertEqual(f'{C()=:x}', 'C()=FORMAT-x')
1152 self.assertEqual(f'{C()=!r:*^20}', 'C()=********REPR********')
1153
Pablo Galindo26f55c22019-05-12 01:43:04 +01001154 self.assertRaises(SyntaxError, eval, "f'{C=]'")
1155
Eric V. Smith6f6ff8a2019-05-27 15:31:52 -04001156 # Make sure leading and following text works.
1157 x = 'foo'
1158 self.assertEqual(f'X{x=}Y', 'Xx='+repr(x)+'Y')
1159
1160 # Make sure whitespace around the = works.
1161 self.assertEqual(f'X{x =}Y', 'Xx ='+repr(x)+'Y')
1162 self.assertEqual(f'X{x= }Y', 'Xx= '+repr(x)+'Y')
1163 self.assertEqual(f'X{x = }Y', 'Xx = '+repr(x)+'Y')
1164
1165 # These next lines contains tabs. Backslash escapes don't
1166 # work in f-strings.
Min ho Kim96e12d52019-07-22 06:12:33 +10001167 # patchcheck doesn't like these tabs. So the only way to test
Eric V. Smith6f6ff8a2019-05-27 15:31:52 -04001168 # this will be to dynamically created and exec the f-strings. But
1169 # that's such a hassle I'll save it for another day. For now, convert
1170 # the tabs to spaces just to shut up patchcheck.
1171 #self.assertEqual(f'X{x =}Y', 'Xx\t='+repr(x)+'Y')
1172 #self.assertEqual(f'X{x = }Y', 'Xx\t=\t'+repr(x)+'Y')
1173
Eric V. Smith9a4135e2019-05-08 16:28:48 -04001174 def test_walrus(self):
1175 x = 20
1176 # This isn't an assignment expression, it's 'x', with a format
1177 # spec of '=10'.
1178 self.assertEqual(f'{x:=10}', ' 20')
1179
1180 # This is an assignment expression, which requires parens.
1181 self.assertEqual(f'{(x:=10)}', '10')
1182 self.assertEqual(x, 10)
1183
Łukasz Langae7c566c2017-09-06 17:27:58 -07001184
Eric V. Smith235a6f02015-09-19 14:51:32 -04001185if __name__ == '__main__':
1186 unittest.main()