blob: 25730029ae76f17ee9f4543b98fcbbfcc6ab8214 [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
Serhiy Storchaka89a31022017-05-25 14:18:55 +0300364 with self.assertWarns(DeprecationWarning): # invalid escape sequence
365 value = eval(r"f'\{6*7}'")
366 self.assertEqual(value, '\\42')
367 self.assertEqual(f'\\{6*7}', '\\42')
368 self.assertEqual(fr'\{6*7}', '\\42')
369
370 AMPERSAND = 'spam'
371 # Get the right unicode character (&), or pick up local variable
372 # depending on the number of backslashes.
373 self.assertEqual(f'\N{AMPERSAND}', '&')
374 self.assertEqual(f'\\N{AMPERSAND}', '\\Nspam')
375 self.assertEqual(fr'\N{AMPERSAND}', '\\Nspam')
376 self.assertEqual(f'\\\N{AMPERSAND}', '\\&')
377
Eric V. Smith451d0e32016-09-09 21:56:20 -0400378 def test_misformed_unicode_character_name(self):
379 # These test are needed because unicode names are parsed
380 # differently inside f-strings.
381 self.assertAllRaise(SyntaxError, r"\(unicode error\) 'unicodeescape' codec can't decode bytes in position .*: malformed \\N character escape",
382 [r"f'\N'",
383 r"f'\N{'",
384 r"f'\N{GREEK CAPITAL LETTER DELTA'",
385
386 # Here are the non-f-string versions,
387 # which should give the same errors.
388 r"'\N'",
389 r"'\N{'",
390 r"'\N{GREEK CAPITAL LETTER DELTA'",
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400391 ])
392
Eric V. Smith451d0e32016-09-09 21:56:20 -0400393 def test_no_backslashes_in_expression_part(self):
394 self.assertAllRaise(SyntaxError, 'f-string expression part cannot include a backslash',
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400395 [r"f'{\'a\'}'",
396 r"f'{\t3}'",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400397 r"f'{\}'",
398 r"rf'{\'a\'}'",
399 r"rf'{\t3}'",
400 r"rf'{\}'",
401 r"""rf'{"\N{LEFT CURLY BRACKET}"}'""",
Jason R. Coombs45cab8c2016-11-06 11:01:08 -0500402 r"f'{\n}'",
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400403 ])
404
Eric V. Smith451d0e32016-09-09 21:56:20 -0400405 def test_no_escapes_for_braces(self):
Jason R. Coombs1c92a762016-11-06 11:25:54 -0500406 """
407 Only literal curly braces begin an expression.
408 """
409 # \x7b is '{'.
410 self.assertEqual(f'\x7b1+1}}', '{1+1}')
411 self.assertEqual(f'\x7b1+1', '{1+1')
412 self.assertEqual(f'\u007b1+1', '{1+1')
413 self.assertEqual(f'\N{LEFT CURLY BRACKET}1+1\N{RIGHT CURLY BRACKET}', '{1+1}')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400414
Eric V. Smith235a6f02015-09-19 14:51:32 -0400415 def test_newlines_in_expressions(self):
416 self.assertEqual(f'{0}', '0')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400417 self.assertEqual(rf'''{3+
4184}''', '7')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400419
420 def test_lambda(self):
421 x = 5
422 self.assertEqual(f'{(lambda y:x*y)("8")!r}', "'88888'")
423 self.assertEqual(f'{(lambda y:x*y)("8")!r:10}', "'88888' ")
424 self.assertEqual(f'{(lambda y:x*y)("8"):10}', "88888 ")
425
426 # lambda doesn't work without parens, because the colon
427 # makes the parser think it's a format_spec
428 self.assertAllRaise(SyntaxError, 'unexpected EOF while parsing',
429 ["f'{lambda x:x}'",
430 ])
431
432 def test_yield(self):
433 # Not terribly useful, but make sure the yield turns
434 # a function into a generator
435 def fn(y):
436 f'y:{yield y*2}'
437
438 g = fn(4)
439 self.assertEqual(next(g), 8)
440
441 def test_yield_send(self):
442 def fn(x):
443 yield f'x:{yield (lambda i: x * i)}'
444
445 g = fn(10)
446 the_lambda = next(g)
447 self.assertEqual(the_lambda(4), 40)
448 self.assertEqual(g.send('string'), 'x:string')
449
450 def test_expressions_with_triple_quoted_strings(self):
451 self.assertEqual(f"{'''x'''}", 'x')
452 self.assertEqual(f"{'''eric's'''}", "eric's")
Eric V. Smith235a6f02015-09-19 14:51:32 -0400453
454 # Test concatenation within an expression
455 self.assertEqual(f'{"x" """eric"s""" "y"}', 'xeric"sy')
456 self.assertEqual(f'{"x" """eric"s"""}', 'xeric"s')
457 self.assertEqual(f'{"""eric"s""" "y"}', 'eric"sy')
458 self.assertEqual(f'{"""x""" """eric"s""" "y"}', 'xeric"sy')
459 self.assertEqual(f'{"""x""" """eric"s""" """y"""}', 'xeric"sy')
460 self.assertEqual(f'{r"""x""" """eric"s""" """y"""}', 'xeric"sy')
461
462 def test_multiple_vars(self):
463 x = 98
464 y = 'abc'
465 self.assertEqual(f'{x}{y}', '98abc')
466
467 self.assertEqual(f'X{x}{y}', 'X98abc')
468 self.assertEqual(f'{x}X{y}', '98Xabc')
469 self.assertEqual(f'{x}{y}X', '98abcX')
470
471 self.assertEqual(f'X{x}Y{y}', 'X98Yabc')
472 self.assertEqual(f'X{x}{y}Y', 'X98abcY')
473 self.assertEqual(f'{x}X{y}Y', '98XabcY')
474
475 self.assertEqual(f'X{x}Y{y}Z', 'X98YabcZ')
476
477 def test_closure(self):
478 def outer(x):
479 def inner():
480 return f'x:{x}'
481 return inner
482
483 self.assertEqual(outer('987')(), 'x:987')
484 self.assertEqual(outer(7)(), 'x:7')
485
486 def test_arguments(self):
487 y = 2
488 def f(x, width):
489 return f'x={x*y:{width}}'
490
491 self.assertEqual(f('foo', 10), 'x=foofoo ')
492 x = 'bar'
493 self.assertEqual(f(10, 10), 'x= 20')
494
495 def test_locals(self):
496 value = 123
497 self.assertEqual(f'v:{value}', 'v:123')
498
499 def test_missing_variable(self):
500 with self.assertRaises(NameError):
501 f'v:{value}'
502
503 def test_missing_format_spec(self):
504 class O:
505 def __format__(self, spec):
506 if not spec:
507 return '*'
508 return spec
509
510 self.assertEqual(f'{O():x}', 'x')
511 self.assertEqual(f'{O()}', '*')
512 self.assertEqual(f'{O():}', '*')
513
514 self.assertEqual(f'{3:}', '3')
515 self.assertEqual(f'{3!s:}', '3')
516
517 def test_global(self):
518 self.assertEqual(f'g:{a_global}', 'g:global variable')
519 self.assertEqual(f'g:{a_global!r}', "g:'global variable'")
520
521 a_local = 'local variable'
522 self.assertEqual(f'g:{a_global} l:{a_local}',
523 'g:global variable l:local variable')
524 self.assertEqual(f'g:{a_global!r}',
525 "g:'global variable'")
526 self.assertEqual(f'g:{a_global} l:{a_local!r}',
527 "g:global variable l:'local variable'")
528
529 self.assertIn("module 'unittest' from", f'{unittest}')
530
531 def test_shadowed_global(self):
532 a_global = 'really a local'
533 self.assertEqual(f'g:{a_global}', 'g:really a local')
534 self.assertEqual(f'g:{a_global!r}', "g:'really a local'")
535
536 a_local = 'local variable'
537 self.assertEqual(f'g:{a_global} l:{a_local}',
538 'g:really a local l:local variable')
539 self.assertEqual(f'g:{a_global!r}',
540 "g:'really a local'")
541 self.assertEqual(f'g:{a_global} l:{a_local!r}',
542 "g:really a local l:'local variable'")
543
544 def test_call(self):
545 def foo(x):
546 return 'x=' + str(x)
547
548 self.assertEqual(f'{foo(10)}', 'x=10')
549
550 def test_nested_fstrings(self):
551 y = 5
552 self.assertEqual(f'{f"{0}"*3}', '000')
553 self.assertEqual(f'{f"{y}"*3}', '555')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400554
555 def test_invalid_string_prefixes(self):
556 self.assertAllRaise(SyntaxError, 'unexpected EOF while parsing',
557 ["fu''",
558 "uf''",
559 "Fu''",
560 "fU''",
561 "Uf''",
562 "uF''",
563 "ufr''",
564 "urf''",
565 "fur''",
566 "fru''",
567 "rfu''",
568 "ruf''",
569 "FUR''",
570 "Fur''",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400571 "fb''",
572 "fB''",
573 "Fb''",
574 "FB''",
575 "bf''",
576 "bF''",
577 "Bf''",
578 "BF''",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400579 ])
580
581 def test_leading_trailing_spaces(self):
582 self.assertEqual(f'{ 3}', '3')
583 self.assertEqual(f'{ 3}', '3')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400584 self.assertEqual(f'{3 }', '3')
585 self.assertEqual(f'{3 }', '3')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400586
587 self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]}}',
588 'expr={1: 2}')
589 self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]} }',
590 'expr={1: 2}')
591
Eric V. Smith235a6f02015-09-19 14:51:32 -0400592 def test_not_equal(self):
593 # There's a special test for this because there's a special
594 # case in the f-string parser to look for != as not ending an
595 # expression. Normally it would, while looking for !s or !r.
596
597 self.assertEqual(f'{3!=4}', 'True')
598 self.assertEqual(f'{3!=4:}', 'True')
599 self.assertEqual(f'{3!=4!s}', 'True')
600 self.assertEqual(f'{3!=4!s:.3}', 'Tru')
601
602 def test_conversions(self):
603 self.assertEqual(f'{3.14:10.10}', ' 3.14')
604 self.assertEqual(f'{3.14!s:10.10}', '3.14 ')
605 self.assertEqual(f'{3.14!r:10.10}', '3.14 ')
606 self.assertEqual(f'{3.14!a:10.10}', '3.14 ')
607
608 self.assertEqual(f'{"a"}', 'a')
609 self.assertEqual(f'{"a"!r}', "'a'")
610 self.assertEqual(f'{"a"!a}', "'a'")
611
612 # Not a conversion.
613 self.assertEqual(f'{"a!r"}', "a!r")
614
615 # Not a conversion, but show that ! is allowed in a format spec.
616 self.assertEqual(f'{3.14:!<10.10}', '3.14!!!!!!')
617
Eric V. Smith235a6f02015-09-19 14:51:32 -0400618 self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character',
619 ["f'{3!g}'",
620 "f'{3!A}'",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400621 "f'{3!3}'",
622 "f'{3!G}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400623 "f'{3!!}'",
624 "f'{3!:}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400625 "f'{3! s}'", # no space before conversion char
Eric V. Smith235a6f02015-09-19 14:51:32 -0400626 ])
627
628 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
629 ["f'{x!s{y}}'",
630 "f'{3!ss}'",
631 "f'{3!ss:}'",
632 "f'{3!ss:s}'",
633 ])
634
635 def test_assignment(self):
636 self.assertAllRaise(SyntaxError, 'invalid syntax',
637 ["f'' = 3",
638 "f'{0}' = x",
639 "f'{x}' = x",
640 ])
641
642 def test_del(self):
643 self.assertAllRaise(SyntaxError, 'invalid syntax',
644 ["del f''",
645 "del '' f''",
646 ])
647
648 def test_mismatched_braces(self):
649 self.assertAllRaise(SyntaxError, "f-string: single '}' is not allowed",
650 ["f'{{}'",
651 "f'{{}}}'",
652 "f'}'",
653 "f'x}'",
654 "f'x}x'",
Jason R. Coombsda25abf72016-11-06 11:14:48 -0500655 r"f'\u007b}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400656
657 # Can't have { or } in a format spec.
658 "f'{3:}>10}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400659 "f'{3:}}>10}'",
660 ])
661
662 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
663 ["f'{3:{{>10}'",
664 "f'{3'",
665 "f'{3!'",
666 "f'{3:'",
667 "f'{3!s'",
668 "f'{3!s:'",
669 "f'{3!s:3'",
670 "f'x{'",
671 "f'x{x'",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400672 "f'{x'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400673 "f'{3:s'",
674 "f'{{{'",
675 "f'{{}}{'",
676 "f'{'",
677 ])
678
Eric V. Smith235a6f02015-09-19 14:51:32 -0400679 # But these are just normal strings.
680 self.assertEqual(f'{"{"}', '{')
681 self.assertEqual(f'{"}"}', '}')
682 self.assertEqual(f'{3:{"}"}>10}', '}}}}}}}}}3')
683 self.assertEqual(f'{2:{"{"}>10}', '{{{{{{{{{2')
684
685 def test_if_conditional(self):
686 # There's special logic in compile.c to test if the
687 # conditional for an if (and while) are constants. Exercise
688 # that code.
689
690 def test_fstring(x, expected):
691 flag = 0
692 if f'{x}':
693 flag = 1
694 else:
695 flag = 2
696 self.assertEqual(flag, expected)
697
698 def test_concat_empty(x, expected):
699 flag = 0
700 if '' f'{x}':
701 flag = 1
702 else:
703 flag = 2
704 self.assertEqual(flag, expected)
705
706 def test_concat_non_empty(x, expected):
707 flag = 0
708 if ' ' f'{x}':
709 flag = 1
710 else:
711 flag = 2
712 self.assertEqual(flag, expected)
713
714 test_fstring('', 2)
715 test_fstring(' ', 1)
716
717 test_concat_empty('', 2)
718 test_concat_empty(' ', 1)
719
720 test_concat_non_empty('', 1)
721 test_concat_non_empty(' ', 1)
722
723 def test_empty_format_specifier(self):
724 x = 'test'
725 self.assertEqual(f'{x}', 'test')
726 self.assertEqual(f'{x:}', 'test')
727 self.assertEqual(f'{x!s:}', 'test')
728 self.assertEqual(f'{x!r:}', "'test'")
729
730 def test_str_format_differences(self):
731 d = {'a': 'string',
732 0: 'integer',
733 }
734 a = 0
735 self.assertEqual(f'{d[0]}', 'integer')
736 self.assertEqual(f'{d["a"]}', 'string')
737 self.assertEqual(f'{d[a]}', 'integer')
738 self.assertEqual('{d[a]}'.format(d=d), 'string')
739 self.assertEqual('{d[0]}'.format(d=d), 'integer')
740
741 def test_invalid_expressions(self):
742 self.assertAllRaise(SyntaxError, 'invalid syntax',
743 [r"f'{a[4)}'",
744 r"f'{a(4]}'",
745 ])
746
Eric V. Smith135d5f42016-02-05 18:23:08 -0500747 def test_errors(self):
748 # see issue 26287
Serhiy Storchaka13c8f322016-10-31 08:13:00 +0200749 self.assertAllRaise(TypeError, 'unsupported',
Eric V. Smith135d5f42016-02-05 18:23:08 -0500750 [r"f'{(lambda: 0):x}'",
751 r"f'{(0,):x}'",
752 ])
753 self.assertAllRaise(ValueError, 'Unknown format code',
754 [r"f'{1000:j}'",
755 r"f'{1000:j}'",
756 ])
757
Eric V. Smith235a6f02015-09-19 14:51:32 -0400758 def test_loop(self):
759 for i in range(1000):
760 self.assertEqual(f'i:{i}', 'i:' + str(i))
761
762 def test_dict(self):
763 d = {'"': 'dquote',
764 "'": 'squote',
765 'foo': 'bar',
766 }
Eric V. Smith235a6f02015-09-19 14:51:32 -0400767 self.assertEqual(f'''{d["'"]}''', 'squote')
768 self.assertEqual(f"""{d['"']}""", 'dquote')
769
770 self.assertEqual(f'{d["foo"]}', 'bar')
771 self.assertEqual(f"{d['foo']}", 'bar')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400772
773if __name__ == '__main__':
774 unittest.main()