blob: e423b52cd585528a465c509dcdec700651c4532b [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
13import unittest
Lysandros Nikolaou6cb0ad22020-05-26 03:10:00 +030014from test.support import use_old_parser
Eric V. Smith235a6f02015-09-19 14:51:32 -040015
16a_global = 'global variable'
17
18# You could argue that I'm too strict in looking for specific error
19# values with assertRaisesRegex, but without it it's way too easy to
20# make a syntax error in the test strings. Especially with all of the
21# triple quotes, raw strings, backslashes, etc. I think it's a
22# worthwhile tradeoff. When I switched to this method, I found many
23# examples where I wasn't testing what I thought I was.
24
25class TestCase(unittest.TestCase):
26 def assertAllRaise(self, exception_type, regex, error_strings):
27 for str in error_strings:
28 with self.subTest(str=str):
29 with self.assertRaisesRegex(exception_type, regex):
30 eval(str)
31
32 def test__format__lookup(self):
33 # Make sure __format__ is looked up on the type, not the instance.
34 class X:
35 def __format__(self, spec):
36 return 'class'
37
38 x = X()
39
40 # Add a bound __format__ method to the 'y' instance, but not
41 # the 'x' instance.
42 y = X()
43 y.__format__ = types.MethodType(lambda self, spec: 'instance', y)
44
45 self.assertEqual(f'{y}', format(y))
46 self.assertEqual(f'{y}', 'class')
47 self.assertEqual(format(x), format(y))
48
49 # __format__ is not called this way, but still make sure it
50 # returns what we expect (so we can make sure we're bypassing
51 # it).
52 self.assertEqual(x.__format__(''), 'class')
53 self.assertEqual(y.__format__(''), 'instance')
54
55 # This is how __format__ is actually called.
56 self.assertEqual(type(x).__format__(x, ''), 'class')
57 self.assertEqual(type(y).__format__(y, ''), 'class')
58
59 def test_ast(self):
60 # Inspired by http://bugs.python.org/issue24975
61 class X:
62 def __init__(self):
63 self.called = False
64 def __call__(self):
65 self.called = True
66 return 4
67 x = X()
68 expr = """
69a = 10
70f'{a * x()}'"""
71 t = ast.parse(expr)
72 c = compile(t, '', 'exec')
73
74 # Make sure x was not called.
75 self.assertFalse(x.called)
76
77 # Actually run the code.
78 exec(c)
79
80 # Make sure x was called.
81 self.assertTrue(x.called)
82
Łukasz Langae7c566c2017-09-06 17:27:58 -070083 def test_ast_line_numbers(self):
84 expr = """
85a = 10
86f'{a * x()}'"""
87 t = ast.parse(expr)
88 self.assertEqual(type(t), ast.Module)
89 self.assertEqual(len(t.body), 2)
90 # check `a = 10`
91 self.assertEqual(type(t.body[0]), ast.Assign)
92 self.assertEqual(t.body[0].lineno, 2)
93 # check `f'...'`
94 self.assertEqual(type(t.body[1]), ast.Expr)
95 self.assertEqual(type(t.body[1].value), ast.JoinedStr)
96 self.assertEqual(len(t.body[1].value.values), 1)
97 self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)
98 self.assertEqual(t.body[1].lineno, 3)
99 self.assertEqual(t.body[1].value.lineno, 3)
100 self.assertEqual(t.body[1].value.values[0].lineno, 3)
101 # check the binop location
102 binop = t.body[1].value.values[0].value
103 self.assertEqual(type(binop), ast.BinOp)
104 self.assertEqual(type(binop.left), ast.Name)
105 self.assertEqual(type(binop.op), ast.Mult)
106 self.assertEqual(type(binop.right), ast.Call)
107 self.assertEqual(binop.lineno, 3)
108 self.assertEqual(binop.left.lineno, 3)
109 self.assertEqual(binop.right.lineno, 3)
110 self.assertEqual(binop.col_offset, 3)
111 self.assertEqual(binop.left.col_offset, 3)
112 self.assertEqual(binop.right.col_offset, 7)
113
114 def test_ast_line_numbers_multiple_formattedvalues(self):
115 expr = """
116f'no formatted values'
117f'eggs {a * x()} spam {b + y()}'"""
118 t = ast.parse(expr)
119 self.assertEqual(type(t), ast.Module)
120 self.assertEqual(len(t.body), 2)
121 # check `f'no formatted value'`
122 self.assertEqual(type(t.body[0]), ast.Expr)
123 self.assertEqual(type(t.body[0].value), ast.JoinedStr)
124 self.assertEqual(t.body[0].lineno, 2)
125 # check `f'...'`
126 self.assertEqual(type(t.body[1]), ast.Expr)
127 self.assertEqual(type(t.body[1].value), ast.JoinedStr)
128 self.assertEqual(len(t.body[1].value.values), 4)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300129 self.assertEqual(type(t.body[1].value.values[0]), ast.Constant)
130 self.assertEqual(type(t.body[1].value.values[0].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700131 self.assertEqual(type(t.body[1].value.values[1]), ast.FormattedValue)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300132 self.assertEqual(type(t.body[1].value.values[2]), ast.Constant)
133 self.assertEqual(type(t.body[1].value.values[2].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700134 self.assertEqual(type(t.body[1].value.values[3]), ast.FormattedValue)
135 self.assertEqual(t.body[1].lineno, 3)
136 self.assertEqual(t.body[1].value.lineno, 3)
137 self.assertEqual(t.body[1].value.values[0].lineno, 3)
138 self.assertEqual(t.body[1].value.values[1].lineno, 3)
139 self.assertEqual(t.body[1].value.values[2].lineno, 3)
140 self.assertEqual(t.body[1].value.values[3].lineno, 3)
141 # check the first binop location
142 binop1 = t.body[1].value.values[1].value
143 self.assertEqual(type(binop1), ast.BinOp)
144 self.assertEqual(type(binop1.left), ast.Name)
145 self.assertEqual(type(binop1.op), ast.Mult)
146 self.assertEqual(type(binop1.right), ast.Call)
147 self.assertEqual(binop1.lineno, 3)
148 self.assertEqual(binop1.left.lineno, 3)
149 self.assertEqual(binop1.right.lineno, 3)
150 self.assertEqual(binop1.col_offset, 8)
151 self.assertEqual(binop1.left.col_offset, 8)
152 self.assertEqual(binop1.right.col_offset, 12)
153 # check the second binop location
154 binop2 = t.body[1].value.values[3].value
155 self.assertEqual(type(binop2), ast.BinOp)
156 self.assertEqual(type(binop2.left), ast.Name)
157 self.assertEqual(type(binop2.op), ast.Add)
158 self.assertEqual(type(binop2.right), ast.Call)
159 self.assertEqual(binop2.lineno, 3)
160 self.assertEqual(binop2.left.lineno, 3)
161 self.assertEqual(binop2.right.lineno, 3)
162 self.assertEqual(binop2.col_offset, 23)
163 self.assertEqual(binop2.left.col_offset, 23)
164 self.assertEqual(binop2.right.col_offset, 27)
165
166 def test_ast_line_numbers_nested(self):
167 expr = """
168a = 10
169f'{a * f"-{x()}-"}'"""
170 t = ast.parse(expr)
171 self.assertEqual(type(t), ast.Module)
172 self.assertEqual(len(t.body), 2)
173 # check `a = 10`
174 self.assertEqual(type(t.body[0]), ast.Assign)
175 self.assertEqual(t.body[0].lineno, 2)
176 # check `f'...'`
177 self.assertEqual(type(t.body[1]), ast.Expr)
178 self.assertEqual(type(t.body[1].value), ast.JoinedStr)
179 self.assertEqual(len(t.body[1].value.values), 1)
180 self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)
181 self.assertEqual(t.body[1].lineno, 3)
182 self.assertEqual(t.body[1].value.lineno, 3)
183 self.assertEqual(t.body[1].value.values[0].lineno, 3)
184 # check the binop location
185 binop = t.body[1].value.values[0].value
186 self.assertEqual(type(binop), ast.BinOp)
187 self.assertEqual(type(binop.left), ast.Name)
188 self.assertEqual(type(binop.op), ast.Mult)
189 self.assertEqual(type(binop.right), ast.JoinedStr)
190 self.assertEqual(binop.lineno, 3)
191 self.assertEqual(binop.left.lineno, 3)
192 self.assertEqual(binop.right.lineno, 3)
193 self.assertEqual(binop.col_offset, 3)
194 self.assertEqual(binop.left.col_offset, 3)
195 self.assertEqual(binop.right.col_offset, 7)
196 # check the nested call location
197 self.assertEqual(len(binop.right.values), 3)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300198 self.assertEqual(type(binop.right.values[0]), ast.Constant)
199 self.assertEqual(type(binop.right.values[0].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700200 self.assertEqual(type(binop.right.values[1]), ast.FormattedValue)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300201 self.assertEqual(type(binop.right.values[2]), ast.Constant)
202 self.assertEqual(type(binop.right.values[2].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700203 self.assertEqual(binop.right.values[0].lineno, 3)
204 self.assertEqual(binop.right.values[1].lineno, 3)
205 self.assertEqual(binop.right.values[2].lineno, 3)
206 call = binop.right.values[1].value
207 self.assertEqual(type(call), ast.Call)
208 self.assertEqual(call.lineno, 3)
Lysandros Nikolaou37af21b2020-04-29 03:43:50 +0300209 self.assertEqual(call.col_offset, 11)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700210
211 def test_ast_line_numbers_duplicate_expression(self):
212 """Duplicate expression
213
214 NOTE: this is currently broken, always sets location of the first
215 expression.
216 """
217 expr = """
218a = 10
219f'{a * x()} {a * x()} {a * x()}'
220"""
221 t = ast.parse(expr)
222 self.assertEqual(type(t), ast.Module)
223 self.assertEqual(len(t.body), 2)
224 # check `a = 10`
225 self.assertEqual(type(t.body[0]), ast.Assign)
226 self.assertEqual(t.body[0].lineno, 2)
227 # check `f'...'`
228 self.assertEqual(type(t.body[1]), ast.Expr)
229 self.assertEqual(type(t.body[1].value), ast.JoinedStr)
230 self.assertEqual(len(t.body[1].value.values), 5)
231 self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300232 self.assertEqual(type(t.body[1].value.values[1]), ast.Constant)
233 self.assertEqual(type(t.body[1].value.values[1].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700234 self.assertEqual(type(t.body[1].value.values[2]), ast.FormattedValue)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300235 self.assertEqual(type(t.body[1].value.values[3]), ast.Constant)
236 self.assertEqual(type(t.body[1].value.values[3].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700237 self.assertEqual(type(t.body[1].value.values[4]), ast.FormattedValue)
238 self.assertEqual(t.body[1].lineno, 3)
239 self.assertEqual(t.body[1].value.lineno, 3)
240 self.assertEqual(t.body[1].value.values[0].lineno, 3)
241 self.assertEqual(t.body[1].value.values[1].lineno, 3)
242 self.assertEqual(t.body[1].value.values[2].lineno, 3)
243 self.assertEqual(t.body[1].value.values[3].lineno, 3)
244 self.assertEqual(t.body[1].value.values[4].lineno, 3)
245 # check the first binop location
246 binop = t.body[1].value.values[0].value
247 self.assertEqual(type(binop), ast.BinOp)
248 self.assertEqual(type(binop.left), ast.Name)
249 self.assertEqual(type(binop.op), ast.Mult)
250 self.assertEqual(type(binop.right), ast.Call)
251 self.assertEqual(binop.lineno, 3)
252 self.assertEqual(binop.left.lineno, 3)
253 self.assertEqual(binop.right.lineno, 3)
254 self.assertEqual(binop.col_offset, 3)
255 self.assertEqual(binop.left.col_offset, 3)
256 self.assertEqual(binop.right.col_offset, 7)
257 # check the second binop location
258 binop = t.body[1].value.values[2].value
259 self.assertEqual(type(binop), ast.BinOp)
260 self.assertEqual(type(binop.left), ast.Name)
261 self.assertEqual(type(binop.op), ast.Mult)
262 self.assertEqual(type(binop.right), ast.Call)
263 self.assertEqual(binop.lineno, 3)
264 self.assertEqual(binop.left.lineno, 3)
265 self.assertEqual(binop.right.lineno, 3)
266 self.assertEqual(binop.col_offset, 3) # FIXME: this is wrong
267 self.assertEqual(binop.left.col_offset, 3) # FIXME: this is wrong
268 self.assertEqual(binop.right.col_offset, 7) # FIXME: this is wrong
269 # check the third binop location
270 binop = t.body[1].value.values[4].value
271 self.assertEqual(type(binop), ast.BinOp)
272 self.assertEqual(type(binop.left), ast.Name)
273 self.assertEqual(type(binop.op), ast.Mult)
274 self.assertEqual(type(binop.right), ast.Call)
275 self.assertEqual(binop.lineno, 3)
276 self.assertEqual(binop.left.lineno, 3)
277 self.assertEqual(binop.right.lineno, 3)
278 self.assertEqual(binop.col_offset, 3) # FIXME: this is wrong
279 self.assertEqual(binop.left.col_offset, 3) # FIXME: this is wrong
280 self.assertEqual(binop.right.col_offset, 7) # FIXME: this is wrong
281
282 def test_ast_line_numbers_multiline_fstring(self):
Anthony Sottile995d9b92019-01-12 20:05:13 -0800283 # See bpo-30465 for details.
Łukasz Langae7c566c2017-09-06 17:27:58 -0700284 expr = """
285a = 10
286f'''
287 {a
288 *
289 x()}
290non-important content
291'''
292"""
293 t = ast.parse(expr)
294 self.assertEqual(type(t), ast.Module)
295 self.assertEqual(len(t.body), 2)
296 # check `a = 10`
297 self.assertEqual(type(t.body[0]), ast.Assign)
298 self.assertEqual(t.body[0].lineno, 2)
299 # check `f'...'`
300 self.assertEqual(type(t.body[1]), ast.Expr)
301 self.assertEqual(type(t.body[1].value), ast.JoinedStr)
302 self.assertEqual(len(t.body[1].value.values), 3)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300303 self.assertEqual(type(t.body[1].value.values[0]), ast.Constant)
304 self.assertEqual(type(t.body[1].value.values[0].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700305 self.assertEqual(type(t.body[1].value.values[1]), ast.FormattedValue)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300306 self.assertEqual(type(t.body[1].value.values[2]), ast.Constant)
307 self.assertEqual(type(t.body[1].value.values[2].value), str)
Anthony Sottile995d9b92019-01-12 20:05:13 -0800308 self.assertEqual(t.body[1].lineno, 3)
309 self.assertEqual(t.body[1].value.lineno, 3)
310 self.assertEqual(t.body[1].value.values[0].lineno, 3)
311 self.assertEqual(t.body[1].value.values[1].lineno, 3)
312 self.assertEqual(t.body[1].value.values[2].lineno, 3)
313 self.assertEqual(t.body[1].col_offset, 0)
314 self.assertEqual(t.body[1].value.col_offset, 0)
315 self.assertEqual(t.body[1].value.values[0].col_offset, 0)
316 self.assertEqual(t.body[1].value.values[1].col_offset, 0)
317 self.assertEqual(t.body[1].value.values[2].col_offset, 0)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700318 # NOTE: the following lineno information and col_offset is correct for
319 # expressions within FormattedValues.
320 binop = t.body[1].value.values[1].value
321 self.assertEqual(type(binop), ast.BinOp)
322 self.assertEqual(type(binop.left), ast.Name)
323 self.assertEqual(type(binop.op), ast.Mult)
324 self.assertEqual(type(binop.right), ast.Call)
325 self.assertEqual(binop.lineno, 4)
326 self.assertEqual(binop.left.lineno, 4)
327 self.assertEqual(binop.right.lineno, 6)
Anthony Sottile995d9b92019-01-12 20:05:13 -0800328 self.assertEqual(binop.col_offset, 4)
329 self.assertEqual(binop.left.col_offset, 4)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700330 self.assertEqual(binop.right.col_offset, 7)
331
Serhiy Storchaka4cc30ae2016-12-11 19:37:19 +0200332 def test_docstring(self):
333 def f():
334 f'''Not a docstring'''
335 self.assertIsNone(f.__doc__)
336 def g():
337 '''Not a docstring''' \
338 f''
339 self.assertIsNone(g.__doc__)
340
Eric V. Smith235a6f02015-09-19 14:51:32 -0400341 def test_literal_eval(self):
Eric V. Smith235a6f02015-09-19 14:51:32 -0400342 with self.assertRaisesRegex(ValueError, 'malformed node or string'):
Serhiy Storchaka4cc30ae2016-12-11 19:37:19 +0200343 ast.literal_eval("f'x'")
Eric V. Smith235a6f02015-09-19 14:51:32 -0400344
345 def test_ast_compile_time_concat(self):
346 x = ['']
347
348 expr = """x[0] = 'foo' f'{3}'"""
349 t = ast.parse(expr)
350 c = compile(t, '', 'exec')
351 exec(c)
352 self.assertEqual(x[0], 'foo3')
353
Eric V. Smith9b88fdf2016-11-07 17:54:01 -0500354 def test_compile_time_concat_errors(self):
355 self.assertAllRaise(SyntaxError,
356 'cannot mix bytes and nonbytes literals',
357 [r"""f'' b''""",
358 r"""b'' f''""",
359 ])
360
Eric V. Smith235a6f02015-09-19 14:51:32 -0400361 def test_literal(self):
362 self.assertEqual(f'', '')
363 self.assertEqual(f'a', 'a')
364 self.assertEqual(f' ', ' ')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400365
366 def test_unterminated_string(self):
367 self.assertAllRaise(SyntaxError, 'f-string: unterminated string',
368 [r"""f'{"x'""",
369 r"""f'{"x}'""",
370 r"""f'{("x'""",
371 r"""f'{("x}'""",
372 ])
373
374 def test_mismatched_parens(self):
Serhiy Storchaka58159ef2019-01-12 09:46:50 +0200375 self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\}' "
376 r"does not match opening parenthesis '\('",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400377 ["f'{((}'",
378 ])
Serhiy Storchaka58159ef2019-01-12 09:46:50 +0200379 self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\)' "
380 r"does not match opening parenthesis '\['",
381 ["f'{a[4)}'",
382 ])
383 self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\]' "
384 r"does not match opening parenthesis '\('",
385 ["f'{a(4]}'",
386 ])
387 self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\}' "
388 r"does not match opening parenthesis '\['",
389 ["f'{a[4}'",
390 ])
391 self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\}' "
392 r"does not match opening parenthesis '\('",
393 ["f'{a(4}'",
394 ])
395 self.assertRaises(SyntaxError, eval, "f'{" + "("*500 + "}'")
Eric V. Smith235a6f02015-09-19 14:51:32 -0400396
397 def test_double_braces(self):
398 self.assertEqual(f'{{', '{')
399 self.assertEqual(f'a{{', 'a{')
400 self.assertEqual(f'{{b', '{b')
401 self.assertEqual(f'a{{b', 'a{b')
402 self.assertEqual(f'}}', '}')
403 self.assertEqual(f'a}}', 'a}')
404 self.assertEqual(f'}}b', '}b')
405 self.assertEqual(f'a}}b', 'a}b')
Eric V. Smith451d0e32016-09-09 21:56:20 -0400406 self.assertEqual(f'{{}}', '{}')
407 self.assertEqual(f'a{{}}', 'a{}')
408 self.assertEqual(f'{{b}}', '{b}')
409 self.assertEqual(f'{{}}c', '{}c')
410 self.assertEqual(f'a{{b}}', 'a{b}')
411 self.assertEqual(f'a{{}}c', 'a{}c')
412 self.assertEqual(f'{{b}}c', '{b}c')
413 self.assertEqual(f'a{{b}}c', 'a{b}c')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400414
415 self.assertEqual(f'{{{10}', '{10')
416 self.assertEqual(f'}}{10}', '}10')
417 self.assertEqual(f'}}{{{10}', '}{10')
418 self.assertEqual(f'}}a{{{10}', '}a{10')
419
420 self.assertEqual(f'{10}{{', '10{')
421 self.assertEqual(f'{10}}}', '10}')
422 self.assertEqual(f'{10}}}{{', '10}{')
423 self.assertEqual(f'{10}}}a{{' '}', '10}a{}')
424
425 # Inside of strings, don't interpret doubled brackets.
426 self.assertEqual(f'{"{{}}"}', '{{}}')
427
428 self.assertAllRaise(TypeError, 'unhashable type',
429 ["f'{ {{}} }'", # dict in a set
430 ])
431
432 def test_compile_time_concat(self):
433 x = 'def'
434 self.assertEqual('abc' f'## {x}ghi', 'abc## defghi')
435 self.assertEqual('abc' f'{x}' 'ghi', 'abcdefghi')
436 self.assertEqual('abc' f'{x}' 'gh' f'i{x:4}', 'abcdefghidef ')
437 self.assertEqual('{x}' f'{x}', '{x}def')
438 self.assertEqual('{x' f'{x}', '{xdef')
439 self.assertEqual('{x}' f'{x}', '{x}def')
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(f'{x}' 'x}}', 'defx}}')
444 self.assertEqual(f'{x}' '', 'def')
445 self.assertEqual('' f'{x}' '', 'def')
446 self.assertEqual('' f'{x}', 'def')
447 self.assertEqual(f'{x}' '2', 'def2')
448 self.assertEqual('1' f'{x}' '2', '1def2')
449 self.assertEqual('1' f'{x}', '1def')
450 self.assertEqual(f'{x}' f'-{x}', 'def-def')
451 self.assertEqual('' f'', '')
452 self.assertEqual('' f'' '', '')
453 self.assertEqual('' f'' '' f'', '')
454 self.assertEqual(f'', '')
455 self.assertEqual(f'' '', '')
456 self.assertEqual(f'' '' f'', '')
457 self.assertEqual(f'' '' f'' '', '')
458
459 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
460 ["f'{3' f'}'", # can't concat to get a valid f-string
461 ])
462
463 def test_comments(self):
464 # These aren't comments, since they're in strings.
465 d = {'#': 'hash'}
466 self.assertEqual(f'{"#"}', '#')
467 self.assertEqual(f'{d["#"]}', 'hash')
468
Eric V. Smith09835dc2016-09-11 18:58:20 -0400469 self.assertAllRaise(SyntaxError, "f-string expression part cannot include '#'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400470 ["f'{1#}'", # error because the expression becomes "(1#)"
471 "f'{3(#)}'",
Eric V. Smith09835dc2016-09-11 18:58:20 -0400472 "f'{#}'",
Serhiy Storchaka58159ef2019-01-12 09:46:50 +0200473 ])
474 self.assertAllRaise(SyntaxError, r"f-string: unmatched '\)'",
475 ["f'{)#}'", # When wrapped in parens, this becomes
Eric V. Smith35a24c52016-09-11 19:01:22 -0400476 # '()#)'. Make sure that doesn't compile.
Eric V. Smith235a6f02015-09-19 14:51:32 -0400477 ])
478
479 def test_many_expressions(self):
480 # Create a string with many expressions in it. Note that
481 # because we have a space in here as a literal, we're actually
482 # going to use twice as many ast nodes: one for each literal
483 # plus one for each expression.
484 def build_fstr(n, extra=''):
485 return "f'" + ('{x} ' * n) + extra + "'"
486
487 x = 'X'
488 width = 1
489
490 # Test around 256.
491 for i in range(250, 260):
492 self.assertEqual(eval(build_fstr(i)), (x+' ')*i)
493
494 # Test concatenating 2 largs fstrings.
495 self.assertEqual(eval(build_fstr(255)*256), (x+' ')*(255*256))
496
497 s = build_fstr(253, '{x:{width}} ')
498 self.assertEqual(eval(s), (x+' ')*254)
499
500 # Test lots of expressions and constants, concatenated.
501 s = "f'{1}' 'x' 'y'" * 1024
502 self.assertEqual(eval(s), '1xy' * 1024)
503
504 def test_format_specifier_expressions(self):
505 width = 10
506 precision = 4
507 value = decimal.Decimal('12.34567')
508 self.assertEqual(f'result: {value:{width}.{precision}}', 'result: 12.35')
509 self.assertEqual(f'result: {value:{width!r}.{precision}}', 'result: 12.35')
510 self.assertEqual(f'result: {value:{width:0}.{precision:1}}', 'result: 12.35')
511 self.assertEqual(f'result: {value:{1}{0:0}.{precision:1}}', 'result: 12.35')
512 self.assertEqual(f'result: {value:{ 1}{ 0:0}.{ precision:1}}', 'result: 12.35')
513 self.assertEqual(f'{10:#{1}0x}', ' 0xa')
514 self.assertEqual(f'{10:{"#"}1{0}{"x"}}', ' 0xa')
515 self.assertEqual(f'{-10:-{"#"}1{0}x}', ' -0xa')
516 self.assertEqual(f'{-10:{"-"}#{1}0{"x"}}', ' -0xa')
517 self.assertEqual(f'{10:#{3 != {4:5} and width}x}', ' 0xa')
518
519 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
520 ["""f'{"s"!r{":10"}}'""",
521
522 # This looks like a nested format spec.
523 ])
524
525 self.assertAllRaise(SyntaxError, "invalid syntax",
Martin Panter263893c2016-07-28 01:25:31 +0000526 [# Invalid syntax inside a nested spec.
Eric V. Smith235a6f02015-09-19 14:51:32 -0400527 "f'{4:{/5}}'",
528 ])
529
530 self.assertAllRaise(SyntaxError, "f-string: expressions nested too deeply",
531 [# Can't nest format specifiers.
532 "f'result: {value:{width:{0}}.{precision:1}}'",
533 ])
534
535 self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character',
536 [# No expansion inside conversion or for
537 # the : or ! itself.
538 """f'{"s"!{"r"}}'""",
539 ])
540
541 def test_side_effect_order(self):
542 class X:
543 def __init__(self):
544 self.i = 0
545 def __format__(self, spec):
546 self.i += 1
547 return str(self.i)
548
549 x = X()
550 self.assertEqual(f'{x} {x}', '1 2')
551
552 def test_missing_expression(self):
553 self.assertAllRaise(SyntaxError, 'f-string: empty expression not allowed',
554 ["f'{}'",
555 "f'{ }'"
556 "f' {} '",
557 "f'{!r}'",
558 "f'{ !r}'",
559 "f'{10:{ }}'",
560 "f' { } '",
Eric V. Smith1d44c412015-09-23 07:49:00 -0400561
Serhiy Storchaka2e9cd582017-06-08 23:43:54 +0300562 # The Python parser ignores also the following
563 # whitespace characters in additional to a space.
564 "f'''{\t\f\r\n}'''",
565
Eric V. Smith1d44c412015-09-23 07:49:00 -0400566 # Catch the empty expression before the
567 # invalid conversion.
568 "f'{!x}'",
569 "f'{ !xr}'",
570 "f'{!x:}'",
571 "f'{!x:a}'",
572 "f'{ !xr:}'",
573 "f'{ !xr:a}'",
Eric V. Smith548c4d32015-09-23 08:00:01 -0400574
575 "f'{!}'",
576 "f'{:}'",
Eric V. Smithb2080f62015-09-23 10:24:43 -0400577
578 # We find the empty expression before the
579 # missing closing brace.
580 "f'{!'",
581 "f'{!s:'",
582 "f'{:'",
583 "f'{:x'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400584 ])
585
Serhiy Storchaka2e9cd582017-06-08 23:43:54 +0300586 # Different error message is raised for other whitespace characters.
Serhiy Storchaka74ea6b52020-05-12 12:42:04 +0300587 self.assertAllRaise(SyntaxError, r"invalid non-printable character U\+00A0",
Serhiy Storchaka2e9cd582017-06-08 23:43:54 +0300588 ["f'''{\xa0}'''",
589 "\xa0",
590 ])
591
Eric V. Smith235a6f02015-09-19 14:51:32 -0400592 def test_parens_in_expressions(self):
593 self.assertEqual(f'{3,}', '(3,)')
594
595 # Add these because when an expression is evaluated, parens
596 # are added around it. But we shouldn't go from an invalid
597 # expression to a valid one. The added parens are just
598 # supposed to allow whitespace (including newlines).
599 self.assertAllRaise(SyntaxError, 'invalid syntax',
600 ["f'{,}'",
601 "f'{,}'", # this is (,), which is an error
602 ])
603
Serhiy Storchaka58159ef2019-01-12 09:46:50 +0200604 self.assertAllRaise(SyntaxError, r"f-string: unmatched '\)'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400605 ["f'{3)+(4}'",
606 ])
607
608 self.assertAllRaise(SyntaxError, 'EOL while scanning string literal',
609 ["f'{\n}'",
610 ])
611
Eric V. Smith451d0e32016-09-09 21:56:20 -0400612 def test_backslashes_in_string_part(self):
613 self.assertEqual(f'\t', '\t')
614 self.assertEqual(r'\t', '\\t')
615 self.assertEqual(rf'\t', '\\t')
616 self.assertEqual(f'{2}\t', '2\t')
617 self.assertEqual(f'{2}\t{3}', '2\t3')
618 self.assertEqual(f'\t{3}', '\t3')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400619
Eric V. Smith451d0e32016-09-09 21:56:20 -0400620 self.assertEqual(f'\u0394', '\u0394')
621 self.assertEqual(r'\u0394', '\\u0394')
622 self.assertEqual(rf'\u0394', '\\u0394')
623 self.assertEqual(f'{2}\u0394', '2\u0394')
624 self.assertEqual(f'{2}\u0394{3}', '2\u03943')
625 self.assertEqual(f'\u0394{3}', '\u03943')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400626
Eric V. Smith451d0e32016-09-09 21:56:20 -0400627 self.assertEqual(f'\U00000394', '\u0394')
628 self.assertEqual(r'\U00000394', '\\U00000394')
629 self.assertEqual(rf'\U00000394', '\\U00000394')
630 self.assertEqual(f'{2}\U00000394', '2\u0394')
631 self.assertEqual(f'{2}\U00000394{3}', '2\u03943')
632 self.assertEqual(f'\U00000394{3}', '\u03943')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400633
Eric V. Smith451d0e32016-09-09 21:56:20 -0400634 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}', '\u0394')
635 self.assertEqual(f'{2}\N{GREEK CAPITAL LETTER DELTA}', '2\u0394')
636 self.assertEqual(f'{2}\N{GREEK CAPITAL LETTER DELTA}{3}', '2\u03943')
637 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}{3}', '\u03943')
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')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400641
Eric V. Smith451d0e32016-09-09 21:56:20 -0400642 self.assertEqual(f'\x20', ' ')
643 self.assertEqual(r'\x20', '\\x20')
644 self.assertEqual(rf'\x20', '\\x20')
645 self.assertEqual(f'{2}\x20', '2 ')
646 self.assertEqual(f'{2}\x20{3}', '2 3')
647 self.assertEqual(f'\x20{3}', ' 3')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400648
Eric V. Smith451d0e32016-09-09 21:56:20 -0400649 self.assertEqual(f'2\x20', '2 ')
650 self.assertEqual(f'2\x203', '2 3')
651 self.assertEqual(f'\x203', ' 3')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400652
Gregory P. Smithb4be87a2019-08-10 00:19:07 -0700653 with self.assertWarns(DeprecationWarning): # invalid escape sequence
Serhiy Storchaka0cd7a3f2017-05-25 13:33:55 +0300654 value = eval(r"f'\{6*7}'")
655 self.assertEqual(value, '\\42')
656 self.assertEqual(f'\\{6*7}', '\\42')
657 self.assertEqual(fr'\{6*7}', '\\42')
658
659 AMPERSAND = 'spam'
660 # Get the right unicode character (&), or pick up local variable
661 # depending on the number of backslashes.
662 self.assertEqual(f'\N{AMPERSAND}', '&')
663 self.assertEqual(f'\\N{AMPERSAND}', '\\Nspam')
664 self.assertEqual(fr'\N{AMPERSAND}', '\\Nspam')
665 self.assertEqual(f'\\\N{AMPERSAND}', '\\&')
666
Eric V. Smith451d0e32016-09-09 21:56:20 -0400667 def test_misformed_unicode_character_name(self):
668 # These test are needed because unicode names are parsed
669 # differently inside f-strings.
670 self.assertAllRaise(SyntaxError, r"\(unicode error\) 'unicodeescape' codec can't decode bytes in position .*: malformed \\N character escape",
671 [r"f'\N'",
672 r"f'\N{'",
673 r"f'\N{GREEK CAPITAL LETTER DELTA'",
674
675 # Here are the non-f-string versions,
676 # which should give the same errors.
677 r"'\N'",
678 r"'\N{'",
679 r"'\N{GREEK CAPITAL LETTER DELTA'",
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400680 ])
681
Eric V. Smith451d0e32016-09-09 21:56:20 -0400682 def test_no_backslashes_in_expression_part(self):
683 self.assertAllRaise(SyntaxError, 'f-string expression part cannot include a backslash',
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400684 [r"f'{\'a\'}'",
685 r"f'{\t3}'",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400686 r"f'{\}'",
687 r"rf'{\'a\'}'",
688 r"rf'{\t3}'",
689 r"rf'{\}'",
690 r"""rf'{"\N{LEFT CURLY BRACKET}"}'""",
Jason R. Coombs45cab8c2016-11-06 11:01:08 -0500691 r"f'{\n}'",
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400692 ])
693
Eric V. Smith451d0e32016-09-09 21:56:20 -0400694 def test_no_escapes_for_braces(self):
Jason R. Coombs1c92a762016-11-06 11:25:54 -0500695 """
696 Only literal curly braces begin an expression.
697 """
698 # \x7b is '{'.
699 self.assertEqual(f'\x7b1+1}}', '{1+1}')
700 self.assertEqual(f'\x7b1+1', '{1+1')
701 self.assertEqual(f'\u007b1+1', '{1+1')
702 self.assertEqual(f'\N{LEFT CURLY BRACKET}1+1\N{RIGHT CURLY BRACKET}', '{1+1}')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400703
Eric V. Smith235a6f02015-09-19 14:51:32 -0400704 def test_newlines_in_expressions(self):
705 self.assertEqual(f'{0}', '0')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400706 self.assertEqual(rf'''{3+
7074}''', '7')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400708
709 def test_lambda(self):
710 x = 5
711 self.assertEqual(f'{(lambda y:x*y)("8")!r}', "'88888'")
712 self.assertEqual(f'{(lambda y:x*y)("8")!r:10}', "'88888' ")
713 self.assertEqual(f'{(lambda y:x*y)("8"):10}', "88888 ")
714
715 # lambda doesn't work without parens, because the colon
716 # makes the parser think it's a format_spec
Lysandros Nikolaou9a4b38f2020-04-15 21:22:10 +0300717 self.assertAllRaise(SyntaxError, 'invalid syntax',
Eric V. Smith235a6f02015-09-19 14:51:32 -0400718 ["f'{lambda x:x}'",
719 ])
720
721 def test_yield(self):
722 # Not terribly useful, but make sure the yield turns
723 # a function into a generator
724 def fn(y):
725 f'y:{yield y*2}'
726
727 g = fn(4)
728 self.assertEqual(next(g), 8)
729
730 def test_yield_send(self):
731 def fn(x):
732 yield f'x:{yield (lambda i: x * i)}'
733
734 g = fn(10)
735 the_lambda = next(g)
736 self.assertEqual(the_lambda(4), 40)
737 self.assertEqual(g.send('string'), 'x:string')
738
739 def test_expressions_with_triple_quoted_strings(self):
740 self.assertEqual(f"{'''x'''}", 'x')
741 self.assertEqual(f"{'''eric's'''}", "eric's")
Eric V. Smith235a6f02015-09-19 14:51:32 -0400742
743 # Test concatenation within an expression
744 self.assertEqual(f'{"x" """eric"s""" "y"}', 'xeric"sy')
745 self.assertEqual(f'{"x" """eric"s"""}', 'xeric"s')
746 self.assertEqual(f'{"""eric"s""" "y"}', 'eric"sy')
747 self.assertEqual(f'{"""x""" """eric"s""" "y"}', 'xeric"sy')
748 self.assertEqual(f'{"""x""" """eric"s""" """y"""}', 'xeric"sy')
749 self.assertEqual(f'{r"""x""" """eric"s""" """y"""}', 'xeric"sy')
750
751 def test_multiple_vars(self):
752 x = 98
753 y = 'abc'
754 self.assertEqual(f'{x}{y}', '98abc')
755
756 self.assertEqual(f'X{x}{y}', 'X98abc')
757 self.assertEqual(f'{x}X{y}', '98Xabc')
758 self.assertEqual(f'{x}{y}X', '98abcX')
759
760 self.assertEqual(f'X{x}Y{y}', 'X98Yabc')
761 self.assertEqual(f'X{x}{y}Y', 'X98abcY')
762 self.assertEqual(f'{x}X{y}Y', '98XabcY')
763
764 self.assertEqual(f'X{x}Y{y}Z', 'X98YabcZ')
765
766 def test_closure(self):
767 def outer(x):
768 def inner():
769 return f'x:{x}'
770 return inner
771
772 self.assertEqual(outer('987')(), 'x:987')
773 self.assertEqual(outer(7)(), 'x:7')
774
775 def test_arguments(self):
776 y = 2
777 def f(x, width):
778 return f'x={x*y:{width}}'
779
780 self.assertEqual(f('foo', 10), 'x=foofoo ')
781 x = 'bar'
782 self.assertEqual(f(10, 10), 'x= 20')
783
784 def test_locals(self):
785 value = 123
786 self.assertEqual(f'v:{value}', 'v:123')
787
788 def test_missing_variable(self):
789 with self.assertRaises(NameError):
790 f'v:{value}'
791
792 def test_missing_format_spec(self):
793 class O:
794 def __format__(self, spec):
795 if not spec:
796 return '*'
797 return spec
798
799 self.assertEqual(f'{O():x}', 'x')
800 self.assertEqual(f'{O()}', '*')
801 self.assertEqual(f'{O():}', '*')
802
803 self.assertEqual(f'{3:}', '3')
804 self.assertEqual(f'{3!s:}', '3')
805
806 def test_global(self):
807 self.assertEqual(f'g:{a_global}', 'g:global variable')
808 self.assertEqual(f'g:{a_global!r}', "g:'global variable'")
809
810 a_local = 'local variable'
811 self.assertEqual(f'g:{a_global} l:{a_local}',
812 'g:global variable l:local variable')
813 self.assertEqual(f'g:{a_global!r}',
814 "g:'global variable'")
815 self.assertEqual(f'g:{a_global} l:{a_local!r}',
816 "g:global variable l:'local variable'")
817
818 self.assertIn("module 'unittest' from", f'{unittest}')
819
820 def test_shadowed_global(self):
821 a_global = 'really a local'
822 self.assertEqual(f'g:{a_global}', 'g:really a local')
823 self.assertEqual(f'g:{a_global!r}', "g:'really a local'")
824
825 a_local = 'local variable'
826 self.assertEqual(f'g:{a_global} l:{a_local}',
827 'g:really a local l:local variable')
828 self.assertEqual(f'g:{a_global!r}',
829 "g:'really a local'")
830 self.assertEqual(f'g:{a_global} l:{a_local!r}',
831 "g:really a local l:'local variable'")
832
833 def test_call(self):
834 def foo(x):
835 return 'x=' + str(x)
836
837 self.assertEqual(f'{foo(10)}', 'x=10')
838
839 def test_nested_fstrings(self):
840 y = 5
841 self.assertEqual(f'{f"{0}"*3}', '000')
842 self.assertEqual(f'{f"{y}"*3}', '555')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400843
844 def test_invalid_string_prefixes(self):
Pablo Galindo70c188e2020-04-13 02:47:35 +0100845 single_quote_cases = ["fu''",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400846 "uf''",
847 "Fu''",
848 "fU''",
849 "Uf''",
850 "uF''",
851 "ufr''",
852 "urf''",
853 "fur''",
854 "fru''",
855 "rfu''",
856 "ruf''",
857 "FUR''",
858 "Fur''",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400859 "fb''",
860 "fB''",
861 "Fb''",
862 "FB''",
863 "bf''",
864 "bF''",
865 "Bf''",
Pablo Galindo70c188e2020-04-13 02:47:35 +0100866 "BF''",]
867 double_quote_cases = [case.replace("'", '"') for case in single_quote_cases]
Lysandros Nikolaou6cb0ad22020-05-26 03:10:00 +0300868 error_msg = (
869 'invalid syntax'
870 if use_old_parser()
871 else 'unexpected EOF while parsing'
872 )
873 self.assertAllRaise(SyntaxError, error_msg,
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
Eric V. Smith235a6f02015-09-19 14:51:32 -04001053 def test_loop(self):
1054 for i in range(1000):
1055 self.assertEqual(f'i:{i}', 'i:' + str(i))
1056
1057 def test_dict(self):
1058 d = {'"': 'dquote',
1059 "'": 'squote',
1060 'foo': 'bar',
1061 }
Eric V. Smith235a6f02015-09-19 14:51:32 -04001062 self.assertEqual(f'''{d["'"]}''', 'squote')
1063 self.assertEqual(f"""{d['"']}""", 'dquote')
1064
1065 self.assertEqual(f'{d["foo"]}', 'bar')
1066 self.assertEqual(f"{d['foo']}", 'bar')
Eric V. Smith235a6f02015-09-19 14:51:32 -04001067
ericvsmith11e97f22017-06-16 06:19:32 -04001068 def test_backslash_char(self):
1069 # Check eval of a backslash followed by a control char.
1070 # See bpo-30682: this used to raise an assert in pydebug mode.
1071 self.assertEqual(eval('f"\\\n"'), '')
1072 self.assertEqual(eval('f"\\\r"'), '')
1073
Eric V. Smith9a4135e2019-05-08 16:28:48 -04001074 def test_debug_conversion(self):
1075 x = 'A string'
1076 self.assertEqual(f'{x=}', 'x=' + repr(x))
1077 self.assertEqual(f'{x =}', 'x =' + repr(x))
1078 self.assertEqual(f'{x=!s}', 'x=' + str(x))
1079 self.assertEqual(f'{x=!r}', 'x=' + repr(x))
1080 self.assertEqual(f'{x=!a}', 'x=' + ascii(x))
1081
1082 x = 2.71828
1083 self.assertEqual(f'{x=:.2f}', 'x=' + format(x, '.2f'))
1084 self.assertEqual(f'{x=:}', 'x=' + format(x, ''))
1085 self.assertEqual(f'{x=!r:^20}', 'x=' + format(repr(x), '^20'))
1086 self.assertEqual(f'{x=!s:^20}', 'x=' + format(str(x), '^20'))
1087 self.assertEqual(f'{x=!a:^20}', 'x=' + format(ascii(x), '^20'))
1088
1089 x = 9
1090 self.assertEqual(f'{3*x+15=}', '3*x+15=42')
1091
1092 # There is code in ast.c that deals with non-ascii expression values. So,
1093 # use a unicode identifier to trigger that.
1094 tenπ = 31.4
1095 self.assertEqual(f'{tenπ=:.2f}', 'tenπ=31.40')
1096
1097 # Also test with Unicode in non-identifiers.
1098 self.assertEqual(f'{"Σ"=}', '"Σ"=\'Σ\'')
1099
1100 # Make sure nested fstrings still work.
1101 self.assertEqual(f'{f"{3.1415=:.1f}":*^20}', '*****3.1415=3.1*****')
1102
1103 # Make sure text before and after an expression with = works
1104 # correctly.
1105 pi = 'π'
1106 self.assertEqual(f'alpha α {pi=} ω omega', "alpha α pi='π' ω omega")
1107
1108 # Check multi-line expressions.
1109 self.assertEqual(f'''{
11103
1111=}''', '\n3\n=3')
1112
1113 # Since = is handled specially, make sure all existing uses of
1114 # it still work.
1115
1116 self.assertEqual(f'{0==1}', 'False')
1117 self.assertEqual(f'{0!=1}', 'True')
1118 self.assertEqual(f'{0<=1}', 'True')
1119 self.assertEqual(f'{0>=1}', 'False')
1120 self.assertEqual(f'{(x:="5")}', '5')
1121 self.assertEqual(x, '5')
1122 self.assertEqual(f'{(x:=5)}', '5')
1123 self.assertEqual(x, 5)
1124 self.assertEqual(f'{"="}', '=')
1125
1126 x = 20
1127 # This isn't an assignment expression, it's 'x', with a format
1128 # spec of '=10'. See test_walrus: you need to use parens.
1129 self.assertEqual(f'{x:=10}', ' 20')
1130
1131 # Test named function parameters, to make sure '=' parsing works
1132 # there.
1133 def f(a):
1134 nonlocal x
1135 oldx = x
1136 x = a
1137 return oldx
1138 x = 0
1139 self.assertEqual(f'{f(a="3=")}', '0')
1140 self.assertEqual(x, '3=')
1141 self.assertEqual(f'{f(a=4)}', '3=')
1142 self.assertEqual(x, 4)
1143
1144 # Make sure __format__ is being called.
1145 class C:
1146 def __format__(self, s):
1147 return f'FORMAT-{s}'
1148 def __repr__(self):
1149 return 'REPR'
1150
1151 self.assertEqual(f'{C()=}', 'C()=REPR')
1152 self.assertEqual(f'{C()=!r}', 'C()=REPR')
1153 self.assertEqual(f'{C()=:}', 'C()=FORMAT-')
1154 self.assertEqual(f'{C()=: }', 'C()=FORMAT- ')
1155 self.assertEqual(f'{C()=:x}', 'C()=FORMAT-x')
1156 self.assertEqual(f'{C()=!r:*^20}', 'C()=********REPR********')
1157
Pablo Galindo26f55c22019-05-12 01:43:04 +01001158 self.assertRaises(SyntaxError, eval, "f'{C=]'")
1159
Eric V. Smith6f6ff8a2019-05-27 15:31:52 -04001160 # Make sure leading and following text works.
1161 x = 'foo'
1162 self.assertEqual(f'X{x=}Y', 'Xx='+repr(x)+'Y')
1163
1164 # Make sure whitespace around the = works.
1165 self.assertEqual(f'X{x =}Y', 'Xx ='+repr(x)+'Y')
1166 self.assertEqual(f'X{x= }Y', 'Xx= '+repr(x)+'Y')
1167 self.assertEqual(f'X{x = }Y', 'Xx = '+repr(x)+'Y')
1168
1169 # These next lines contains tabs. Backslash escapes don't
1170 # work in f-strings.
Min ho Kim96e12d52019-07-22 06:12:33 +10001171 # patchcheck doesn't like these tabs. So the only way to test
Eric V. Smith6f6ff8a2019-05-27 15:31:52 -04001172 # this will be to dynamically created and exec the f-strings. But
1173 # that's such a hassle I'll save it for another day. For now, convert
1174 # the tabs to spaces just to shut up patchcheck.
1175 #self.assertEqual(f'X{x =}Y', 'Xx\t='+repr(x)+'Y')
1176 #self.assertEqual(f'X{x = }Y', 'Xx\t=\t'+repr(x)+'Y')
1177
Eric V. Smith9a4135e2019-05-08 16:28:48 -04001178 def test_walrus(self):
1179 x = 20
1180 # This isn't an assignment expression, it's 'x', with a format
1181 # spec of '=10'.
1182 self.assertEqual(f'{x:=10}', ' 20')
1183
1184 # This is an assignment expression, which requires parens.
1185 self.assertEqual(f'{(x:=10)}', '10')
1186 self.assertEqual(x, 10)
1187
Łukasz Langae7c566c2017-09-06 17:27:58 -07001188
Eric V. Smith235a6f02015-09-19 14:51:32 -04001189if __name__ == '__main__':
1190 unittest.main()