blob: e0bb5b56b2614f44992daf5748e7f85ec990623d [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
14
15a_global = 'global variable'
16
17# You could argue that I'm too strict in looking for specific error
18# values with assertRaisesRegex, but without it it's way too easy to
19# make a syntax error in the test strings. Especially with all of the
20# triple quotes, raw strings, backslashes, etc. I think it's a
21# worthwhile tradeoff. When I switched to this method, I found many
22# examples where I wasn't testing what I thought I was.
23
24class TestCase(unittest.TestCase):
25 def assertAllRaise(self, exception_type, regex, error_strings):
26 for str in error_strings:
27 with self.subTest(str=str):
28 with self.assertRaisesRegex(exception_type, regex):
29 eval(str)
30
31 def test__format__lookup(self):
32 # Make sure __format__ is looked up on the type, not the instance.
33 class X:
34 def __format__(self, spec):
35 return 'class'
36
37 x = X()
38
39 # Add a bound __format__ method to the 'y' instance, but not
40 # the 'x' instance.
41 y = X()
42 y.__format__ = types.MethodType(lambda self, spec: 'instance', y)
43
44 self.assertEqual(f'{y}', format(y))
45 self.assertEqual(f'{y}', 'class')
46 self.assertEqual(format(x), format(y))
47
48 # __format__ is not called this way, but still make sure it
49 # returns what we expect (so we can make sure we're bypassing
50 # it).
51 self.assertEqual(x.__format__(''), 'class')
52 self.assertEqual(y.__format__(''), 'instance')
53
54 # This is how __format__ is actually called.
55 self.assertEqual(type(x).__format__(x, ''), 'class')
56 self.assertEqual(type(y).__format__(y, ''), 'class')
57
58 def test_ast(self):
59 # Inspired by http://bugs.python.org/issue24975
60 class X:
61 def __init__(self):
62 self.called = False
63 def __call__(self):
64 self.called = True
65 return 4
66 x = X()
67 expr = """
68a = 10
69f'{a * x()}'"""
70 t = ast.parse(expr)
71 c = compile(t, '', 'exec')
72
73 # Make sure x was not called.
74 self.assertFalse(x.called)
75
76 # Actually run the code.
77 exec(c)
78
79 # Make sure x was called.
80 self.assertTrue(x.called)
81
Łukasz Langae7c566c2017-09-06 17:27:58 -070082 def test_ast_line_numbers(self):
83 expr = """
84a = 10
85f'{a * x()}'"""
86 t = ast.parse(expr)
87 self.assertEqual(type(t), ast.Module)
88 self.assertEqual(len(t.body), 2)
89 # check `a = 10`
90 self.assertEqual(type(t.body[0]), ast.Assign)
91 self.assertEqual(t.body[0].lineno, 2)
92 # check `f'...'`
93 self.assertEqual(type(t.body[1]), ast.Expr)
94 self.assertEqual(type(t.body[1].value), ast.JoinedStr)
95 self.assertEqual(len(t.body[1].value.values), 1)
96 self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)
97 self.assertEqual(t.body[1].lineno, 3)
98 self.assertEqual(t.body[1].value.lineno, 3)
99 self.assertEqual(t.body[1].value.values[0].lineno, 3)
100 # check the binop location
101 binop = t.body[1].value.values[0].value
102 self.assertEqual(type(binop), ast.BinOp)
103 self.assertEqual(type(binop.left), ast.Name)
104 self.assertEqual(type(binop.op), ast.Mult)
105 self.assertEqual(type(binop.right), ast.Call)
106 self.assertEqual(binop.lineno, 3)
107 self.assertEqual(binop.left.lineno, 3)
108 self.assertEqual(binop.right.lineno, 3)
109 self.assertEqual(binop.col_offset, 3)
110 self.assertEqual(binop.left.col_offset, 3)
111 self.assertEqual(binop.right.col_offset, 7)
112
113 def test_ast_line_numbers_multiple_formattedvalues(self):
114 expr = """
115f'no formatted values'
116f'eggs {a * x()} spam {b + y()}'"""
117 t = ast.parse(expr)
118 self.assertEqual(type(t), ast.Module)
119 self.assertEqual(len(t.body), 2)
120 # check `f'no formatted value'`
121 self.assertEqual(type(t.body[0]), ast.Expr)
122 self.assertEqual(type(t.body[0].value), ast.JoinedStr)
123 self.assertEqual(t.body[0].lineno, 2)
124 # check `f'...'`
125 self.assertEqual(type(t.body[1]), ast.Expr)
126 self.assertEqual(type(t.body[1].value), ast.JoinedStr)
127 self.assertEqual(len(t.body[1].value.values), 4)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300128 self.assertEqual(type(t.body[1].value.values[0]), ast.Constant)
129 self.assertEqual(type(t.body[1].value.values[0].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700130 self.assertEqual(type(t.body[1].value.values[1]), ast.FormattedValue)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300131 self.assertEqual(type(t.body[1].value.values[2]), ast.Constant)
132 self.assertEqual(type(t.body[1].value.values[2].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700133 self.assertEqual(type(t.body[1].value.values[3]), ast.FormattedValue)
134 self.assertEqual(t.body[1].lineno, 3)
135 self.assertEqual(t.body[1].value.lineno, 3)
136 self.assertEqual(t.body[1].value.values[0].lineno, 3)
137 self.assertEqual(t.body[1].value.values[1].lineno, 3)
138 self.assertEqual(t.body[1].value.values[2].lineno, 3)
139 self.assertEqual(t.body[1].value.values[3].lineno, 3)
140 # check the first binop location
141 binop1 = t.body[1].value.values[1].value
142 self.assertEqual(type(binop1), ast.BinOp)
143 self.assertEqual(type(binop1.left), ast.Name)
144 self.assertEqual(type(binop1.op), ast.Mult)
145 self.assertEqual(type(binop1.right), ast.Call)
146 self.assertEqual(binop1.lineno, 3)
147 self.assertEqual(binop1.left.lineno, 3)
148 self.assertEqual(binop1.right.lineno, 3)
149 self.assertEqual(binop1.col_offset, 8)
150 self.assertEqual(binop1.left.col_offset, 8)
151 self.assertEqual(binop1.right.col_offset, 12)
152 # check the second binop location
153 binop2 = t.body[1].value.values[3].value
154 self.assertEqual(type(binop2), ast.BinOp)
155 self.assertEqual(type(binop2.left), ast.Name)
156 self.assertEqual(type(binop2.op), ast.Add)
157 self.assertEqual(type(binop2.right), ast.Call)
158 self.assertEqual(binop2.lineno, 3)
159 self.assertEqual(binop2.left.lineno, 3)
160 self.assertEqual(binop2.right.lineno, 3)
161 self.assertEqual(binop2.col_offset, 23)
162 self.assertEqual(binop2.left.col_offset, 23)
163 self.assertEqual(binop2.right.col_offset, 27)
164
165 def test_ast_line_numbers_nested(self):
166 expr = """
167a = 10
168f'{a * f"-{x()}-"}'"""
169 t = ast.parse(expr)
170 self.assertEqual(type(t), ast.Module)
171 self.assertEqual(len(t.body), 2)
172 # check `a = 10`
173 self.assertEqual(type(t.body[0]), ast.Assign)
174 self.assertEqual(t.body[0].lineno, 2)
175 # check `f'...'`
176 self.assertEqual(type(t.body[1]), ast.Expr)
177 self.assertEqual(type(t.body[1].value), ast.JoinedStr)
178 self.assertEqual(len(t.body[1].value.values), 1)
179 self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)
180 self.assertEqual(t.body[1].lineno, 3)
181 self.assertEqual(t.body[1].value.lineno, 3)
182 self.assertEqual(t.body[1].value.values[0].lineno, 3)
183 # check the binop location
184 binop = t.body[1].value.values[0].value
185 self.assertEqual(type(binop), ast.BinOp)
186 self.assertEqual(type(binop.left), ast.Name)
187 self.assertEqual(type(binop.op), ast.Mult)
188 self.assertEqual(type(binop.right), ast.JoinedStr)
189 self.assertEqual(binop.lineno, 3)
190 self.assertEqual(binop.left.lineno, 3)
191 self.assertEqual(binop.right.lineno, 3)
192 self.assertEqual(binop.col_offset, 3)
193 self.assertEqual(binop.left.col_offset, 3)
194 self.assertEqual(binop.right.col_offset, 7)
195 # check the nested call location
196 self.assertEqual(len(binop.right.values), 3)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300197 self.assertEqual(type(binop.right.values[0]), ast.Constant)
198 self.assertEqual(type(binop.right.values[0].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700199 self.assertEqual(type(binop.right.values[1]), ast.FormattedValue)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300200 self.assertEqual(type(binop.right.values[2]), ast.Constant)
201 self.assertEqual(type(binop.right.values[2].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700202 self.assertEqual(binop.right.values[0].lineno, 3)
203 self.assertEqual(binop.right.values[1].lineno, 3)
204 self.assertEqual(binop.right.values[2].lineno, 3)
205 call = binop.right.values[1].value
206 self.assertEqual(type(call), ast.Call)
207 self.assertEqual(call.lineno, 3)
Lysandros Nikolaou37af21b2020-04-29 03:43:50 +0300208 self.assertEqual(call.col_offset, 11)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700209
210 def test_ast_line_numbers_duplicate_expression(self):
211 """Duplicate expression
212
213 NOTE: this is currently broken, always sets location of the first
214 expression.
215 """
216 expr = """
217a = 10
218f'{a * x()} {a * x()} {a * x()}'
219"""
220 t = ast.parse(expr)
221 self.assertEqual(type(t), ast.Module)
222 self.assertEqual(len(t.body), 2)
223 # check `a = 10`
224 self.assertEqual(type(t.body[0]), ast.Assign)
225 self.assertEqual(t.body[0].lineno, 2)
226 # check `f'...'`
227 self.assertEqual(type(t.body[1]), ast.Expr)
228 self.assertEqual(type(t.body[1].value), ast.JoinedStr)
229 self.assertEqual(len(t.body[1].value.values), 5)
230 self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300231 self.assertEqual(type(t.body[1].value.values[1]), ast.Constant)
232 self.assertEqual(type(t.body[1].value.values[1].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700233 self.assertEqual(type(t.body[1].value.values[2]), ast.FormattedValue)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300234 self.assertEqual(type(t.body[1].value.values[3]), ast.Constant)
235 self.assertEqual(type(t.body[1].value.values[3].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700236 self.assertEqual(type(t.body[1].value.values[4]), ast.FormattedValue)
237 self.assertEqual(t.body[1].lineno, 3)
238 self.assertEqual(t.body[1].value.lineno, 3)
239 self.assertEqual(t.body[1].value.values[0].lineno, 3)
240 self.assertEqual(t.body[1].value.values[1].lineno, 3)
241 self.assertEqual(t.body[1].value.values[2].lineno, 3)
242 self.assertEqual(t.body[1].value.values[3].lineno, 3)
243 self.assertEqual(t.body[1].value.values[4].lineno, 3)
244 # check the first binop location
245 binop = t.body[1].value.values[0].value
246 self.assertEqual(type(binop), ast.BinOp)
247 self.assertEqual(type(binop.left), ast.Name)
248 self.assertEqual(type(binop.op), ast.Mult)
249 self.assertEqual(type(binop.right), ast.Call)
250 self.assertEqual(binop.lineno, 3)
251 self.assertEqual(binop.left.lineno, 3)
252 self.assertEqual(binop.right.lineno, 3)
253 self.assertEqual(binop.col_offset, 3)
254 self.assertEqual(binop.left.col_offset, 3)
255 self.assertEqual(binop.right.col_offset, 7)
256 # check the second binop location
257 binop = t.body[1].value.values[2].value
258 self.assertEqual(type(binop), ast.BinOp)
259 self.assertEqual(type(binop.left), ast.Name)
260 self.assertEqual(type(binop.op), ast.Mult)
261 self.assertEqual(type(binop.right), ast.Call)
262 self.assertEqual(binop.lineno, 3)
263 self.assertEqual(binop.left.lineno, 3)
264 self.assertEqual(binop.right.lineno, 3)
265 self.assertEqual(binop.col_offset, 3) # FIXME: this is wrong
266 self.assertEqual(binop.left.col_offset, 3) # FIXME: this is wrong
267 self.assertEqual(binop.right.col_offset, 7) # FIXME: this is wrong
268 # check the third binop location
269 binop = t.body[1].value.values[4].value
270 self.assertEqual(type(binop), ast.BinOp)
271 self.assertEqual(type(binop.left), ast.Name)
272 self.assertEqual(type(binop.op), ast.Mult)
273 self.assertEqual(type(binop.right), ast.Call)
274 self.assertEqual(binop.lineno, 3)
275 self.assertEqual(binop.left.lineno, 3)
276 self.assertEqual(binop.right.lineno, 3)
277 self.assertEqual(binop.col_offset, 3) # FIXME: this is wrong
278 self.assertEqual(binop.left.col_offset, 3) # FIXME: this is wrong
279 self.assertEqual(binop.right.col_offset, 7) # FIXME: this is wrong
280
281 def test_ast_line_numbers_multiline_fstring(self):
Anthony Sottile995d9b92019-01-12 20:05:13 -0800282 # See bpo-30465 for details.
Łukasz Langae7c566c2017-09-06 17:27:58 -0700283 expr = """
284a = 10
285f'''
286 {a
287 *
288 x()}
289non-important content
290'''
291"""
292 t = ast.parse(expr)
293 self.assertEqual(type(t), ast.Module)
294 self.assertEqual(len(t.body), 2)
295 # check `a = 10`
296 self.assertEqual(type(t.body[0]), ast.Assign)
297 self.assertEqual(t.body[0].lineno, 2)
298 # check `f'...'`
299 self.assertEqual(type(t.body[1]), ast.Expr)
300 self.assertEqual(type(t.body[1].value), ast.JoinedStr)
301 self.assertEqual(len(t.body[1].value.values), 3)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300302 self.assertEqual(type(t.body[1].value.values[0]), ast.Constant)
303 self.assertEqual(type(t.body[1].value.values[0].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700304 self.assertEqual(type(t.body[1].value.values[1]), ast.FormattedValue)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300305 self.assertEqual(type(t.body[1].value.values[2]), ast.Constant)
306 self.assertEqual(type(t.body[1].value.values[2].value), str)
Anthony Sottile995d9b92019-01-12 20:05:13 -0800307 self.assertEqual(t.body[1].lineno, 3)
308 self.assertEqual(t.body[1].value.lineno, 3)
309 self.assertEqual(t.body[1].value.values[0].lineno, 3)
310 self.assertEqual(t.body[1].value.values[1].lineno, 3)
311 self.assertEqual(t.body[1].value.values[2].lineno, 3)
312 self.assertEqual(t.body[1].col_offset, 0)
313 self.assertEqual(t.body[1].value.col_offset, 0)
314 self.assertEqual(t.body[1].value.values[0].col_offset, 0)
315 self.assertEqual(t.body[1].value.values[1].col_offset, 0)
316 self.assertEqual(t.body[1].value.values[2].col_offset, 0)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700317 # NOTE: the following lineno information and col_offset is correct for
318 # expressions within FormattedValues.
319 binop = t.body[1].value.values[1].value
320 self.assertEqual(type(binop), ast.BinOp)
321 self.assertEqual(type(binop.left), ast.Name)
322 self.assertEqual(type(binop.op), ast.Mult)
323 self.assertEqual(type(binop.right), ast.Call)
324 self.assertEqual(binop.lineno, 4)
325 self.assertEqual(binop.left.lineno, 4)
326 self.assertEqual(binop.right.lineno, 6)
Anthony Sottile995d9b92019-01-12 20:05:13 -0800327 self.assertEqual(binop.col_offset, 4)
328 self.assertEqual(binop.left.col_offset, 4)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700329 self.assertEqual(binop.right.col_offset, 7)
330
Serhiy Storchaka4cc30ae2016-12-11 19:37:19 +0200331 def test_docstring(self):
332 def f():
333 f'''Not a docstring'''
334 self.assertIsNone(f.__doc__)
335 def g():
336 '''Not a docstring''' \
337 f''
338 self.assertIsNone(g.__doc__)
339
Eric V. Smith235a6f02015-09-19 14:51:32 -0400340 def test_literal_eval(self):
Eric V. Smith235a6f02015-09-19 14:51:32 -0400341 with self.assertRaisesRegex(ValueError, 'malformed node or string'):
Serhiy Storchaka4cc30ae2016-12-11 19:37:19 +0200342 ast.literal_eval("f'x'")
Eric V. Smith235a6f02015-09-19 14:51:32 -0400343
344 def test_ast_compile_time_concat(self):
345 x = ['']
346
347 expr = """x[0] = 'foo' f'{3}'"""
348 t = ast.parse(expr)
349 c = compile(t, '', 'exec')
350 exec(c)
351 self.assertEqual(x[0], 'foo3')
352
Eric V. Smith9b88fdf2016-11-07 17:54:01 -0500353 def test_compile_time_concat_errors(self):
354 self.assertAllRaise(SyntaxError,
355 'cannot mix bytes and nonbytes literals',
356 [r"""f'' b''""",
357 r"""b'' f''""",
358 ])
359
Eric V. Smith235a6f02015-09-19 14:51:32 -0400360 def test_literal(self):
361 self.assertEqual(f'', '')
362 self.assertEqual(f'a', 'a')
363 self.assertEqual(f' ', ' ')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400364
365 def test_unterminated_string(self):
366 self.assertAllRaise(SyntaxError, 'f-string: unterminated string',
367 [r"""f'{"x'""",
368 r"""f'{"x}'""",
369 r"""f'{("x'""",
370 r"""f'{("x}'""",
371 ])
372
373 def test_mismatched_parens(self):
Serhiy Storchaka58159ef2019-01-12 09:46:50 +0200374 self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\}' "
375 r"does not match opening parenthesis '\('",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400376 ["f'{((}'",
377 ])
Serhiy Storchaka58159ef2019-01-12 09:46:50 +0200378 self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\)' "
379 r"does not match opening parenthesis '\['",
380 ["f'{a[4)}'",
381 ])
382 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.assertRaises(SyntaxError, eval, "f'{" + "("*500 + "}'")
Eric V. Smith235a6f02015-09-19 14:51:32 -0400395
396 def test_double_braces(self):
397 self.assertEqual(f'{{', '{')
398 self.assertEqual(f'a{{', 'a{')
399 self.assertEqual(f'{{b', '{b')
400 self.assertEqual(f'a{{b', 'a{b')
401 self.assertEqual(f'}}', '}')
402 self.assertEqual(f'a}}', 'a}')
403 self.assertEqual(f'}}b', '}b')
404 self.assertEqual(f'a}}b', 'a}b')
Eric V. Smith451d0e32016-09-09 21:56:20 -0400405 self.assertEqual(f'{{}}', '{}')
406 self.assertEqual(f'a{{}}', 'a{}')
407 self.assertEqual(f'{{b}}', '{b}')
408 self.assertEqual(f'{{}}c', '{}c')
409 self.assertEqual(f'a{{b}}', 'a{b}')
410 self.assertEqual(f'a{{}}c', 'a{}c')
411 self.assertEqual(f'{{b}}c', '{b}c')
412 self.assertEqual(f'a{{b}}c', 'a{b}c')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400413
414 self.assertEqual(f'{{{10}', '{10')
415 self.assertEqual(f'}}{10}', '}10')
416 self.assertEqual(f'}}{{{10}', '}{10')
417 self.assertEqual(f'}}a{{{10}', '}a{10')
418
419 self.assertEqual(f'{10}{{', '10{')
420 self.assertEqual(f'{10}}}', '10}')
421 self.assertEqual(f'{10}}}{{', '10}{')
422 self.assertEqual(f'{10}}}a{{' '}', '10}a{}')
423
424 # Inside of strings, don't interpret doubled brackets.
425 self.assertEqual(f'{"{{}}"}', '{{}}')
426
427 self.assertAllRaise(TypeError, 'unhashable type',
428 ["f'{ {{}} }'", # dict in a set
429 ])
430
431 def test_compile_time_concat(self):
432 x = 'def'
433 self.assertEqual('abc' f'## {x}ghi', 'abc## defghi')
434 self.assertEqual('abc' f'{x}' 'ghi', 'abcdefghi')
435 self.assertEqual('abc' f'{x}' 'gh' f'i{x:4}', 'abcdefghidef ')
436 self.assertEqual('{x}' f'{x}', '{x}def')
437 self.assertEqual('{x' f'{x}', '{xdef')
438 self.assertEqual('{x}' f'{x}', '{x}def')
439 self.assertEqual('{{x}}' f'{x}', '{{x}}def')
440 self.assertEqual('{{x' f'{x}', '{{xdef')
441 self.assertEqual('x}}' f'{x}', 'x}}def')
442 self.assertEqual(f'{x}' 'x}}', 'defx}}')
443 self.assertEqual(f'{x}' '', 'def')
444 self.assertEqual('' f'{x}' '', 'def')
445 self.assertEqual('' f'{x}', 'def')
446 self.assertEqual(f'{x}' '2', 'def2')
447 self.assertEqual('1' f'{x}' '2', '1def2')
448 self.assertEqual('1' f'{x}', '1def')
449 self.assertEqual(f'{x}' f'-{x}', 'def-def')
450 self.assertEqual('' f'', '')
451 self.assertEqual('' f'' '', '')
452 self.assertEqual('' f'' '' f'', '')
453 self.assertEqual(f'', '')
454 self.assertEqual(f'' '', '')
455 self.assertEqual(f'' '' f'', '')
456 self.assertEqual(f'' '' f'' '', '')
457
458 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
459 ["f'{3' f'}'", # can't concat to get a valid f-string
460 ])
461
462 def test_comments(self):
463 # These aren't comments, since they're in strings.
464 d = {'#': 'hash'}
465 self.assertEqual(f'{"#"}', '#')
466 self.assertEqual(f'{d["#"]}', 'hash')
467
Eric V. Smith09835dc2016-09-11 18:58:20 -0400468 self.assertAllRaise(SyntaxError, "f-string expression part cannot include '#'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400469 ["f'{1#}'", # error because the expression becomes "(1#)"
470 "f'{3(#)}'",
Eric V. Smith09835dc2016-09-11 18:58:20 -0400471 "f'{#}'",
Serhiy Storchaka58159ef2019-01-12 09:46:50 +0200472 ])
473 self.assertAllRaise(SyntaxError, r"f-string: unmatched '\)'",
474 ["f'{)#}'", # When wrapped in parens, this becomes
Eric V. Smith35a24c52016-09-11 19:01:22 -0400475 # '()#)'. Make sure that doesn't compile.
Eric V. Smith235a6f02015-09-19 14:51:32 -0400476 ])
477
478 def test_many_expressions(self):
479 # Create a string with many expressions in it. Note that
480 # because we have a space in here as a literal, we're actually
481 # going to use twice as many ast nodes: one for each literal
482 # plus one for each expression.
483 def build_fstr(n, extra=''):
484 return "f'" + ('{x} ' * n) + extra + "'"
485
486 x = 'X'
487 width = 1
488
489 # Test around 256.
490 for i in range(250, 260):
491 self.assertEqual(eval(build_fstr(i)), (x+' ')*i)
492
493 # Test concatenating 2 largs fstrings.
494 self.assertEqual(eval(build_fstr(255)*256), (x+' ')*(255*256))
495
496 s = build_fstr(253, '{x:{width}} ')
497 self.assertEqual(eval(s), (x+' ')*254)
498
499 # Test lots of expressions and constants, concatenated.
500 s = "f'{1}' 'x' 'y'" * 1024
501 self.assertEqual(eval(s), '1xy' * 1024)
502
503 def test_format_specifier_expressions(self):
504 width = 10
505 precision = 4
506 value = decimal.Decimal('12.34567')
507 self.assertEqual(f'result: {value:{width}.{precision}}', 'result: 12.35')
508 self.assertEqual(f'result: {value:{width!r}.{precision}}', 'result: 12.35')
509 self.assertEqual(f'result: {value:{width:0}.{precision:1}}', 'result: 12.35')
510 self.assertEqual(f'result: {value:{1}{0:0}.{precision:1}}', 'result: 12.35')
511 self.assertEqual(f'result: {value:{ 1}{ 0:0}.{ precision:1}}', 'result: 12.35')
512 self.assertEqual(f'{10:#{1}0x}', ' 0xa')
513 self.assertEqual(f'{10:{"#"}1{0}{"x"}}', ' 0xa')
514 self.assertEqual(f'{-10:-{"#"}1{0}x}', ' -0xa')
515 self.assertEqual(f'{-10:{"-"}#{1}0{"x"}}', ' -0xa')
516 self.assertEqual(f'{10:#{3 != {4:5} and width}x}', ' 0xa')
517
518 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
519 ["""f'{"s"!r{":10"}}'""",
520
521 # This looks like a nested format spec.
522 ])
523
524 self.assertAllRaise(SyntaxError, "invalid syntax",
Martin Panter263893c2016-07-28 01:25:31 +0000525 [# Invalid syntax inside a nested spec.
Eric V. Smith235a6f02015-09-19 14:51:32 -0400526 "f'{4:{/5}}'",
527 ])
528
529 self.assertAllRaise(SyntaxError, "f-string: expressions nested too deeply",
530 [# Can't nest format specifiers.
531 "f'result: {value:{width:{0}}.{precision:1}}'",
532 ])
533
534 self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character',
535 [# No expansion inside conversion or for
536 # the : or ! itself.
537 """f'{"s"!{"r"}}'""",
538 ])
539
540 def test_side_effect_order(self):
541 class X:
542 def __init__(self):
543 self.i = 0
544 def __format__(self, spec):
545 self.i += 1
546 return str(self.i)
547
548 x = X()
549 self.assertEqual(f'{x} {x}', '1 2')
550
551 def test_missing_expression(self):
552 self.assertAllRaise(SyntaxError, 'f-string: empty expression not allowed',
553 ["f'{}'",
554 "f'{ }'"
555 "f' {} '",
556 "f'{!r}'",
557 "f'{ !r}'",
558 "f'{10:{ }}'",
559 "f' { } '",
Eric V. Smith1d44c412015-09-23 07:49:00 -0400560
Serhiy Storchaka2e9cd582017-06-08 23:43:54 +0300561 # The Python parser ignores also the following
562 # whitespace characters in additional to a space.
563 "f'''{\t\f\r\n}'''",
564
Eric V. Smith1d44c412015-09-23 07:49:00 -0400565 # Catch the empty expression before the
566 # invalid conversion.
567 "f'{!x}'",
568 "f'{ !xr}'",
569 "f'{!x:}'",
570 "f'{!x:a}'",
571 "f'{ !xr:}'",
572 "f'{ !xr:a}'",
Eric V. Smith548c4d32015-09-23 08:00:01 -0400573
574 "f'{!}'",
575 "f'{:}'",
Eric V. Smithb2080f62015-09-23 10:24:43 -0400576
577 # We find the empty expression before the
578 # missing closing brace.
579 "f'{!'",
580 "f'{!s:'",
581 "f'{:'",
582 "f'{:x'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400583 ])
584
Serhiy Storchaka2e9cd582017-06-08 23:43:54 +0300585 # Different error message is raised for other whitespace characters.
Serhiy Storchaka74ea6b52020-05-12 12:42:04 +0300586 self.assertAllRaise(SyntaxError, r"invalid non-printable character U\+00A0",
Serhiy Storchaka2e9cd582017-06-08 23:43:54 +0300587 ["f'''{\xa0}'''",
588 "\xa0",
589 ])
590
Eric V. Smith235a6f02015-09-19 14:51:32 -0400591 def test_parens_in_expressions(self):
592 self.assertEqual(f'{3,}', '(3,)')
593
594 # Add these because when an expression is evaluated, parens
595 # are added around it. But we shouldn't go from an invalid
596 # expression to a valid one. The added parens are just
597 # supposed to allow whitespace (including newlines).
598 self.assertAllRaise(SyntaxError, 'invalid syntax',
599 ["f'{,}'",
600 "f'{,}'", # this is (,), which is an error
601 ])
602
Serhiy Storchaka58159ef2019-01-12 09:46:50 +0200603 self.assertAllRaise(SyntaxError, r"f-string: unmatched '\)'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400604 ["f'{3)+(4}'",
605 ])
606
607 self.assertAllRaise(SyntaxError, 'EOL while scanning string literal',
608 ["f'{\n}'",
609 ])
610
Eric V. Smith451d0e32016-09-09 21:56:20 -0400611 def test_backslashes_in_string_part(self):
612 self.assertEqual(f'\t', '\t')
613 self.assertEqual(r'\t', '\\t')
614 self.assertEqual(rf'\t', '\\t')
615 self.assertEqual(f'{2}\t', '2\t')
616 self.assertEqual(f'{2}\t{3}', '2\t3')
617 self.assertEqual(f'\t{3}', '\t3')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400618
Eric V. Smith451d0e32016-09-09 21:56:20 -0400619 self.assertEqual(f'\u0394', '\u0394')
620 self.assertEqual(r'\u0394', '\\u0394')
621 self.assertEqual(rf'\u0394', '\\u0394')
622 self.assertEqual(f'{2}\u0394', '2\u0394')
623 self.assertEqual(f'{2}\u0394{3}', '2\u03943')
624 self.assertEqual(f'\u0394{3}', '\u03943')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400625
Eric V. Smith451d0e32016-09-09 21:56:20 -0400626 self.assertEqual(f'\U00000394', '\u0394')
627 self.assertEqual(r'\U00000394', '\\U00000394')
628 self.assertEqual(rf'\U00000394', '\\U00000394')
629 self.assertEqual(f'{2}\U00000394', '2\u0394')
630 self.assertEqual(f'{2}\U00000394{3}', '2\u03943')
631 self.assertEqual(f'\U00000394{3}', '\u03943')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400632
Eric V. Smith451d0e32016-09-09 21:56:20 -0400633 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}', '\u0394')
634 self.assertEqual(f'{2}\N{GREEK CAPITAL LETTER DELTA}', '2\u0394')
635 self.assertEqual(f'{2}\N{GREEK CAPITAL LETTER DELTA}{3}', '2\u03943')
636 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}{3}', '\u03943')
637 self.assertEqual(f'2\N{GREEK CAPITAL LETTER DELTA}', '2\u0394')
638 self.assertEqual(f'2\N{GREEK CAPITAL LETTER DELTA}3', '2\u03943')
639 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}3', '\u03943')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400640
Eric V. Smith451d0e32016-09-09 21:56:20 -0400641 self.assertEqual(f'\x20', ' ')
642 self.assertEqual(r'\x20', '\\x20')
643 self.assertEqual(rf'\x20', '\\x20')
644 self.assertEqual(f'{2}\x20', '2 ')
645 self.assertEqual(f'{2}\x20{3}', '2 3')
646 self.assertEqual(f'\x20{3}', ' 3')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400647
Eric V. Smith451d0e32016-09-09 21:56:20 -0400648 self.assertEqual(f'2\x20', '2 ')
649 self.assertEqual(f'2\x203', '2 3')
650 self.assertEqual(f'\x203', ' 3')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400651
Gregory P. Smithb4be87a2019-08-10 00:19:07 -0700652 with self.assertWarns(DeprecationWarning): # invalid escape sequence
Serhiy Storchaka0cd7a3f2017-05-25 13:33:55 +0300653 value = eval(r"f'\{6*7}'")
654 self.assertEqual(value, '\\42')
655 self.assertEqual(f'\\{6*7}', '\\42')
656 self.assertEqual(fr'\{6*7}', '\\42')
657
658 AMPERSAND = 'spam'
659 # Get the right unicode character (&), or pick up local variable
660 # depending on the number of backslashes.
661 self.assertEqual(f'\N{AMPERSAND}', '&')
662 self.assertEqual(f'\\N{AMPERSAND}', '\\Nspam')
663 self.assertEqual(fr'\N{AMPERSAND}', '\\Nspam')
664 self.assertEqual(f'\\\N{AMPERSAND}', '\\&')
665
Eric V. Smith451d0e32016-09-09 21:56:20 -0400666 def test_misformed_unicode_character_name(self):
667 # These test are needed because unicode names are parsed
668 # differently inside f-strings.
669 self.assertAllRaise(SyntaxError, r"\(unicode error\) 'unicodeescape' codec can't decode bytes in position .*: malformed \\N character escape",
670 [r"f'\N'",
671 r"f'\N{'",
672 r"f'\N{GREEK CAPITAL LETTER DELTA'",
673
674 # Here are the non-f-string versions,
675 # which should give the same errors.
676 r"'\N'",
677 r"'\N{'",
678 r"'\N{GREEK CAPITAL LETTER DELTA'",
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400679 ])
680
Eric V. Smith451d0e32016-09-09 21:56:20 -0400681 def test_no_backslashes_in_expression_part(self):
682 self.assertAllRaise(SyntaxError, 'f-string expression part cannot include a backslash',
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400683 [r"f'{\'a\'}'",
684 r"f'{\t3}'",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400685 r"f'{\}'",
686 r"rf'{\'a\'}'",
687 r"rf'{\t3}'",
688 r"rf'{\}'",
689 r"""rf'{"\N{LEFT CURLY BRACKET}"}'""",
Jason R. Coombs45cab8c2016-11-06 11:01:08 -0500690 r"f'{\n}'",
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400691 ])
692
Eric V. Smith451d0e32016-09-09 21:56:20 -0400693 def test_no_escapes_for_braces(self):
Jason R. Coombs1c92a762016-11-06 11:25:54 -0500694 """
695 Only literal curly braces begin an expression.
696 """
697 # \x7b is '{'.
698 self.assertEqual(f'\x7b1+1}}', '{1+1}')
699 self.assertEqual(f'\x7b1+1', '{1+1')
700 self.assertEqual(f'\u007b1+1', '{1+1')
701 self.assertEqual(f'\N{LEFT CURLY BRACKET}1+1\N{RIGHT CURLY BRACKET}', '{1+1}')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400702
Eric V. Smith235a6f02015-09-19 14:51:32 -0400703 def test_newlines_in_expressions(self):
704 self.assertEqual(f'{0}', '0')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400705 self.assertEqual(rf'''{3+
7064}''', '7')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400707
708 def test_lambda(self):
709 x = 5
710 self.assertEqual(f'{(lambda y:x*y)("8")!r}', "'88888'")
711 self.assertEqual(f'{(lambda y:x*y)("8")!r:10}', "'88888' ")
712 self.assertEqual(f'{(lambda y:x*y)("8"):10}', "88888 ")
713
714 # lambda doesn't work without parens, because the colon
715 # makes the parser think it's a format_spec
Lysandros Nikolaou9a4b38f2020-04-15 21:22:10 +0300716 self.assertAllRaise(SyntaxError, 'invalid syntax',
Eric V. Smith235a6f02015-09-19 14:51:32 -0400717 ["f'{lambda x:x}'",
718 ])
719
720 def test_yield(self):
721 # Not terribly useful, but make sure the yield turns
722 # a function into a generator
723 def fn(y):
724 f'y:{yield y*2}'
725
726 g = fn(4)
727 self.assertEqual(next(g), 8)
728
729 def test_yield_send(self):
730 def fn(x):
731 yield f'x:{yield (lambda i: x * i)}'
732
733 g = fn(10)
734 the_lambda = next(g)
735 self.assertEqual(the_lambda(4), 40)
736 self.assertEqual(g.send('string'), 'x:string')
737
738 def test_expressions_with_triple_quoted_strings(self):
739 self.assertEqual(f"{'''x'''}", 'x')
740 self.assertEqual(f"{'''eric's'''}", "eric's")
Eric V. Smith235a6f02015-09-19 14:51:32 -0400741
742 # Test concatenation within an expression
743 self.assertEqual(f'{"x" """eric"s""" "y"}', 'xeric"sy')
744 self.assertEqual(f'{"x" """eric"s"""}', 'xeric"s')
745 self.assertEqual(f'{"""eric"s""" "y"}', 'eric"sy')
746 self.assertEqual(f'{"""x""" """eric"s""" "y"}', 'xeric"sy')
747 self.assertEqual(f'{"""x""" """eric"s""" """y"""}', 'xeric"sy')
748 self.assertEqual(f'{r"""x""" """eric"s""" """y"""}', 'xeric"sy')
749
750 def test_multiple_vars(self):
751 x = 98
752 y = 'abc'
753 self.assertEqual(f'{x}{y}', '98abc')
754
755 self.assertEqual(f'X{x}{y}', 'X98abc')
756 self.assertEqual(f'{x}X{y}', '98Xabc')
757 self.assertEqual(f'{x}{y}X', '98abcX')
758
759 self.assertEqual(f'X{x}Y{y}', 'X98Yabc')
760 self.assertEqual(f'X{x}{y}Y', 'X98abcY')
761 self.assertEqual(f'{x}X{y}Y', '98XabcY')
762
763 self.assertEqual(f'X{x}Y{y}Z', 'X98YabcZ')
764
765 def test_closure(self):
766 def outer(x):
767 def inner():
768 return f'x:{x}'
769 return inner
770
771 self.assertEqual(outer('987')(), 'x:987')
772 self.assertEqual(outer(7)(), 'x:7')
773
774 def test_arguments(self):
775 y = 2
776 def f(x, width):
777 return f'x={x*y:{width}}'
778
779 self.assertEqual(f('foo', 10), 'x=foofoo ')
780 x = 'bar'
781 self.assertEqual(f(10, 10), 'x= 20')
782
783 def test_locals(self):
784 value = 123
785 self.assertEqual(f'v:{value}', 'v:123')
786
787 def test_missing_variable(self):
788 with self.assertRaises(NameError):
789 f'v:{value}'
790
791 def test_missing_format_spec(self):
792 class O:
793 def __format__(self, spec):
794 if not spec:
795 return '*'
796 return spec
797
798 self.assertEqual(f'{O():x}', 'x')
799 self.assertEqual(f'{O()}', '*')
800 self.assertEqual(f'{O():}', '*')
801
802 self.assertEqual(f'{3:}', '3')
803 self.assertEqual(f'{3!s:}', '3')
804
805 def test_global(self):
806 self.assertEqual(f'g:{a_global}', 'g:global variable')
807 self.assertEqual(f'g:{a_global!r}', "g:'global variable'")
808
809 a_local = 'local variable'
810 self.assertEqual(f'g:{a_global} l:{a_local}',
811 'g:global variable l:local variable')
812 self.assertEqual(f'g:{a_global!r}',
813 "g:'global variable'")
814 self.assertEqual(f'g:{a_global} l:{a_local!r}',
815 "g:global variable l:'local variable'")
816
817 self.assertIn("module 'unittest' from", f'{unittest}')
818
819 def test_shadowed_global(self):
820 a_global = 'really a local'
821 self.assertEqual(f'g:{a_global}', 'g:really a local')
822 self.assertEqual(f'g:{a_global!r}', "g:'really a local'")
823
824 a_local = 'local variable'
825 self.assertEqual(f'g:{a_global} l:{a_local}',
826 'g:really a local l:local variable')
827 self.assertEqual(f'g:{a_global!r}',
828 "g:'really a local'")
829 self.assertEqual(f'g:{a_global} l:{a_local!r}',
830 "g:really a local l:'local variable'")
831
832 def test_call(self):
833 def foo(x):
834 return 'x=' + str(x)
835
836 self.assertEqual(f'{foo(10)}', 'x=10')
837
838 def test_nested_fstrings(self):
839 y = 5
840 self.assertEqual(f'{f"{0}"*3}', '000')
841 self.assertEqual(f'{f"{y}"*3}', '555')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400842
843 def test_invalid_string_prefixes(self):
Pablo Galindo70c188e2020-04-13 02:47:35 +0100844 single_quote_cases = ["fu''",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400845 "uf''",
846 "Fu''",
847 "fU''",
848 "Uf''",
849 "uF''",
850 "ufr''",
851 "urf''",
852 "fur''",
853 "fru''",
854 "rfu''",
855 "ruf''",
856 "FUR''",
857 "Fur''",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400858 "fb''",
859 "fB''",
860 "Fb''",
861 "FB''",
862 "bf''",
863 "bF''",
864 "Bf''",
Pablo Galindo70c188e2020-04-13 02:47:35 +0100865 "BF''",]
866 double_quote_cases = [case.replace("'", '"') for case in single_quote_cases]
Lysandros Nikolaou846d8b22020-05-04 14:32:18 +0300867 self.assertAllRaise(SyntaxError, 'unexpected EOF while parsing',
Pablo Galindo70c188e2020-04-13 02:47:35 +0100868 single_quote_cases + double_quote_cases)
Eric V. Smith235a6f02015-09-19 14:51:32 -0400869
870 def test_leading_trailing_spaces(self):
871 self.assertEqual(f'{ 3}', '3')
872 self.assertEqual(f'{ 3}', '3')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400873 self.assertEqual(f'{3 }', '3')
874 self.assertEqual(f'{3 }', '3')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400875
876 self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]}}',
877 'expr={1: 2}')
878 self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]} }',
879 'expr={1: 2}')
880
Eric V. Smith235a6f02015-09-19 14:51:32 -0400881 def test_not_equal(self):
882 # There's a special test for this because there's a special
883 # case in the f-string parser to look for != as not ending an
884 # expression. Normally it would, while looking for !s or !r.
885
886 self.assertEqual(f'{3!=4}', 'True')
887 self.assertEqual(f'{3!=4:}', 'True')
888 self.assertEqual(f'{3!=4!s}', 'True')
889 self.assertEqual(f'{3!=4!s:.3}', 'Tru')
890
Eric V. Smith9a4135e2019-05-08 16:28:48 -0400891 def test_equal_equal(self):
892 # Because an expression ending in = has special meaning,
893 # there's a special test for ==. Make sure it works.
894
895 self.assertEqual(f'{0==1}', 'False')
896
Eric V. Smith235a6f02015-09-19 14:51:32 -0400897 def test_conversions(self):
898 self.assertEqual(f'{3.14:10.10}', ' 3.14')
899 self.assertEqual(f'{3.14!s:10.10}', '3.14 ')
900 self.assertEqual(f'{3.14!r:10.10}', '3.14 ')
901 self.assertEqual(f'{3.14!a:10.10}', '3.14 ')
902
903 self.assertEqual(f'{"a"}', 'a')
904 self.assertEqual(f'{"a"!r}', "'a'")
905 self.assertEqual(f'{"a"!a}', "'a'")
906
907 # Not a conversion.
908 self.assertEqual(f'{"a!r"}', "a!r")
909
910 # Not a conversion, but show that ! is allowed in a format spec.
911 self.assertEqual(f'{3.14:!<10.10}', '3.14!!!!!!')
912
Eric V. Smith235a6f02015-09-19 14:51:32 -0400913 self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character',
914 ["f'{3!g}'",
915 "f'{3!A}'",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400916 "f'{3!3}'",
917 "f'{3!G}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400918 "f'{3!!}'",
919 "f'{3!:}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400920 "f'{3! s}'", # no space before conversion char
Eric V. Smith235a6f02015-09-19 14:51:32 -0400921 ])
922
923 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
924 ["f'{x!s{y}}'",
925 "f'{3!ss}'",
926 "f'{3!ss:}'",
927 "f'{3!ss:s}'",
928 ])
929
930 def test_assignment(self):
931 self.assertAllRaise(SyntaxError, 'invalid syntax',
932 ["f'' = 3",
933 "f'{0}' = x",
934 "f'{x}' = x",
935 ])
936
937 def test_del(self):
938 self.assertAllRaise(SyntaxError, 'invalid syntax',
939 ["del f''",
940 "del '' f''",
941 ])
942
943 def test_mismatched_braces(self):
944 self.assertAllRaise(SyntaxError, "f-string: single '}' is not allowed",
945 ["f'{{}'",
946 "f'{{}}}'",
947 "f'}'",
948 "f'x}'",
949 "f'x}x'",
Jason R. Coombsda25abf72016-11-06 11:14:48 -0500950 r"f'\u007b}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400951
952 # Can't have { or } in a format spec.
953 "f'{3:}>10}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400954 "f'{3:}}>10}'",
955 ])
956
957 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
958 ["f'{3:{{>10}'",
959 "f'{3'",
960 "f'{3!'",
961 "f'{3:'",
962 "f'{3!s'",
963 "f'{3!s:'",
964 "f'{3!s:3'",
965 "f'x{'",
966 "f'x{x'",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400967 "f'{x'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400968 "f'{3:s'",
969 "f'{{{'",
970 "f'{{}}{'",
971 "f'{'",
972 ])
973
Eric V. Smith235a6f02015-09-19 14:51:32 -0400974 # But these are just normal strings.
975 self.assertEqual(f'{"{"}', '{')
976 self.assertEqual(f'{"}"}', '}')
977 self.assertEqual(f'{3:{"}"}>10}', '}}}}}}}}}3')
978 self.assertEqual(f'{2:{"{"}>10}', '{{{{{{{{{2')
979
980 def test_if_conditional(self):
981 # There's special logic in compile.c to test if the
982 # conditional for an if (and while) are constants. Exercise
983 # that code.
984
985 def test_fstring(x, expected):
986 flag = 0
987 if f'{x}':
988 flag = 1
989 else:
990 flag = 2
991 self.assertEqual(flag, expected)
992
993 def test_concat_empty(x, expected):
994 flag = 0
995 if '' f'{x}':
996 flag = 1
997 else:
998 flag = 2
999 self.assertEqual(flag, expected)
1000
1001 def test_concat_non_empty(x, expected):
1002 flag = 0
1003 if ' ' f'{x}':
1004 flag = 1
1005 else:
1006 flag = 2
1007 self.assertEqual(flag, expected)
1008
1009 test_fstring('', 2)
1010 test_fstring(' ', 1)
1011
1012 test_concat_empty('', 2)
1013 test_concat_empty(' ', 1)
1014
1015 test_concat_non_empty('', 1)
1016 test_concat_non_empty(' ', 1)
1017
1018 def test_empty_format_specifier(self):
1019 x = 'test'
1020 self.assertEqual(f'{x}', 'test')
1021 self.assertEqual(f'{x:}', 'test')
1022 self.assertEqual(f'{x!s:}', 'test')
1023 self.assertEqual(f'{x!r:}', "'test'")
1024
1025 def test_str_format_differences(self):
1026 d = {'a': 'string',
1027 0: 'integer',
1028 }
1029 a = 0
1030 self.assertEqual(f'{d[0]}', 'integer')
1031 self.assertEqual(f'{d["a"]}', 'string')
1032 self.assertEqual(f'{d[a]}', 'integer')
1033 self.assertEqual('{d[a]}'.format(d=d), 'string')
1034 self.assertEqual('{d[0]}'.format(d=d), 'integer')
1035
Eric V. Smith135d5f42016-02-05 18:23:08 -05001036 def test_errors(self):
1037 # see issue 26287
Serhiy Storchaka13c8f322016-10-31 08:13:00 +02001038 self.assertAllRaise(TypeError, 'unsupported',
Eric V. Smith135d5f42016-02-05 18:23:08 -05001039 [r"f'{(lambda: 0):x}'",
1040 r"f'{(0,):x}'",
1041 ])
1042 self.assertAllRaise(ValueError, 'Unknown format code',
1043 [r"f'{1000:j}'",
1044 r"f'{1000:j}'",
1045 ])
1046
Eric V. Smith235a6f02015-09-19 14:51:32 -04001047 def test_loop(self):
1048 for i in range(1000):
1049 self.assertEqual(f'i:{i}', 'i:' + str(i))
1050
1051 def test_dict(self):
1052 d = {'"': 'dquote',
1053 "'": 'squote',
1054 'foo': 'bar',
1055 }
Eric V. Smith235a6f02015-09-19 14:51:32 -04001056 self.assertEqual(f'''{d["'"]}''', 'squote')
1057 self.assertEqual(f"""{d['"']}""", 'dquote')
1058
1059 self.assertEqual(f'{d["foo"]}', 'bar')
1060 self.assertEqual(f"{d['foo']}", 'bar')
Eric V. Smith235a6f02015-09-19 14:51:32 -04001061
ericvsmith11e97f22017-06-16 06:19:32 -04001062 def test_backslash_char(self):
1063 # Check eval of a backslash followed by a control char.
1064 # See bpo-30682: this used to raise an assert in pydebug mode.
1065 self.assertEqual(eval('f"\\\n"'), '')
1066 self.assertEqual(eval('f"\\\r"'), '')
1067
Eric V. Smith9a4135e2019-05-08 16:28:48 -04001068 def test_debug_conversion(self):
1069 x = 'A string'
1070 self.assertEqual(f'{x=}', 'x=' + repr(x))
1071 self.assertEqual(f'{x =}', 'x =' + repr(x))
1072 self.assertEqual(f'{x=!s}', 'x=' + str(x))
1073 self.assertEqual(f'{x=!r}', 'x=' + repr(x))
1074 self.assertEqual(f'{x=!a}', 'x=' + ascii(x))
1075
1076 x = 2.71828
1077 self.assertEqual(f'{x=:.2f}', 'x=' + format(x, '.2f'))
1078 self.assertEqual(f'{x=:}', 'x=' + format(x, ''))
1079 self.assertEqual(f'{x=!r:^20}', 'x=' + format(repr(x), '^20'))
1080 self.assertEqual(f'{x=!s:^20}', 'x=' + format(str(x), '^20'))
1081 self.assertEqual(f'{x=!a:^20}', 'x=' + format(ascii(x), '^20'))
1082
1083 x = 9
1084 self.assertEqual(f'{3*x+15=}', '3*x+15=42')
1085
1086 # There is code in ast.c that deals with non-ascii expression values. So,
1087 # use a unicode identifier to trigger that.
1088 tenπ = 31.4
1089 self.assertEqual(f'{tenπ=:.2f}', 'tenπ=31.40')
1090
1091 # Also test with Unicode in non-identifiers.
1092 self.assertEqual(f'{"Σ"=}', '"Σ"=\'Σ\'')
1093
1094 # Make sure nested fstrings still work.
1095 self.assertEqual(f'{f"{3.1415=:.1f}":*^20}', '*****3.1415=3.1*****')
1096
1097 # Make sure text before and after an expression with = works
1098 # correctly.
1099 pi = 'π'
1100 self.assertEqual(f'alpha α {pi=} ω omega', "alpha α pi='π' ω omega")
1101
1102 # Check multi-line expressions.
1103 self.assertEqual(f'''{
11043
1105=}''', '\n3\n=3')
1106
1107 # Since = is handled specially, make sure all existing uses of
1108 # it still work.
1109
1110 self.assertEqual(f'{0==1}', 'False')
1111 self.assertEqual(f'{0!=1}', 'True')
1112 self.assertEqual(f'{0<=1}', 'True')
1113 self.assertEqual(f'{0>=1}', 'False')
1114 self.assertEqual(f'{(x:="5")}', '5')
1115 self.assertEqual(x, '5')
1116 self.assertEqual(f'{(x:=5)}', '5')
1117 self.assertEqual(x, 5)
1118 self.assertEqual(f'{"="}', '=')
1119
1120 x = 20
1121 # This isn't an assignment expression, it's 'x', with a format
1122 # spec of '=10'. See test_walrus: you need to use parens.
1123 self.assertEqual(f'{x:=10}', ' 20')
1124
1125 # Test named function parameters, to make sure '=' parsing works
1126 # there.
1127 def f(a):
1128 nonlocal x
1129 oldx = x
1130 x = a
1131 return oldx
1132 x = 0
1133 self.assertEqual(f'{f(a="3=")}', '0')
1134 self.assertEqual(x, '3=')
1135 self.assertEqual(f'{f(a=4)}', '3=')
1136 self.assertEqual(x, 4)
1137
1138 # Make sure __format__ is being called.
1139 class C:
1140 def __format__(self, s):
1141 return f'FORMAT-{s}'
1142 def __repr__(self):
1143 return 'REPR'
1144
1145 self.assertEqual(f'{C()=}', 'C()=REPR')
1146 self.assertEqual(f'{C()=!r}', 'C()=REPR')
1147 self.assertEqual(f'{C()=:}', 'C()=FORMAT-')
1148 self.assertEqual(f'{C()=: }', 'C()=FORMAT- ')
1149 self.assertEqual(f'{C()=:x}', 'C()=FORMAT-x')
1150 self.assertEqual(f'{C()=!r:*^20}', 'C()=********REPR********')
1151
Pablo Galindo26f55c22019-05-12 01:43:04 +01001152 self.assertRaises(SyntaxError, eval, "f'{C=]'")
1153
Eric V. Smith6f6ff8a2019-05-27 15:31:52 -04001154 # Make sure leading and following text works.
1155 x = 'foo'
1156 self.assertEqual(f'X{x=}Y', 'Xx='+repr(x)+'Y')
1157
1158 # Make sure whitespace around the = works.
1159 self.assertEqual(f'X{x =}Y', 'Xx ='+repr(x)+'Y')
1160 self.assertEqual(f'X{x= }Y', 'Xx= '+repr(x)+'Y')
1161 self.assertEqual(f'X{x = }Y', 'Xx = '+repr(x)+'Y')
1162
1163 # These next lines contains tabs. Backslash escapes don't
1164 # work in f-strings.
Min ho Kim96e12d52019-07-22 06:12:33 +10001165 # patchcheck doesn't like these tabs. So the only way to test
Eric V. Smith6f6ff8a2019-05-27 15:31:52 -04001166 # this will be to dynamically created and exec the f-strings. But
1167 # that's such a hassle I'll save it for another day. For now, convert
1168 # the tabs to spaces just to shut up patchcheck.
1169 #self.assertEqual(f'X{x =}Y', 'Xx\t='+repr(x)+'Y')
1170 #self.assertEqual(f'X{x = }Y', 'Xx\t=\t'+repr(x)+'Y')
1171
Eric V. Smith9a4135e2019-05-08 16:28:48 -04001172 def test_walrus(self):
1173 x = 20
1174 # This isn't an assignment expression, it's 'x', with a format
1175 # spec of '=10'.
1176 self.assertEqual(f'{x:=10}', ' 20')
1177
1178 # This is an assignment expression, which requires parens.
1179 self.assertEqual(f'{(x:=10)}', '10')
1180 self.assertEqual(x, 10)
1181
Łukasz Langae7c566c2017-09-06 17:27:58 -07001182
Eric V. Smith235a6f02015-09-19 14:51:32 -04001183if __name__ == '__main__':
1184 unittest.main()