blob: 3d762b5aa9bf6385eeec213f11c09f0d065337ee [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
Serhiy Storchaka570b1c92017-06-09 00:38:06 +0300283 # The Python parser ignores also the following
284 # whitespace characters in additional to a space.
285 "f'''{\t\f\r\n}'''",
286
Eric V. Smith1d44c412015-09-23 07:49:00 -0400287 # Catch the empty expression before the
288 # invalid conversion.
289 "f'{!x}'",
290 "f'{ !xr}'",
291 "f'{!x:}'",
292 "f'{!x:a}'",
293 "f'{ !xr:}'",
294 "f'{ !xr:a}'",
Eric V. Smith548c4d32015-09-23 08:00:01 -0400295
296 "f'{!}'",
297 "f'{:}'",
Eric V. Smithb2080f62015-09-23 10:24:43 -0400298
299 # We find the empty expression before the
300 # missing closing brace.
301 "f'{!'",
302 "f'{!s:'",
303 "f'{:'",
304 "f'{:x'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400305 ])
306
Serhiy Storchaka570b1c92017-06-09 00:38:06 +0300307 # Different error message is raised for other whitespace characters.
308 self.assertAllRaise(SyntaxError, 'invalid character in identifier',
309 ["f'''{\xa0}'''",
310 "\xa0",
311 ])
312
Eric V. Smith235a6f02015-09-19 14:51:32 -0400313 def test_parens_in_expressions(self):
314 self.assertEqual(f'{3,}', '(3,)')
315
316 # Add these because when an expression is evaluated, parens
317 # are added around it. But we shouldn't go from an invalid
318 # expression to a valid one. The added parens are just
319 # supposed to allow whitespace (including newlines).
320 self.assertAllRaise(SyntaxError, 'invalid syntax',
321 ["f'{,}'",
322 "f'{,}'", # this is (,), which is an error
323 ])
324
325 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
326 ["f'{3)+(4}'",
327 ])
328
329 self.assertAllRaise(SyntaxError, 'EOL while scanning string literal',
330 ["f'{\n}'",
331 ])
332
Eric V. Smith451d0e32016-09-09 21:56:20 -0400333 def test_backslashes_in_string_part(self):
334 self.assertEqual(f'\t', '\t')
335 self.assertEqual(r'\t', '\\t')
336 self.assertEqual(rf'\t', '\\t')
337 self.assertEqual(f'{2}\t', '2\t')
338 self.assertEqual(f'{2}\t{3}', '2\t3')
339 self.assertEqual(f'\t{3}', '\t3')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400340
Eric V. Smith451d0e32016-09-09 21:56:20 -0400341 self.assertEqual(f'\u0394', '\u0394')
342 self.assertEqual(r'\u0394', '\\u0394')
343 self.assertEqual(rf'\u0394', '\\u0394')
344 self.assertEqual(f'{2}\u0394', '2\u0394')
345 self.assertEqual(f'{2}\u0394{3}', '2\u03943')
346 self.assertEqual(f'\u0394{3}', '\u03943')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400347
Eric V. Smith451d0e32016-09-09 21:56:20 -0400348 self.assertEqual(f'\U00000394', '\u0394')
349 self.assertEqual(r'\U00000394', '\\U00000394')
350 self.assertEqual(rf'\U00000394', '\\U00000394')
351 self.assertEqual(f'{2}\U00000394', '2\u0394')
352 self.assertEqual(f'{2}\U00000394{3}', '2\u03943')
353 self.assertEqual(f'\U00000394{3}', '\u03943')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400354
Eric V. Smith451d0e32016-09-09 21:56:20 -0400355 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}', '\u0394')
356 self.assertEqual(f'{2}\N{GREEK CAPITAL LETTER DELTA}', '2\u0394')
357 self.assertEqual(f'{2}\N{GREEK CAPITAL LETTER DELTA}{3}', '2\u03943')
358 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}{3}', '\u03943')
359 self.assertEqual(f'2\N{GREEK CAPITAL LETTER DELTA}', '2\u0394')
360 self.assertEqual(f'2\N{GREEK CAPITAL LETTER DELTA}3', '2\u03943')
361 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}3', '\u03943')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400362
Eric V. Smith451d0e32016-09-09 21:56:20 -0400363 self.assertEqual(f'\x20', ' ')
364 self.assertEqual(r'\x20', '\\x20')
365 self.assertEqual(rf'\x20', '\\x20')
366 self.assertEqual(f'{2}\x20', '2 ')
367 self.assertEqual(f'{2}\x20{3}', '2 3')
368 self.assertEqual(f'\x20{3}', ' 3')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400369
Eric V. Smith451d0e32016-09-09 21:56:20 -0400370 self.assertEqual(f'2\x20', '2 ')
371 self.assertEqual(f'2\x203', '2 3')
372 self.assertEqual(f'\x203', ' 3')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400373
Serhiy Storchaka89a31022017-05-25 14:18:55 +0300374 with self.assertWarns(DeprecationWarning): # invalid escape sequence
375 value = eval(r"f'\{6*7}'")
376 self.assertEqual(value, '\\42')
377 self.assertEqual(f'\\{6*7}', '\\42')
378 self.assertEqual(fr'\{6*7}', '\\42')
379
380 AMPERSAND = 'spam'
381 # Get the right unicode character (&), or pick up local variable
382 # depending on the number of backslashes.
383 self.assertEqual(f'\N{AMPERSAND}', '&')
384 self.assertEqual(f'\\N{AMPERSAND}', '\\Nspam')
385 self.assertEqual(fr'\N{AMPERSAND}', '\\Nspam')
386 self.assertEqual(f'\\\N{AMPERSAND}', '\\&')
387
Eric V. Smith451d0e32016-09-09 21:56:20 -0400388 def test_misformed_unicode_character_name(self):
389 # These test are needed because unicode names are parsed
390 # differently inside f-strings.
391 self.assertAllRaise(SyntaxError, r"\(unicode error\) 'unicodeescape' codec can't decode bytes in position .*: malformed \\N character escape",
392 [r"f'\N'",
393 r"f'\N{'",
394 r"f'\N{GREEK CAPITAL LETTER DELTA'",
395
396 # Here are the non-f-string versions,
397 # which should give the same errors.
398 r"'\N'",
399 r"'\N{'",
400 r"'\N{GREEK CAPITAL LETTER DELTA'",
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400401 ])
402
Eric V. Smith451d0e32016-09-09 21:56:20 -0400403 def test_no_backslashes_in_expression_part(self):
404 self.assertAllRaise(SyntaxError, 'f-string expression part cannot include a backslash',
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400405 [r"f'{\'a\'}'",
406 r"f'{\t3}'",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400407 r"f'{\}'",
408 r"rf'{\'a\'}'",
409 r"rf'{\t3}'",
410 r"rf'{\}'",
411 r"""rf'{"\N{LEFT CURLY BRACKET}"}'""",
Jason R. Coombs45cab8c2016-11-06 11:01:08 -0500412 r"f'{\n}'",
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400413 ])
414
Eric V. Smith451d0e32016-09-09 21:56:20 -0400415 def test_no_escapes_for_braces(self):
Jason R. Coombs1c92a762016-11-06 11:25:54 -0500416 """
417 Only literal curly braces begin an expression.
418 """
419 # \x7b is '{'.
420 self.assertEqual(f'\x7b1+1}}', '{1+1}')
421 self.assertEqual(f'\x7b1+1', '{1+1')
422 self.assertEqual(f'\u007b1+1', '{1+1')
423 self.assertEqual(f'\N{LEFT CURLY BRACKET}1+1\N{RIGHT CURLY BRACKET}', '{1+1}')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400424
Eric V. Smith235a6f02015-09-19 14:51:32 -0400425 def test_newlines_in_expressions(self):
426 self.assertEqual(f'{0}', '0')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400427 self.assertEqual(rf'''{3+
4284}''', '7')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400429
430 def test_lambda(self):
431 x = 5
432 self.assertEqual(f'{(lambda y:x*y)("8")!r}', "'88888'")
433 self.assertEqual(f'{(lambda y:x*y)("8")!r:10}', "'88888' ")
434 self.assertEqual(f'{(lambda y:x*y)("8"):10}', "88888 ")
435
436 # lambda doesn't work without parens, because the colon
437 # makes the parser think it's a format_spec
438 self.assertAllRaise(SyntaxError, 'unexpected EOF while parsing',
439 ["f'{lambda x:x}'",
440 ])
441
442 def test_yield(self):
443 # Not terribly useful, but make sure the yield turns
444 # a function into a generator
445 def fn(y):
446 f'y:{yield y*2}'
447
448 g = fn(4)
449 self.assertEqual(next(g), 8)
450
451 def test_yield_send(self):
452 def fn(x):
453 yield f'x:{yield (lambda i: x * i)}'
454
455 g = fn(10)
456 the_lambda = next(g)
457 self.assertEqual(the_lambda(4), 40)
458 self.assertEqual(g.send('string'), 'x:string')
459
460 def test_expressions_with_triple_quoted_strings(self):
461 self.assertEqual(f"{'''x'''}", 'x')
462 self.assertEqual(f"{'''eric's'''}", "eric's")
Eric V. Smith235a6f02015-09-19 14:51:32 -0400463
464 # Test concatenation within an expression
465 self.assertEqual(f'{"x" """eric"s""" "y"}', 'xeric"sy')
466 self.assertEqual(f'{"x" """eric"s"""}', 'xeric"s')
467 self.assertEqual(f'{"""eric"s""" "y"}', 'eric"sy')
468 self.assertEqual(f'{"""x""" """eric"s""" "y"}', 'xeric"sy')
469 self.assertEqual(f'{"""x""" """eric"s""" """y"""}', 'xeric"sy')
470 self.assertEqual(f'{r"""x""" """eric"s""" """y"""}', 'xeric"sy')
471
472 def test_multiple_vars(self):
473 x = 98
474 y = 'abc'
475 self.assertEqual(f'{x}{y}', '98abc')
476
477 self.assertEqual(f'X{x}{y}', 'X98abc')
478 self.assertEqual(f'{x}X{y}', '98Xabc')
479 self.assertEqual(f'{x}{y}X', '98abcX')
480
481 self.assertEqual(f'X{x}Y{y}', 'X98Yabc')
482 self.assertEqual(f'X{x}{y}Y', 'X98abcY')
483 self.assertEqual(f'{x}X{y}Y', '98XabcY')
484
485 self.assertEqual(f'X{x}Y{y}Z', 'X98YabcZ')
486
487 def test_closure(self):
488 def outer(x):
489 def inner():
490 return f'x:{x}'
491 return inner
492
493 self.assertEqual(outer('987')(), 'x:987')
494 self.assertEqual(outer(7)(), 'x:7')
495
496 def test_arguments(self):
497 y = 2
498 def f(x, width):
499 return f'x={x*y:{width}}'
500
501 self.assertEqual(f('foo', 10), 'x=foofoo ')
502 x = 'bar'
503 self.assertEqual(f(10, 10), 'x= 20')
504
505 def test_locals(self):
506 value = 123
507 self.assertEqual(f'v:{value}', 'v:123')
508
509 def test_missing_variable(self):
510 with self.assertRaises(NameError):
511 f'v:{value}'
512
513 def test_missing_format_spec(self):
514 class O:
515 def __format__(self, spec):
516 if not spec:
517 return '*'
518 return spec
519
520 self.assertEqual(f'{O():x}', 'x')
521 self.assertEqual(f'{O()}', '*')
522 self.assertEqual(f'{O():}', '*')
523
524 self.assertEqual(f'{3:}', '3')
525 self.assertEqual(f'{3!s:}', '3')
526
527 def test_global(self):
528 self.assertEqual(f'g:{a_global}', 'g:global variable')
529 self.assertEqual(f'g:{a_global!r}', "g:'global variable'")
530
531 a_local = 'local variable'
532 self.assertEqual(f'g:{a_global} l:{a_local}',
533 'g:global variable l:local variable')
534 self.assertEqual(f'g:{a_global!r}',
535 "g:'global variable'")
536 self.assertEqual(f'g:{a_global} l:{a_local!r}',
537 "g:global variable l:'local variable'")
538
539 self.assertIn("module 'unittest' from", f'{unittest}')
540
541 def test_shadowed_global(self):
542 a_global = 'really a local'
543 self.assertEqual(f'g:{a_global}', 'g:really a local')
544 self.assertEqual(f'g:{a_global!r}', "g:'really a local'")
545
546 a_local = 'local variable'
547 self.assertEqual(f'g:{a_global} l:{a_local}',
548 'g:really a local l:local variable')
549 self.assertEqual(f'g:{a_global!r}',
550 "g:'really a local'")
551 self.assertEqual(f'g:{a_global} l:{a_local!r}',
552 "g:really a local l:'local variable'")
553
554 def test_call(self):
555 def foo(x):
556 return 'x=' + str(x)
557
558 self.assertEqual(f'{foo(10)}', 'x=10')
559
560 def test_nested_fstrings(self):
561 y = 5
562 self.assertEqual(f'{f"{0}"*3}', '000')
563 self.assertEqual(f'{f"{y}"*3}', '555')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400564
565 def test_invalid_string_prefixes(self):
566 self.assertAllRaise(SyntaxError, 'unexpected EOF while parsing',
567 ["fu''",
568 "uf''",
569 "Fu''",
570 "fU''",
571 "Uf''",
572 "uF''",
573 "ufr''",
574 "urf''",
575 "fur''",
576 "fru''",
577 "rfu''",
578 "ruf''",
579 "FUR''",
580 "Fur''",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400581 "fb''",
582 "fB''",
583 "Fb''",
584 "FB''",
585 "bf''",
586 "bF''",
587 "Bf''",
588 "BF''",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400589 ])
590
591 def test_leading_trailing_spaces(self):
592 self.assertEqual(f'{ 3}', '3')
593 self.assertEqual(f'{ 3}', '3')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400594 self.assertEqual(f'{3 }', '3')
595 self.assertEqual(f'{3 }', '3')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400596
597 self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]}}',
598 'expr={1: 2}')
599 self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]} }',
600 'expr={1: 2}')
601
Eric V. Smith235a6f02015-09-19 14:51:32 -0400602 def test_not_equal(self):
603 # There's a special test for this because there's a special
604 # case in the f-string parser to look for != as not ending an
605 # expression. Normally it would, while looking for !s or !r.
606
607 self.assertEqual(f'{3!=4}', 'True')
608 self.assertEqual(f'{3!=4:}', 'True')
609 self.assertEqual(f'{3!=4!s}', 'True')
610 self.assertEqual(f'{3!=4!s:.3}', 'Tru')
611
612 def test_conversions(self):
613 self.assertEqual(f'{3.14:10.10}', ' 3.14')
614 self.assertEqual(f'{3.14!s:10.10}', '3.14 ')
615 self.assertEqual(f'{3.14!r:10.10}', '3.14 ')
616 self.assertEqual(f'{3.14!a:10.10}', '3.14 ')
617
618 self.assertEqual(f'{"a"}', 'a')
619 self.assertEqual(f'{"a"!r}', "'a'")
620 self.assertEqual(f'{"a"!a}', "'a'")
621
622 # Not a conversion.
623 self.assertEqual(f'{"a!r"}', "a!r")
624
625 # Not a conversion, but show that ! is allowed in a format spec.
626 self.assertEqual(f'{3.14:!<10.10}', '3.14!!!!!!')
627
Eric V. Smith235a6f02015-09-19 14:51:32 -0400628 self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character',
629 ["f'{3!g}'",
630 "f'{3!A}'",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400631 "f'{3!3}'",
632 "f'{3!G}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400633 "f'{3!!}'",
634 "f'{3!:}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400635 "f'{3! s}'", # no space before conversion char
Eric V. Smith235a6f02015-09-19 14:51:32 -0400636 ])
637
638 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
639 ["f'{x!s{y}}'",
640 "f'{3!ss}'",
641 "f'{3!ss:}'",
642 "f'{3!ss:s}'",
643 ])
644
645 def test_assignment(self):
646 self.assertAllRaise(SyntaxError, 'invalid syntax',
647 ["f'' = 3",
648 "f'{0}' = x",
649 "f'{x}' = x",
650 ])
651
652 def test_del(self):
653 self.assertAllRaise(SyntaxError, 'invalid syntax',
654 ["del f''",
655 "del '' f''",
656 ])
657
658 def test_mismatched_braces(self):
659 self.assertAllRaise(SyntaxError, "f-string: single '}' is not allowed",
660 ["f'{{}'",
661 "f'{{}}}'",
662 "f'}'",
663 "f'x}'",
664 "f'x}x'",
Jason R. Coombsda25abf72016-11-06 11:14:48 -0500665 r"f'\u007b}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400666
667 # Can't have { or } in a format spec.
668 "f'{3:}>10}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400669 "f'{3:}}>10}'",
670 ])
671
672 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
673 ["f'{3:{{>10}'",
674 "f'{3'",
675 "f'{3!'",
676 "f'{3:'",
677 "f'{3!s'",
678 "f'{3!s:'",
679 "f'{3!s:3'",
680 "f'x{'",
681 "f'x{x'",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400682 "f'{x'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400683 "f'{3:s'",
684 "f'{{{'",
685 "f'{{}}{'",
686 "f'{'",
687 ])
688
Eric V. Smith235a6f02015-09-19 14:51:32 -0400689 # But these are just normal strings.
690 self.assertEqual(f'{"{"}', '{')
691 self.assertEqual(f'{"}"}', '}')
692 self.assertEqual(f'{3:{"}"}>10}', '}}}}}}}}}3')
693 self.assertEqual(f'{2:{"{"}>10}', '{{{{{{{{{2')
694
695 def test_if_conditional(self):
696 # There's special logic in compile.c to test if the
697 # conditional for an if (and while) are constants. Exercise
698 # that code.
699
700 def test_fstring(x, expected):
701 flag = 0
702 if f'{x}':
703 flag = 1
704 else:
705 flag = 2
706 self.assertEqual(flag, expected)
707
708 def test_concat_empty(x, expected):
709 flag = 0
710 if '' f'{x}':
711 flag = 1
712 else:
713 flag = 2
714 self.assertEqual(flag, expected)
715
716 def test_concat_non_empty(x, expected):
717 flag = 0
718 if ' ' f'{x}':
719 flag = 1
720 else:
721 flag = 2
722 self.assertEqual(flag, expected)
723
724 test_fstring('', 2)
725 test_fstring(' ', 1)
726
727 test_concat_empty('', 2)
728 test_concat_empty(' ', 1)
729
730 test_concat_non_empty('', 1)
731 test_concat_non_empty(' ', 1)
732
733 def test_empty_format_specifier(self):
734 x = 'test'
735 self.assertEqual(f'{x}', 'test')
736 self.assertEqual(f'{x:}', 'test')
737 self.assertEqual(f'{x!s:}', 'test')
738 self.assertEqual(f'{x!r:}', "'test'")
739
740 def test_str_format_differences(self):
741 d = {'a': 'string',
742 0: 'integer',
743 }
744 a = 0
745 self.assertEqual(f'{d[0]}', 'integer')
746 self.assertEqual(f'{d["a"]}', 'string')
747 self.assertEqual(f'{d[a]}', 'integer')
748 self.assertEqual('{d[a]}'.format(d=d), 'string')
749 self.assertEqual('{d[0]}'.format(d=d), 'integer')
750
751 def test_invalid_expressions(self):
752 self.assertAllRaise(SyntaxError, 'invalid syntax',
753 [r"f'{a[4)}'",
754 r"f'{a(4]}'",
755 ])
756
Eric V. Smith135d5f42016-02-05 18:23:08 -0500757 def test_errors(self):
758 # see issue 26287
Serhiy Storchaka13c8f322016-10-31 08:13:00 +0200759 self.assertAllRaise(TypeError, 'unsupported',
Eric V. Smith135d5f42016-02-05 18:23:08 -0500760 [r"f'{(lambda: 0):x}'",
761 r"f'{(0,):x}'",
762 ])
763 self.assertAllRaise(ValueError, 'Unknown format code',
764 [r"f'{1000:j}'",
765 r"f'{1000:j}'",
766 ])
767
Eric V. Smith235a6f02015-09-19 14:51:32 -0400768 def test_loop(self):
769 for i in range(1000):
770 self.assertEqual(f'i:{i}', 'i:' + str(i))
771
772 def test_dict(self):
773 d = {'"': 'dquote',
774 "'": 'squote',
775 'foo': 'bar',
776 }
Eric V. Smith235a6f02015-09-19 14:51:32 -0400777 self.assertEqual(f'''{d["'"]}''', 'squote')
778 self.assertEqual(f"""{d['"']}""", 'dquote')
779
780 self.assertEqual(f'{d["foo"]}', 'bar')
781 self.assertEqual(f"{d['foo']}", 'bar')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400782
Serhiy Storchaka2eca5b42017-06-16 16:29:42 +0300783 def test_backslash_char(self):
784 # Check eval of a backslash followed by a control char.
785 # See bpo-30682: this used to raise an assert in pydebug mode.
786 self.assertEqual(eval('f"\\\n"'), '')
787 self.assertEqual(eval('f"\\\r"'), '')
788
Eric V. Smith235a6f02015-09-19 14:51:32 -0400789if __name__ == '__main__':
790 unittest.main()