blob: 708ed25b526b743c10e12cfc4dba98ef8cac26d0 [file] [log] [blame]
Eric V. Smith235a6f02015-09-19 14:51:32 -04001import ast
2import types
3import decimal
4import unittest
5
6a_global = 'global variable'
7
8# You could argue that I'm too strict in looking for specific error
9# values with assertRaisesRegex, but without it it's way too easy to
10# make a syntax error in the test strings. Especially with all of the
11# triple quotes, raw strings, backslashes, etc. I think it's a
12# worthwhile tradeoff. When I switched to this method, I found many
13# examples where I wasn't testing what I thought I was.
14
15class TestCase(unittest.TestCase):
16 def assertAllRaise(self, exception_type, regex, error_strings):
17 for str in error_strings:
18 with self.subTest(str=str):
19 with self.assertRaisesRegex(exception_type, regex):
20 eval(str)
21
22 def test__format__lookup(self):
23 # Make sure __format__ is looked up on the type, not the instance.
24 class X:
25 def __format__(self, spec):
26 return 'class'
27
28 x = X()
29
30 # Add a bound __format__ method to the 'y' instance, but not
31 # the 'x' instance.
32 y = X()
33 y.__format__ = types.MethodType(lambda self, spec: 'instance', y)
34
35 self.assertEqual(f'{y}', format(y))
36 self.assertEqual(f'{y}', 'class')
37 self.assertEqual(format(x), format(y))
38
39 # __format__ is not called this way, but still make sure it
40 # returns what we expect (so we can make sure we're bypassing
41 # it).
42 self.assertEqual(x.__format__(''), 'class')
43 self.assertEqual(y.__format__(''), 'instance')
44
45 # This is how __format__ is actually called.
46 self.assertEqual(type(x).__format__(x, ''), 'class')
47 self.assertEqual(type(y).__format__(y, ''), 'class')
48
49 def test_ast(self):
50 # Inspired by http://bugs.python.org/issue24975
51 class X:
52 def __init__(self):
53 self.called = False
54 def __call__(self):
55 self.called = True
56 return 4
57 x = X()
58 expr = """
59a = 10
60f'{a * x()}'"""
61 t = ast.parse(expr)
62 c = compile(t, '', 'exec')
63
64 # Make sure x was not called.
65 self.assertFalse(x.called)
66
67 # Actually run the code.
68 exec(c)
69
70 # Make sure x was called.
71 self.assertTrue(x.called)
72
Serhiy Storchaka4cc30ae2016-12-11 19:37:19 +020073 def test_docstring(self):
74 def f():
75 f'''Not a docstring'''
76 self.assertIsNone(f.__doc__)
77 def g():
78 '''Not a docstring''' \
79 f''
80 self.assertIsNone(g.__doc__)
81
Eric V. Smith235a6f02015-09-19 14:51:32 -040082 def test_literal_eval(self):
Eric V. Smith235a6f02015-09-19 14:51:32 -040083 with self.assertRaisesRegex(ValueError, 'malformed node or string'):
Serhiy Storchaka4cc30ae2016-12-11 19:37:19 +020084 ast.literal_eval("f'x'")
Eric V. Smith235a6f02015-09-19 14:51:32 -040085
86 def test_ast_compile_time_concat(self):
87 x = ['']
88
89 expr = """x[0] = 'foo' f'{3}'"""
90 t = ast.parse(expr)
91 c = compile(t, '', 'exec')
92 exec(c)
93 self.assertEqual(x[0], 'foo3')
94
Eric V. Smith9b88fdf2016-11-07 17:54:01 -050095 def test_compile_time_concat_errors(self):
96 self.assertAllRaise(SyntaxError,
97 'cannot mix bytes and nonbytes literals',
98 [r"""f'' b''""",
99 r"""b'' f''""",
100 ])
101
Eric V. Smith235a6f02015-09-19 14:51:32 -0400102 def test_literal(self):
103 self.assertEqual(f'', '')
104 self.assertEqual(f'a', 'a')
105 self.assertEqual(f' ', ' ')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400106
107 def test_unterminated_string(self):
108 self.assertAllRaise(SyntaxError, 'f-string: unterminated string',
109 [r"""f'{"x'""",
110 r"""f'{"x}'""",
111 r"""f'{("x'""",
112 r"""f'{("x}'""",
113 ])
114
115 def test_mismatched_parens(self):
116 self.assertAllRaise(SyntaxError, 'f-string: mismatched',
117 ["f'{((}'",
118 ])
119
120 def test_double_braces(self):
121 self.assertEqual(f'{{', '{')
122 self.assertEqual(f'a{{', 'a{')
123 self.assertEqual(f'{{b', '{b')
124 self.assertEqual(f'a{{b', 'a{b')
125 self.assertEqual(f'}}', '}')
126 self.assertEqual(f'a}}', 'a}')
127 self.assertEqual(f'}}b', '}b')
128 self.assertEqual(f'a}}b', 'a}b')
Eric V. Smith451d0e32016-09-09 21:56:20 -0400129 self.assertEqual(f'{{}}', '{}')
130 self.assertEqual(f'a{{}}', 'a{}')
131 self.assertEqual(f'{{b}}', '{b}')
132 self.assertEqual(f'{{}}c', '{}c')
133 self.assertEqual(f'a{{b}}', 'a{b}')
134 self.assertEqual(f'a{{}}c', 'a{}c')
135 self.assertEqual(f'{{b}}c', '{b}c')
136 self.assertEqual(f'a{{b}}c', 'a{b}c')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400137
138 self.assertEqual(f'{{{10}', '{10')
139 self.assertEqual(f'}}{10}', '}10')
140 self.assertEqual(f'}}{{{10}', '}{10')
141 self.assertEqual(f'}}a{{{10}', '}a{10')
142
143 self.assertEqual(f'{10}{{', '10{')
144 self.assertEqual(f'{10}}}', '10}')
145 self.assertEqual(f'{10}}}{{', '10}{')
146 self.assertEqual(f'{10}}}a{{' '}', '10}a{}')
147
148 # Inside of strings, don't interpret doubled brackets.
149 self.assertEqual(f'{"{{}}"}', '{{}}')
150
151 self.assertAllRaise(TypeError, 'unhashable type',
152 ["f'{ {{}} }'", # dict in a set
153 ])
154
155 def test_compile_time_concat(self):
156 x = 'def'
157 self.assertEqual('abc' f'## {x}ghi', 'abc## defghi')
158 self.assertEqual('abc' f'{x}' 'ghi', 'abcdefghi')
159 self.assertEqual('abc' f'{x}' 'gh' f'i{x:4}', 'abcdefghidef ')
160 self.assertEqual('{x}' f'{x}', '{x}def')
161 self.assertEqual('{x' f'{x}', '{xdef')
162 self.assertEqual('{x}' f'{x}', '{x}def')
163 self.assertEqual('{{x}}' f'{x}', '{{x}}def')
164 self.assertEqual('{{x' f'{x}', '{{xdef')
165 self.assertEqual('x}}' f'{x}', 'x}}def')
166 self.assertEqual(f'{x}' 'x}}', 'defx}}')
167 self.assertEqual(f'{x}' '', 'def')
168 self.assertEqual('' f'{x}' '', 'def')
169 self.assertEqual('' f'{x}', 'def')
170 self.assertEqual(f'{x}' '2', 'def2')
171 self.assertEqual('1' f'{x}' '2', '1def2')
172 self.assertEqual('1' f'{x}', '1def')
173 self.assertEqual(f'{x}' f'-{x}', 'def-def')
174 self.assertEqual('' f'', '')
175 self.assertEqual('' f'' '', '')
176 self.assertEqual('' f'' '' f'', '')
177 self.assertEqual(f'', '')
178 self.assertEqual(f'' '', '')
179 self.assertEqual(f'' '' f'', '')
180 self.assertEqual(f'' '' f'' '', '')
181
182 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
183 ["f'{3' f'}'", # can't concat to get a valid f-string
184 ])
185
186 def test_comments(self):
187 # These aren't comments, since they're in strings.
188 d = {'#': 'hash'}
189 self.assertEqual(f'{"#"}', '#')
190 self.assertEqual(f'{d["#"]}', 'hash')
191
Eric V. Smith09835dc2016-09-11 18:58:20 -0400192 self.assertAllRaise(SyntaxError, "f-string expression part cannot include '#'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400193 ["f'{1#}'", # error because the expression becomes "(1#)"
194 "f'{3(#)}'",
Eric V. Smith09835dc2016-09-11 18:58:20 -0400195 "f'{#}'",
Eric V. Smith35a24c52016-09-11 19:01:22 -0400196 "f'{)#}'", # When wrapped in parens, this becomes
197 # '()#)'. Make sure that doesn't compile.
Eric V. Smith235a6f02015-09-19 14:51:32 -0400198 ])
199
200 def test_many_expressions(self):
201 # Create a string with many expressions in it. Note that
202 # because we have a space in here as a literal, we're actually
203 # going to use twice as many ast nodes: one for each literal
204 # plus one for each expression.
205 def build_fstr(n, extra=''):
206 return "f'" + ('{x} ' * n) + extra + "'"
207
208 x = 'X'
209 width = 1
210
211 # Test around 256.
212 for i in range(250, 260):
213 self.assertEqual(eval(build_fstr(i)), (x+' ')*i)
214
215 # Test concatenating 2 largs fstrings.
216 self.assertEqual(eval(build_fstr(255)*256), (x+' ')*(255*256))
217
218 s = build_fstr(253, '{x:{width}} ')
219 self.assertEqual(eval(s), (x+' ')*254)
220
221 # Test lots of expressions and constants, concatenated.
222 s = "f'{1}' 'x' 'y'" * 1024
223 self.assertEqual(eval(s), '1xy' * 1024)
224
225 def test_format_specifier_expressions(self):
226 width = 10
227 precision = 4
228 value = decimal.Decimal('12.34567')
229 self.assertEqual(f'result: {value:{width}.{precision}}', 'result: 12.35')
230 self.assertEqual(f'result: {value:{width!r}.{precision}}', 'result: 12.35')
231 self.assertEqual(f'result: {value:{width:0}.{precision:1}}', 'result: 12.35')
232 self.assertEqual(f'result: {value:{1}{0:0}.{precision:1}}', 'result: 12.35')
233 self.assertEqual(f'result: {value:{ 1}{ 0:0}.{ precision:1}}', 'result: 12.35')
234 self.assertEqual(f'{10:#{1}0x}', ' 0xa')
235 self.assertEqual(f'{10:{"#"}1{0}{"x"}}', ' 0xa')
236 self.assertEqual(f'{-10:-{"#"}1{0}x}', ' -0xa')
237 self.assertEqual(f'{-10:{"-"}#{1}0{"x"}}', ' -0xa')
238 self.assertEqual(f'{10:#{3 != {4:5} and width}x}', ' 0xa')
239
240 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
241 ["""f'{"s"!r{":10"}}'""",
242
243 # This looks like a nested format spec.
244 ])
245
246 self.assertAllRaise(SyntaxError, "invalid syntax",
Martin Panter263893c2016-07-28 01:25:31 +0000247 [# Invalid syntax inside a nested spec.
Eric V. Smith235a6f02015-09-19 14:51:32 -0400248 "f'{4:{/5}}'",
249 ])
250
251 self.assertAllRaise(SyntaxError, "f-string: expressions nested too deeply",
252 [# Can't nest format specifiers.
253 "f'result: {value:{width:{0}}.{precision:1}}'",
254 ])
255
256 self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character',
257 [# No expansion inside conversion or for
258 # the : or ! itself.
259 """f'{"s"!{"r"}}'""",
260 ])
261
262 def test_side_effect_order(self):
263 class X:
264 def __init__(self):
265 self.i = 0
266 def __format__(self, spec):
267 self.i += 1
268 return str(self.i)
269
270 x = X()
271 self.assertEqual(f'{x} {x}', '1 2')
272
273 def test_missing_expression(self):
274 self.assertAllRaise(SyntaxError, 'f-string: empty expression not allowed',
275 ["f'{}'",
276 "f'{ }'"
277 "f' {} '",
278 "f'{!r}'",
279 "f'{ !r}'",
280 "f'{10:{ }}'",
281 "f' { } '",
Eric V. Smith1d44c412015-09-23 07:49:00 -0400282
283 # Catch the empty expression before the
284 # invalid conversion.
285 "f'{!x}'",
286 "f'{ !xr}'",
287 "f'{!x:}'",
288 "f'{!x:a}'",
289 "f'{ !xr:}'",
290 "f'{ !xr:a}'",
Eric V. Smith548c4d32015-09-23 08:00:01 -0400291
292 "f'{!}'",
293 "f'{:}'",
Eric V. Smithb2080f62015-09-23 10:24:43 -0400294
295 # We find the empty expression before the
296 # missing closing brace.
297 "f'{!'",
298 "f'{!s:'",
299 "f'{:'",
300 "f'{:x'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400301 ])
302
303 def test_parens_in_expressions(self):
304 self.assertEqual(f'{3,}', '(3,)')
305
306 # Add these because when an expression is evaluated, parens
307 # are added around it. But we shouldn't go from an invalid
308 # expression to a valid one. The added parens are just
309 # supposed to allow whitespace (including newlines).
310 self.assertAllRaise(SyntaxError, 'invalid syntax',
311 ["f'{,}'",
312 "f'{,}'", # this is (,), which is an error
313 ])
314
315 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
316 ["f'{3)+(4}'",
317 ])
318
319 self.assertAllRaise(SyntaxError, 'EOL while scanning string literal',
320 ["f'{\n}'",
321 ])
322
Eric V. Smith451d0e32016-09-09 21:56:20 -0400323 def test_backslashes_in_string_part(self):
324 self.assertEqual(f'\t', '\t')
325 self.assertEqual(r'\t', '\\t')
326 self.assertEqual(rf'\t', '\\t')
327 self.assertEqual(f'{2}\t', '2\t')
328 self.assertEqual(f'{2}\t{3}', '2\t3')
329 self.assertEqual(f'\t{3}', '\t3')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400330
Eric V. Smith451d0e32016-09-09 21:56:20 -0400331 self.assertEqual(f'\u0394', '\u0394')
332 self.assertEqual(r'\u0394', '\\u0394')
333 self.assertEqual(rf'\u0394', '\\u0394')
334 self.assertEqual(f'{2}\u0394', '2\u0394')
335 self.assertEqual(f'{2}\u0394{3}', '2\u03943')
336 self.assertEqual(f'\u0394{3}', '\u03943')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400337
Eric V. Smith451d0e32016-09-09 21:56:20 -0400338 self.assertEqual(f'\U00000394', '\u0394')
339 self.assertEqual(r'\U00000394', '\\U00000394')
340 self.assertEqual(rf'\U00000394', '\\U00000394')
341 self.assertEqual(f'{2}\U00000394', '2\u0394')
342 self.assertEqual(f'{2}\U00000394{3}', '2\u03943')
343 self.assertEqual(f'\U00000394{3}', '\u03943')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400344
Eric V. Smith451d0e32016-09-09 21:56:20 -0400345 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}', '\u0394')
346 self.assertEqual(f'{2}\N{GREEK CAPITAL LETTER DELTA}', '2\u0394')
347 self.assertEqual(f'{2}\N{GREEK CAPITAL LETTER DELTA}{3}', '2\u03943')
348 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}{3}', '\u03943')
349 self.assertEqual(f'2\N{GREEK CAPITAL LETTER DELTA}', '2\u0394')
350 self.assertEqual(f'2\N{GREEK CAPITAL LETTER DELTA}3', '2\u03943')
351 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}3', '\u03943')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400352
Eric V. Smith451d0e32016-09-09 21:56:20 -0400353 self.assertEqual(f'\x20', ' ')
354 self.assertEqual(r'\x20', '\\x20')
355 self.assertEqual(rf'\x20', '\\x20')
356 self.assertEqual(f'{2}\x20', '2 ')
357 self.assertEqual(f'{2}\x20{3}', '2 3')
358 self.assertEqual(f'\x20{3}', ' 3')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400359
Eric V. Smith451d0e32016-09-09 21:56:20 -0400360 self.assertEqual(f'2\x20', '2 ')
361 self.assertEqual(f'2\x203', '2 3')
362 self.assertEqual(f'\x203', ' 3')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400363
Eric V. Smith451d0e32016-09-09 21:56:20 -0400364 def test_misformed_unicode_character_name(self):
365 # These test are needed because unicode names are parsed
366 # differently inside f-strings.
367 self.assertAllRaise(SyntaxError, r"\(unicode error\) 'unicodeescape' codec can't decode bytes in position .*: malformed \\N character escape",
368 [r"f'\N'",
369 r"f'\N{'",
370 r"f'\N{GREEK CAPITAL LETTER DELTA'",
371
372 # Here are the non-f-string versions,
373 # which should give the same errors.
374 r"'\N'",
375 r"'\N{'",
376 r"'\N{GREEK CAPITAL LETTER DELTA'",
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400377 ])
378
Eric V. Smith451d0e32016-09-09 21:56:20 -0400379 def test_no_backslashes_in_expression_part(self):
380 self.assertAllRaise(SyntaxError, 'f-string expression part cannot include a backslash',
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400381 [r"f'{\'a\'}'",
382 r"f'{\t3}'",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400383 r"f'{\}'",
384 r"rf'{\'a\'}'",
385 r"rf'{\t3}'",
386 r"rf'{\}'",
387 r"""rf'{"\N{LEFT CURLY BRACKET}"}'""",
Jason R. Coombs45cab8c2016-11-06 11:01:08 -0500388 r"f'{\n}'",
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400389 ])
390
Eric V. Smith451d0e32016-09-09 21:56:20 -0400391 def test_no_escapes_for_braces(self):
Jason R. Coombs1c92a762016-11-06 11:25:54 -0500392 """
393 Only literal curly braces begin an expression.
394 """
395 # \x7b is '{'.
396 self.assertEqual(f'\x7b1+1}}', '{1+1}')
397 self.assertEqual(f'\x7b1+1', '{1+1')
398 self.assertEqual(f'\u007b1+1', '{1+1')
399 self.assertEqual(f'\N{LEFT CURLY BRACKET}1+1\N{RIGHT CURLY BRACKET}', '{1+1}')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400400
Eric V. Smith235a6f02015-09-19 14:51:32 -0400401 def test_newlines_in_expressions(self):
402 self.assertEqual(f'{0}', '0')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400403 self.assertEqual(rf'''{3+
4044}''', '7')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400405
406 def test_lambda(self):
407 x = 5
408 self.assertEqual(f'{(lambda y:x*y)("8")!r}', "'88888'")
409 self.assertEqual(f'{(lambda y:x*y)("8")!r:10}', "'88888' ")
410 self.assertEqual(f'{(lambda y:x*y)("8"):10}', "88888 ")
411
412 # lambda doesn't work without parens, because the colon
413 # makes the parser think it's a format_spec
414 self.assertAllRaise(SyntaxError, 'unexpected EOF while parsing',
415 ["f'{lambda x:x}'",
416 ])
417
418 def test_yield(self):
419 # Not terribly useful, but make sure the yield turns
420 # a function into a generator
421 def fn(y):
422 f'y:{yield y*2}'
423
424 g = fn(4)
425 self.assertEqual(next(g), 8)
426
427 def test_yield_send(self):
428 def fn(x):
429 yield f'x:{yield (lambda i: x * i)}'
430
431 g = fn(10)
432 the_lambda = next(g)
433 self.assertEqual(the_lambda(4), 40)
434 self.assertEqual(g.send('string'), 'x:string')
435
436 def test_expressions_with_triple_quoted_strings(self):
437 self.assertEqual(f"{'''x'''}", 'x')
438 self.assertEqual(f"{'''eric's'''}", "eric's")
Eric V. Smith235a6f02015-09-19 14:51:32 -0400439
440 # Test concatenation within an expression
441 self.assertEqual(f'{"x" """eric"s""" "y"}', 'xeric"sy')
442 self.assertEqual(f'{"x" """eric"s"""}', 'xeric"s')
443 self.assertEqual(f'{"""eric"s""" "y"}', 'eric"sy')
444 self.assertEqual(f'{"""x""" """eric"s""" "y"}', 'xeric"sy')
445 self.assertEqual(f'{"""x""" """eric"s""" """y"""}', 'xeric"sy')
446 self.assertEqual(f'{r"""x""" """eric"s""" """y"""}', 'xeric"sy')
447
448 def test_multiple_vars(self):
449 x = 98
450 y = 'abc'
451 self.assertEqual(f'{x}{y}', '98abc')
452
453 self.assertEqual(f'X{x}{y}', 'X98abc')
454 self.assertEqual(f'{x}X{y}', '98Xabc')
455 self.assertEqual(f'{x}{y}X', '98abcX')
456
457 self.assertEqual(f'X{x}Y{y}', 'X98Yabc')
458 self.assertEqual(f'X{x}{y}Y', 'X98abcY')
459 self.assertEqual(f'{x}X{y}Y', '98XabcY')
460
461 self.assertEqual(f'X{x}Y{y}Z', 'X98YabcZ')
462
463 def test_closure(self):
464 def outer(x):
465 def inner():
466 return f'x:{x}'
467 return inner
468
469 self.assertEqual(outer('987')(), 'x:987')
470 self.assertEqual(outer(7)(), 'x:7')
471
472 def test_arguments(self):
473 y = 2
474 def f(x, width):
475 return f'x={x*y:{width}}'
476
477 self.assertEqual(f('foo', 10), 'x=foofoo ')
478 x = 'bar'
479 self.assertEqual(f(10, 10), 'x= 20')
480
481 def test_locals(self):
482 value = 123
483 self.assertEqual(f'v:{value}', 'v:123')
484
485 def test_missing_variable(self):
486 with self.assertRaises(NameError):
487 f'v:{value}'
488
489 def test_missing_format_spec(self):
490 class O:
491 def __format__(self, spec):
492 if not spec:
493 return '*'
494 return spec
495
496 self.assertEqual(f'{O():x}', 'x')
497 self.assertEqual(f'{O()}', '*')
498 self.assertEqual(f'{O():}', '*')
499
500 self.assertEqual(f'{3:}', '3')
501 self.assertEqual(f'{3!s:}', '3')
502
503 def test_global(self):
504 self.assertEqual(f'g:{a_global}', 'g:global variable')
505 self.assertEqual(f'g:{a_global!r}', "g:'global variable'")
506
507 a_local = 'local variable'
508 self.assertEqual(f'g:{a_global} l:{a_local}',
509 'g:global variable l:local variable')
510 self.assertEqual(f'g:{a_global!r}',
511 "g:'global variable'")
512 self.assertEqual(f'g:{a_global} l:{a_local!r}',
513 "g:global variable l:'local variable'")
514
515 self.assertIn("module 'unittest' from", f'{unittest}')
516
517 def test_shadowed_global(self):
518 a_global = 'really a local'
519 self.assertEqual(f'g:{a_global}', 'g:really a local')
520 self.assertEqual(f'g:{a_global!r}', "g:'really a local'")
521
522 a_local = 'local variable'
523 self.assertEqual(f'g:{a_global} l:{a_local}',
524 'g:really a local l:local variable')
525 self.assertEqual(f'g:{a_global!r}',
526 "g:'really a local'")
527 self.assertEqual(f'g:{a_global} l:{a_local!r}',
528 "g:really a local l:'local variable'")
529
530 def test_call(self):
531 def foo(x):
532 return 'x=' + str(x)
533
534 self.assertEqual(f'{foo(10)}', 'x=10')
535
536 def test_nested_fstrings(self):
537 y = 5
538 self.assertEqual(f'{f"{0}"*3}', '000')
539 self.assertEqual(f'{f"{y}"*3}', '555')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400540
541 def test_invalid_string_prefixes(self):
542 self.assertAllRaise(SyntaxError, 'unexpected EOF while parsing',
543 ["fu''",
544 "uf''",
545 "Fu''",
546 "fU''",
547 "Uf''",
548 "uF''",
549 "ufr''",
550 "urf''",
551 "fur''",
552 "fru''",
553 "rfu''",
554 "ruf''",
555 "FUR''",
556 "Fur''",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400557 "fb''",
558 "fB''",
559 "Fb''",
560 "FB''",
561 "bf''",
562 "bF''",
563 "Bf''",
564 "BF''",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400565 ])
566
567 def test_leading_trailing_spaces(self):
568 self.assertEqual(f'{ 3}', '3')
569 self.assertEqual(f'{ 3}', '3')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400570 self.assertEqual(f'{3 }', '3')
571 self.assertEqual(f'{3 }', '3')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400572
573 self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]}}',
574 'expr={1: 2}')
575 self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]} }',
576 'expr={1: 2}')
577
Eric V. Smith235a6f02015-09-19 14:51:32 -0400578 def test_not_equal(self):
579 # There's a special test for this because there's a special
580 # case in the f-string parser to look for != as not ending an
581 # expression. Normally it would, while looking for !s or !r.
582
583 self.assertEqual(f'{3!=4}', 'True')
584 self.assertEqual(f'{3!=4:}', 'True')
585 self.assertEqual(f'{3!=4!s}', 'True')
586 self.assertEqual(f'{3!=4!s:.3}', 'Tru')
587
588 def test_conversions(self):
589 self.assertEqual(f'{3.14:10.10}', ' 3.14')
590 self.assertEqual(f'{3.14!s:10.10}', '3.14 ')
591 self.assertEqual(f'{3.14!r:10.10}', '3.14 ')
592 self.assertEqual(f'{3.14!a:10.10}', '3.14 ')
593
594 self.assertEqual(f'{"a"}', 'a')
595 self.assertEqual(f'{"a"!r}', "'a'")
596 self.assertEqual(f'{"a"!a}', "'a'")
597
598 # Not a conversion.
599 self.assertEqual(f'{"a!r"}', "a!r")
600
601 # Not a conversion, but show that ! is allowed in a format spec.
602 self.assertEqual(f'{3.14:!<10.10}', '3.14!!!!!!')
603
Eric V. Smith235a6f02015-09-19 14:51:32 -0400604 self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character',
605 ["f'{3!g}'",
606 "f'{3!A}'",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400607 "f'{3!3}'",
608 "f'{3!G}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400609 "f'{3!!}'",
610 "f'{3!:}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400611 "f'{3! s}'", # no space before conversion char
Eric V. Smith235a6f02015-09-19 14:51:32 -0400612 ])
613
614 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
615 ["f'{x!s{y}}'",
616 "f'{3!ss}'",
617 "f'{3!ss:}'",
618 "f'{3!ss:s}'",
619 ])
620
621 def test_assignment(self):
622 self.assertAllRaise(SyntaxError, 'invalid syntax',
623 ["f'' = 3",
624 "f'{0}' = x",
625 "f'{x}' = x",
626 ])
627
628 def test_del(self):
629 self.assertAllRaise(SyntaxError, 'invalid syntax',
630 ["del f''",
631 "del '' f''",
632 ])
633
634 def test_mismatched_braces(self):
635 self.assertAllRaise(SyntaxError, "f-string: single '}' is not allowed",
636 ["f'{{}'",
637 "f'{{}}}'",
638 "f'}'",
639 "f'x}'",
640 "f'x}x'",
Jason R. Coombsda25abf72016-11-06 11:14:48 -0500641 r"f'\u007b}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400642
643 # Can't have { or } in a format spec.
644 "f'{3:}>10}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400645 "f'{3:}}>10}'",
646 ])
647
648 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
649 ["f'{3:{{>10}'",
650 "f'{3'",
651 "f'{3!'",
652 "f'{3:'",
653 "f'{3!s'",
654 "f'{3!s:'",
655 "f'{3!s:3'",
656 "f'x{'",
657 "f'x{x'",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400658 "f'{x'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400659 "f'{3:s'",
660 "f'{{{'",
661 "f'{{}}{'",
662 "f'{'",
663 ])
664
Eric V. Smith235a6f02015-09-19 14:51:32 -0400665 # But these are just normal strings.
666 self.assertEqual(f'{"{"}', '{')
667 self.assertEqual(f'{"}"}', '}')
668 self.assertEqual(f'{3:{"}"}>10}', '}}}}}}}}}3')
669 self.assertEqual(f'{2:{"{"}>10}', '{{{{{{{{{2')
670
671 def test_if_conditional(self):
672 # There's special logic in compile.c to test if the
673 # conditional for an if (and while) are constants. Exercise
674 # that code.
675
676 def test_fstring(x, expected):
677 flag = 0
678 if f'{x}':
679 flag = 1
680 else:
681 flag = 2
682 self.assertEqual(flag, expected)
683
684 def test_concat_empty(x, expected):
685 flag = 0
686 if '' f'{x}':
687 flag = 1
688 else:
689 flag = 2
690 self.assertEqual(flag, expected)
691
692 def test_concat_non_empty(x, expected):
693 flag = 0
694 if ' ' f'{x}':
695 flag = 1
696 else:
697 flag = 2
698 self.assertEqual(flag, expected)
699
700 test_fstring('', 2)
701 test_fstring(' ', 1)
702
703 test_concat_empty('', 2)
704 test_concat_empty(' ', 1)
705
706 test_concat_non_empty('', 1)
707 test_concat_non_empty(' ', 1)
708
709 def test_empty_format_specifier(self):
710 x = 'test'
711 self.assertEqual(f'{x}', 'test')
712 self.assertEqual(f'{x:}', 'test')
713 self.assertEqual(f'{x!s:}', 'test')
714 self.assertEqual(f'{x!r:}', "'test'")
715
716 def test_str_format_differences(self):
717 d = {'a': 'string',
718 0: 'integer',
719 }
720 a = 0
721 self.assertEqual(f'{d[0]}', 'integer')
722 self.assertEqual(f'{d["a"]}', 'string')
723 self.assertEqual(f'{d[a]}', 'integer')
724 self.assertEqual('{d[a]}'.format(d=d), 'string')
725 self.assertEqual('{d[0]}'.format(d=d), 'integer')
726
727 def test_invalid_expressions(self):
728 self.assertAllRaise(SyntaxError, 'invalid syntax',
729 [r"f'{a[4)}'",
730 r"f'{a(4]}'",
731 ])
732
Eric V. Smith135d5f42016-02-05 18:23:08 -0500733 def test_errors(self):
734 # see issue 26287
Serhiy Storchaka13c8f322016-10-31 08:13:00 +0200735 self.assertAllRaise(TypeError, 'unsupported',
Eric V. Smith135d5f42016-02-05 18:23:08 -0500736 [r"f'{(lambda: 0):x}'",
737 r"f'{(0,):x}'",
738 ])
739 self.assertAllRaise(ValueError, 'Unknown format code',
740 [r"f'{1000:j}'",
741 r"f'{1000:j}'",
742 ])
743
Eric V. Smith235a6f02015-09-19 14:51:32 -0400744 def test_loop(self):
745 for i in range(1000):
746 self.assertEqual(f'i:{i}', 'i:' + str(i))
747
748 def test_dict(self):
749 d = {'"': 'dquote',
750 "'": 'squote',
751 'foo': 'bar',
752 }
Eric V. Smith235a6f02015-09-19 14:51:32 -0400753 self.assertEqual(f'''{d["'"]}''', 'squote')
754 self.assertEqual(f"""{d['"']}""", 'dquote')
755
756 self.assertEqual(f'{d["foo"]}', 'bar')
757 self.assertEqual(f"{d['foo']}", 'bar')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400758
759if __name__ == '__main__':
760 unittest.main()