blob: 49663923e7f5aa2e52c04a4e01514d4bc0d05459 [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)
208 self.assertEqual(call.col_offset, 11)
209
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.
586 self.assertAllRaise(SyntaxError, 'invalid character in identifier',
587 ["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
Serhiy Storchaka4c5b6ba2019-08-10 01:34:22 +0300652 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
716 self.assertAllRaise(SyntaxError, 'unexpected EOF while parsing',
717 ["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):
844 self.assertAllRaise(SyntaxError, 'unexpected EOF while parsing',
845 ["fu''",
846 "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''",
866 "BF''",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400867 ])
868
869 def test_leading_trailing_spaces(self):
870 self.assertEqual(f'{ 3}', '3')
871 self.assertEqual(f'{ 3}', '3')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400872 self.assertEqual(f'{3 }', '3')
873 self.assertEqual(f'{3 }', '3')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400874
875 self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]}}',
876 'expr={1: 2}')
877 self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]} }',
878 'expr={1: 2}')
879
Eric V. Smith235a6f02015-09-19 14:51:32 -0400880 def test_not_equal(self):
881 # There's a special test for this because there's a special
882 # case in the f-string parser to look for != as not ending an
883 # expression. Normally it would, while looking for !s or !r.
884
885 self.assertEqual(f'{3!=4}', 'True')
886 self.assertEqual(f'{3!=4:}', 'True')
887 self.assertEqual(f'{3!=4!s}', 'True')
888 self.assertEqual(f'{3!=4!s:.3}', 'Tru')
889
Eric V. Smith9a4135e2019-05-08 16:28:48 -0400890 def test_equal_equal(self):
891 # Because an expression ending in = has special meaning,
892 # there's a special test for ==. Make sure it works.
893
894 self.assertEqual(f'{0==1}', 'False')
895
Eric V. Smith235a6f02015-09-19 14:51:32 -0400896 def test_conversions(self):
897 self.assertEqual(f'{3.14:10.10}', ' 3.14')
898 self.assertEqual(f'{3.14!s:10.10}', '3.14 ')
899 self.assertEqual(f'{3.14!r:10.10}', '3.14 ')
900 self.assertEqual(f'{3.14!a:10.10}', '3.14 ')
901
902 self.assertEqual(f'{"a"}', 'a')
903 self.assertEqual(f'{"a"!r}', "'a'")
904 self.assertEqual(f'{"a"!a}', "'a'")
905
906 # Not a conversion.
907 self.assertEqual(f'{"a!r"}', "a!r")
908
909 # Not a conversion, but show that ! is allowed in a format spec.
910 self.assertEqual(f'{3.14:!<10.10}', '3.14!!!!!!')
911
Eric V. Smith235a6f02015-09-19 14:51:32 -0400912 self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character',
913 ["f'{3!g}'",
914 "f'{3!A}'",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400915 "f'{3!3}'",
916 "f'{3!G}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400917 "f'{3!!}'",
918 "f'{3!:}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400919 "f'{3! s}'", # no space before conversion char
Eric V. Smith235a6f02015-09-19 14:51:32 -0400920 ])
921
922 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
923 ["f'{x!s{y}}'",
924 "f'{3!ss}'",
925 "f'{3!ss:}'",
926 "f'{3!ss:s}'",
927 ])
928
929 def test_assignment(self):
930 self.assertAllRaise(SyntaxError, 'invalid syntax',
931 ["f'' = 3",
932 "f'{0}' = x",
933 "f'{x}' = x",
934 ])
935
936 def test_del(self):
937 self.assertAllRaise(SyntaxError, 'invalid syntax',
938 ["del f''",
939 "del '' f''",
940 ])
941
942 def test_mismatched_braces(self):
943 self.assertAllRaise(SyntaxError, "f-string: single '}' is not allowed",
944 ["f'{{}'",
945 "f'{{}}}'",
946 "f'}'",
947 "f'x}'",
948 "f'x}x'",
Jason R. Coombsda25abf72016-11-06 11:14:48 -0500949 r"f'\u007b}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400950
951 # Can't have { or } in a format spec.
952 "f'{3:}>10}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400953 "f'{3:}}>10}'",
954 ])
955
956 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
957 ["f'{3:{{>10}'",
958 "f'{3'",
959 "f'{3!'",
960 "f'{3:'",
961 "f'{3!s'",
962 "f'{3!s:'",
963 "f'{3!s:3'",
964 "f'x{'",
965 "f'x{x'",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400966 "f'{x'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400967 "f'{3:s'",
968 "f'{{{'",
969 "f'{{}}{'",
970 "f'{'",
971 ])
972
Eric V. Smith235a6f02015-09-19 14:51:32 -0400973 # But these are just normal strings.
974 self.assertEqual(f'{"{"}', '{')
975 self.assertEqual(f'{"}"}', '}')
976 self.assertEqual(f'{3:{"}"}>10}', '}}}}}}}}}3')
977 self.assertEqual(f'{2:{"{"}>10}', '{{{{{{{{{2')
978
979 def test_if_conditional(self):
980 # There's special logic in compile.c to test if the
981 # conditional for an if (and while) are constants. Exercise
982 # that code.
983
984 def test_fstring(x, expected):
985 flag = 0
986 if f'{x}':
987 flag = 1
988 else:
989 flag = 2
990 self.assertEqual(flag, expected)
991
992 def test_concat_empty(x, expected):
993 flag = 0
994 if '' f'{x}':
995 flag = 1
996 else:
997 flag = 2
998 self.assertEqual(flag, expected)
999
1000 def test_concat_non_empty(x, expected):
1001 flag = 0
1002 if ' ' f'{x}':
1003 flag = 1
1004 else:
1005 flag = 2
1006 self.assertEqual(flag, expected)
1007
1008 test_fstring('', 2)
1009 test_fstring(' ', 1)
1010
1011 test_concat_empty('', 2)
1012 test_concat_empty(' ', 1)
1013
1014 test_concat_non_empty('', 1)
1015 test_concat_non_empty(' ', 1)
1016
1017 def test_empty_format_specifier(self):
1018 x = 'test'
1019 self.assertEqual(f'{x}', 'test')
1020 self.assertEqual(f'{x:}', 'test')
1021 self.assertEqual(f'{x!s:}', 'test')
1022 self.assertEqual(f'{x!r:}', "'test'")
1023
1024 def test_str_format_differences(self):
1025 d = {'a': 'string',
1026 0: 'integer',
1027 }
1028 a = 0
1029 self.assertEqual(f'{d[0]}', 'integer')
1030 self.assertEqual(f'{d["a"]}', 'string')
1031 self.assertEqual(f'{d[a]}', 'integer')
1032 self.assertEqual('{d[a]}'.format(d=d), 'string')
1033 self.assertEqual('{d[0]}'.format(d=d), 'integer')
1034
Eric V. Smith135d5f42016-02-05 18:23:08 -05001035 def test_errors(self):
1036 # see issue 26287
Serhiy Storchaka13c8f322016-10-31 08:13:00 +02001037 self.assertAllRaise(TypeError, 'unsupported',
Eric V. Smith135d5f42016-02-05 18:23:08 -05001038 [r"f'{(lambda: 0):x}'",
1039 r"f'{(0,):x}'",
1040 ])
1041 self.assertAllRaise(ValueError, 'Unknown format code',
1042 [r"f'{1000:j}'",
1043 r"f'{1000:j}'",
1044 ])
1045
Eric V. Smith235a6f02015-09-19 14:51:32 -04001046 def test_loop(self):
1047 for i in range(1000):
1048 self.assertEqual(f'i:{i}', 'i:' + str(i))
1049
1050 def test_dict(self):
1051 d = {'"': 'dquote',
1052 "'": 'squote',
1053 'foo': 'bar',
1054 }
Eric V. Smith235a6f02015-09-19 14:51:32 -04001055 self.assertEqual(f'''{d["'"]}''', 'squote')
1056 self.assertEqual(f"""{d['"']}""", 'dquote')
1057
1058 self.assertEqual(f'{d["foo"]}', 'bar')
1059 self.assertEqual(f"{d['foo']}", 'bar')
Eric V. Smith235a6f02015-09-19 14:51:32 -04001060
ericvsmith11e97f22017-06-16 06:19:32 -04001061 def test_backslash_char(self):
1062 # Check eval of a backslash followed by a control char.
1063 # See bpo-30682: this used to raise an assert in pydebug mode.
1064 self.assertEqual(eval('f"\\\n"'), '')
1065 self.assertEqual(eval('f"\\\r"'), '')
1066
Eric V. Smith9a4135e2019-05-08 16:28:48 -04001067 def test_debug_conversion(self):
1068 x = 'A string'
1069 self.assertEqual(f'{x=}', 'x=' + repr(x))
1070 self.assertEqual(f'{x =}', 'x =' + repr(x))
1071 self.assertEqual(f'{x=!s}', 'x=' + str(x))
1072 self.assertEqual(f'{x=!r}', 'x=' + repr(x))
1073 self.assertEqual(f'{x=!a}', 'x=' + ascii(x))
1074
1075 x = 2.71828
1076 self.assertEqual(f'{x=:.2f}', 'x=' + format(x, '.2f'))
1077 self.assertEqual(f'{x=:}', 'x=' + format(x, ''))
1078 self.assertEqual(f'{x=!r:^20}', 'x=' + format(repr(x), '^20'))
1079 self.assertEqual(f'{x=!s:^20}', 'x=' + format(str(x), '^20'))
1080 self.assertEqual(f'{x=!a:^20}', 'x=' + format(ascii(x), '^20'))
1081
1082 x = 9
1083 self.assertEqual(f'{3*x+15=}', '3*x+15=42')
1084
1085 # There is code in ast.c that deals with non-ascii expression values. So,
1086 # use a unicode identifier to trigger that.
1087 tenπ = 31.4
1088 self.assertEqual(f'{tenπ=:.2f}', 'tenπ=31.40')
1089
1090 # Also test with Unicode in non-identifiers.
1091 self.assertEqual(f'{"Σ"=}', '"Σ"=\'Σ\'')
1092
1093 # Make sure nested fstrings still work.
1094 self.assertEqual(f'{f"{3.1415=:.1f}":*^20}', '*****3.1415=3.1*****')
1095
1096 # Make sure text before and after an expression with = works
1097 # correctly.
1098 pi = 'π'
1099 self.assertEqual(f'alpha α {pi=} ω omega', "alpha α pi='π' ω omega")
1100
1101 # Check multi-line expressions.
1102 self.assertEqual(f'''{
11033
1104=}''', '\n3\n=3')
1105
1106 # Since = is handled specially, make sure all existing uses of
1107 # it still work.
1108
1109 self.assertEqual(f'{0==1}', 'False')
1110 self.assertEqual(f'{0!=1}', 'True')
1111 self.assertEqual(f'{0<=1}', 'True')
1112 self.assertEqual(f'{0>=1}', 'False')
1113 self.assertEqual(f'{(x:="5")}', '5')
1114 self.assertEqual(x, '5')
1115 self.assertEqual(f'{(x:=5)}', '5')
1116 self.assertEqual(x, 5)
1117 self.assertEqual(f'{"="}', '=')
1118
1119 x = 20
1120 # This isn't an assignment expression, it's 'x', with a format
1121 # spec of '=10'. See test_walrus: you need to use parens.
1122 self.assertEqual(f'{x:=10}', ' 20')
1123
1124 # Test named function parameters, to make sure '=' parsing works
1125 # there.
1126 def f(a):
1127 nonlocal x
1128 oldx = x
1129 x = a
1130 return oldx
1131 x = 0
1132 self.assertEqual(f'{f(a="3=")}', '0')
1133 self.assertEqual(x, '3=')
1134 self.assertEqual(f'{f(a=4)}', '3=')
1135 self.assertEqual(x, 4)
1136
1137 # Make sure __format__ is being called.
1138 class C:
1139 def __format__(self, s):
1140 return f'FORMAT-{s}'
1141 def __repr__(self):
1142 return 'REPR'
1143
1144 self.assertEqual(f'{C()=}', 'C()=REPR')
1145 self.assertEqual(f'{C()=!r}', 'C()=REPR')
1146 self.assertEqual(f'{C()=:}', 'C()=FORMAT-')
1147 self.assertEqual(f'{C()=: }', 'C()=FORMAT- ')
1148 self.assertEqual(f'{C()=:x}', 'C()=FORMAT-x')
1149 self.assertEqual(f'{C()=!r:*^20}', 'C()=********REPR********')
1150
Pablo Galindo26f55c22019-05-12 01:43:04 +01001151 self.assertRaises(SyntaxError, eval, "f'{C=]'")
1152
Eric V. Smith6f6ff8a2019-05-27 15:31:52 -04001153 # Make sure leading and following text works.
1154 x = 'foo'
1155 self.assertEqual(f'X{x=}Y', 'Xx='+repr(x)+'Y')
1156
1157 # Make sure whitespace around the = works.
1158 self.assertEqual(f'X{x =}Y', 'Xx ='+repr(x)+'Y')
1159 self.assertEqual(f'X{x= }Y', 'Xx= '+repr(x)+'Y')
1160 self.assertEqual(f'X{x = }Y', 'Xx = '+repr(x)+'Y')
1161
1162 # These next lines contains tabs. Backslash escapes don't
1163 # work in f-strings.
Kyle Stanley24b5b362019-07-21 22:48:45 -04001164 # patchcheck doesn't like these tabs. So the only way to test
Eric V. Smith6f6ff8a2019-05-27 15:31:52 -04001165 # this will be to dynamically created and exec the f-strings. But
1166 # that's such a hassle I'll save it for another day. For now, convert
1167 # the tabs to spaces just to shut up patchcheck.
1168 #self.assertEqual(f'X{x =}Y', 'Xx\t='+repr(x)+'Y')
1169 #self.assertEqual(f'X{x = }Y', 'Xx\t=\t'+repr(x)+'Y')
1170
Eric V. Smith9a4135e2019-05-08 16:28:48 -04001171 def test_walrus(self):
1172 x = 20
1173 # This isn't an assignment expression, it's 'x', with a format
1174 # spec of '=10'.
1175 self.assertEqual(f'{x:=10}', ' 20')
1176
1177 # This is an assignment expression, which requires parens.
1178 self.assertEqual(f'{(x:=10)}', '10')
1179 self.assertEqual(x, 10)
1180
Łukasz Langae7c566c2017-09-06 17:27:58 -07001181
Eric V. Smith235a6f02015-09-19 14:51:32 -04001182if __name__ == '__main__':
1183 unittest.main()