blob: fec72e008e5bc7671f4f4594ea9b1b3714f8b209 [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
Łukasz Langae7c566c2017-09-06 17:27:58 -070073 def test_ast_line_numbers(self):
74 expr = """
75a = 10
76f'{a * x()}'"""
77 t = ast.parse(expr)
78 self.assertEqual(type(t), ast.Module)
79 self.assertEqual(len(t.body), 2)
80 # check `a = 10`
81 self.assertEqual(type(t.body[0]), ast.Assign)
82 self.assertEqual(t.body[0].lineno, 2)
83 # check `f'...'`
84 self.assertEqual(type(t.body[1]), ast.Expr)
85 self.assertEqual(type(t.body[1].value), ast.JoinedStr)
86 self.assertEqual(len(t.body[1].value.values), 1)
87 self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)
88 self.assertEqual(t.body[1].lineno, 3)
89 self.assertEqual(t.body[1].value.lineno, 3)
90 self.assertEqual(t.body[1].value.values[0].lineno, 3)
91 # check the binop location
92 binop = t.body[1].value.values[0].value
93 self.assertEqual(type(binop), ast.BinOp)
94 self.assertEqual(type(binop.left), ast.Name)
95 self.assertEqual(type(binop.op), ast.Mult)
96 self.assertEqual(type(binop.right), ast.Call)
97 self.assertEqual(binop.lineno, 3)
98 self.assertEqual(binop.left.lineno, 3)
99 self.assertEqual(binop.right.lineno, 3)
100 self.assertEqual(binop.col_offset, 3)
101 self.assertEqual(binop.left.col_offset, 3)
102 self.assertEqual(binop.right.col_offset, 7)
103
104 def test_ast_line_numbers_multiple_formattedvalues(self):
105 expr = """
106f'no formatted values'
107f'eggs {a * x()} spam {b + y()}'"""
108 t = ast.parse(expr)
109 self.assertEqual(type(t), ast.Module)
110 self.assertEqual(len(t.body), 2)
111 # check `f'no formatted value'`
112 self.assertEqual(type(t.body[0]), ast.Expr)
113 self.assertEqual(type(t.body[0].value), ast.JoinedStr)
114 self.assertEqual(t.body[0].lineno, 2)
115 # check `f'...'`
116 self.assertEqual(type(t.body[1]), ast.Expr)
117 self.assertEqual(type(t.body[1].value), ast.JoinedStr)
118 self.assertEqual(len(t.body[1].value.values), 4)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300119 self.assertEqual(type(t.body[1].value.values[0]), ast.Constant)
120 self.assertEqual(type(t.body[1].value.values[0].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700121 self.assertEqual(type(t.body[1].value.values[1]), ast.FormattedValue)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300122 self.assertEqual(type(t.body[1].value.values[2]), ast.Constant)
123 self.assertEqual(type(t.body[1].value.values[2].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700124 self.assertEqual(type(t.body[1].value.values[3]), ast.FormattedValue)
125 self.assertEqual(t.body[1].lineno, 3)
126 self.assertEqual(t.body[1].value.lineno, 3)
127 self.assertEqual(t.body[1].value.values[0].lineno, 3)
128 self.assertEqual(t.body[1].value.values[1].lineno, 3)
129 self.assertEqual(t.body[1].value.values[2].lineno, 3)
130 self.assertEqual(t.body[1].value.values[3].lineno, 3)
131 # check the first binop location
132 binop1 = t.body[1].value.values[1].value
133 self.assertEqual(type(binop1), ast.BinOp)
134 self.assertEqual(type(binop1.left), ast.Name)
135 self.assertEqual(type(binop1.op), ast.Mult)
136 self.assertEqual(type(binop1.right), ast.Call)
137 self.assertEqual(binop1.lineno, 3)
138 self.assertEqual(binop1.left.lineno, 3)
139 self.assertEqual(binop1.right.lineno, 3)
140 self.assertEqual(binop1.col_offset, 8)
141 self.assertEqual(binop1.left.col_offset, 8)
142 self.assertEqual(binop1.right.col_offset, 12)
143 # check the second binop location
144 binop2 = t.body[1].value.values[3].value
145 self.assertEqual(type(binop2), ast.BinOp)
146 self.assertEqual(type(binop2.left), ast.Name)
147 self.assertEqual(type(binop2.op), ast.Add)
148 self.assertEqual(type(binop2.right), ast.Call)
149 self.assertEqual(binop2.lineno, 3)
150 self.assertEqual(binop2.left.lineno, 3)
151 self.assertEqual(binop2.right.lineno, 3)
152 self.assertEqual(binop2.col_offset, 23)
153 self.assertEqual(binop2.left.col_offset, 23)
154 self.assertEqual(binop2.right.col_offset, 27)
155
156 def test_ast_line_numbers_nested(self):
157 expr = """
158a = 10
159f'{a * f"-{x()}-"}'"""
160 t = ast.parse(expr)
161 self.assertEqual(type(t), ast.Module)
162 self.assertEqual(len(t.body), 2)
163 # check `a = 10`
164 self.assertEqual(type(t.body[0]), ast.Assign)
165 self.assertEqual(t.body[0].lineno, 2)
166 # check `f'...'`
167 self.assertEqual(type(t.body[1]), ast.Expr)
168 self.assertEqual(type(t.body[1].value), ast.JoinedStr)
169 self.assertEqual(len(t.body[1].value.values), 1)
170 self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)
171 self.assertEqual(t.body[1].lineno, 3)
172 self.assertEqual(t.body[1].value.lineno, 3)
173 self.assertEqual(t.body[1].value.values[0].lineno, 3)
174 # check the binop location
175 binop = t.body[1].value.values[0].value
176 self.assertEqual(type(binop), ast.BinOp)
177 self.assertEqual(type(binop.left), ast.Name)
178 self.assertEqual(type(binop.op), ast.Mult)
179 self.assertEqual(type(binop.right), ast.JoinedStr)
180 self.assertEqual(binop.lineno, 3)
181 self.assertEqual(binop.left.lineno, 3)
182 self.assertEqual(binop.right.lineno, 3)
183 self.assertEqual(binop.col_offset, 3)
184 self.assertEqual(binop.left.col_offset, 3)
185 self.assertEqual(binop.right.col_offset, 7)
186 # check the nested call location
187 self.assertEqual(len(binop.right.values), 3)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300188 self.assertEqual(type(binop.right.values[0]), ast.Constant)
189 self.assertEqual(type(binop.right.values[0].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700190 self.assertEqual(type(binop.right.values[1]), ast.FormattedValue)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300191 self.assertEqual(type(binop.right.values[2]), ast.Constant)
192 self.assertEqual(type(binop.right.values[2].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700193 self.assertEqual(binop.right.values[0].lineno, 3)
194 self.assertEqual(binop.right.values[1].lineno, 3)
195 self.assertEqual(binop.right.values[2].lineno, 3)
196 call = binop.right.values[1].value
197 self.assertEqual(type(call), ast.Call)
198 self.assertEqual(call.lineno, 3)
199 self.assertEqual(call.col_offset, 11)
200
201 def test_ast_line_numbers_duplicate_expression(self):
202 """Duplicate expression
203
204 NOTE: this is currently broken, always sets location of the first
205 expression.
206 """
207 expr = """
208a = 10
209f'{a * x()} {a * x()} {a * x()}'
210"""
211 t = ast.parse(expr)
212 self.assertEqual(type(t), ast.Module)
213 self.assertEqual(len(t.body), 2)
214 # check `a = 10`
215 self.assertEqual(type(t.body[0]), ast.Assign)
216 self.assertEqual(t.body[0].lineno, 2)
217 # check `f'...'`
218 self.assertEqual(type(t.body[1]), ast.Expr)
219 self.assertEqual(type(t.body[1].value), ast.JoinedStr)
220 self.assertEqual(len(t.body[1].value.values), 5)
221 self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300222 self.assertEqual(type(t.body[1].value.values[1]), ast.Constant)
223 self.assertEqual(type(t.body[1].value.values[1].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700224 self.assertEqual(type(t.body[1].value.values[2]), ast.FormattedValue)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300225 self.assertEqual(type(t.body[1].value.values[3]), ast.Constant)
226 self.assertEqual(type(t.body[1].value.values[3].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700227 self.assertEqual(type(t.body[1].value.values[4]), ast.FormattedValue)
228 self.assertEqual(t.body[1].lineno, 3)
229 self.assertEqual(t.body[1].value.lineno, 3)
230 self.assertEqual(t.body[1].value.values[0].lineno, 3)
231 self.assertEqual(t.body[1].value.values[1].lineno, 3)
232 self.assertEqual(t.body[1].value.values[2].lineno, 3)
233 self.assertEqual(t.body[1].value.values[3].lineno, 3)
234 self.assertEqual(t.body[1].value.values[4].lineno, 3)
235 # check the first binop location
236 binop = t.body[1].value.values[0].value
237 self.assertEqual(type(binop), ast.BinOp)
238 self.assertEqual(type(binop.left), ast.Name)
239 self.assertEqual(type(binop.op), ast.Mult)
240 self.assertEqual(type(binop.right), ast.Call)
241 self.assertEqual(binop.lineno, 3)
242 self.assertEqual(binop.left.lineno, 3)
243 self.assertEqual(binop.right.lineno, 3)
244 self.assertEqual(binop.col_offset, 3)
245 self.assertEqual(binop.left.col_offset, 3)
246 self.assertEqual(binop.right.col_offset, 7)
247 # check the second binop location
248 binop = t.body[1].value.values[2].value
249 self.assertEqual(type(binop), ast.BinOp)
250 self.assertEqual(type(binop.left), ast.Name)
251 self.assertEqual(type(binop.op), ast.Mult)
252 self.assertEqual(type(binop.right), ast.Call)
253 self.assertEqual(binop.lineno, 3)
254 self.assertEqual(binop.left.lineno, 3)
255 self.assertEqual(binop.right.lineno, 3)
256 self.assertEqual(binop.col_offset, 3) # FIXME: this is wrong
257 self.assertEqual(binop.left.col_offset, 3) # FIXME: this is wrong
258 self.assertEqual(binop.right.col_offset, 7) # FIXME: this is wrong
259 # check the third binop location
260 binop = t.body[1].value.values[4].value
261 self.assertEqual(type(binop), ast.BinOp)
262 self.assertEqual(type(binop.left), ast.Name)
263 self.assertEqual(type(binop.op), ast.Mult)
264 self.assertEqual(type(binop.right), ast.Call)
265 self.assertEqual(binop.lineno, 3)
266 self.assertEqual(binop.left.lineno, 3)
267 self.assertEqual(binop.right.lineno, 3)
268 self.assertEqual(binop.col_offset, 3) # FIXME: this is wrong
269 self.assertEqual(binop.left.col_offset, 3) # FIXME: this is wrong
270 self.assertEqual(binop.right.col_offset, 7) # FIXME: this is wrong
271
272 def test_ast_line_numbers_multiline_fstring(self):
273 # FIXME: This test demonstrates invalid behavior due to JoinedStr's
274 # immediate child nodes containing the wrong lineno. The enclosed
275 # expressions have valid line information and column offsets.
276 # See bpo-16806 and bpo-30465 for details.
277 expr = """
278a = 10
279f'''
280 {a
281 *
282 x()}
283non-important content
284'''
285"""
286 t = ast.parse(expr)
287 self.assertEqual(type(t), ast.Module)
288 self.assertEqual(len(t.body), 2)
289 # check `a = 10`
290 self.assertEqual(type(t.body[0]), ast.Assign)
291 self.assertEqual(t.body[0].lineno, 2)
292 # check `f'...'`
293 self.assertEqual(type(t.body[1]), ast.Expr)
294 self.assertEqual(type(t.body[1].value), ast.JoinedStr)
295 self.assertEqual(len(t.body[1].value.values), 3)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300296 self.assertEqual(type(t.body[1].value.values[0]), ast.Constant)
297 self.assertEqual(type(t.body[1].value.values[0].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700298 self.assertEqual(type(t.body[1].value.values[1]), ast.FormattedValue)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300299 self.assertEqual(type(t.body[1].value.values[2]), ast.Constant)
300 self.assertEqual(type(t.body[1].value.values[2].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700301 # NOTE: the following invalid behavior is described in bpo-16806.
302 # - line number should be the *first* line (3), not the *last* (8)
303 # - column offset should not be -1
304 self.assertEqual(t.body[1].lineno, 8)
305 self.assertEqual(t.body[1].value.lineno, 8)
306 self.assertEqual(t.body[1].value.values[0].lineno, 8)
307 self.assertEqual(t.body[1].value.values[1].lineno, 8)
308 self.assertEqual(t.body[1].value.values[2].lineno, 8)
309 self.assertEqual(t.body[1].col_offset, -1)
310 self.assertEqual(t.body[1].value.col_offset, -1)
311 self.assertEqual(t.body[1].value.values[0].col_offset, -1)
312 self.assertEqual(t.body[1].value.values[1].col_offset, -1)
313 self.assertEqual(t.body[1].value.values[2].col_offset, -1)
314 # NOTE: the following lineno information and col_offset is correct for
315 # expressions within FormattedValues.
316 binop = t.body[1].value.values[1].value
317 self.assertEqual(type(binop), ast.BinOp)
318 self.assertEqual(type(binop.left), ast.Name)
319 self.assertEqual(type(binop.op), ast.Mult)
320 self.assertEqual(type(binop.right), ast.Call)
321 self.assertEqual(binop.lineno, 4)
322 self.assertEqual(binop.left.lineno, 4)
323 self.assertEqual(binop.right.lineno, 6)
324 self.assertEqual(binop.col_offset, 3)
325 self.assertEqual(binop.left.col_offset, 3)
326 self.assertEqual(binop.right.col_offset, 7)
327
Serhiy Storchaka4cc30ae2016-12-11 19:37:19 +0200328 def test_docstring(self):
329 def f():
330 f'''Not a docstring'''
331 self.assertIsNone(f.__doc__)
332 def g():
333 '''Not a docstring''' \
334 f''
335 self.assertIsNone(g.__doc__)
336
Eric V. Smith235a6f02015-09-19 14:51:32 -0400337 def test_literal_eval(self):
Eric V. Smith235a6f02015-09-19 14:51:32 -0400338 with self.assertRaisesRegex(ValueError, 'malformed node or string'):
Serhiy Storchaka4cc30ae2016-12-11 19:37:19 +0200339 ast.literal_eval("f'x'")
Eric V. Smith235a6f02015-09-19 14:51:32 -0400340
341 def test_ast_compile_time_concat(self):
342 x = ['']
343
344 expr = """x[0] = 'foo' f'{3}'"""
345 t = ast.parse(expr)
346 c = compile(t, '', 'exec')
347 exec(c)
348 self.assertEqual(x[0], 'foo3')
349
Eric V. Smith9b88fdf2016-11-07 17:54:01 -0500350 def test_compile_time_concat_errors(self):
351 self.assertAllRaise(SyntaxError,
352 'cannot mix bytes and nonbytes literals',
353 [r"""f'' b''""",
354 r"""b'' f''""",
355 ])
356
Eric V. Smith235a6f02015-09-19 14:51:32 -0400357 def test_literal(self):
358 self.assertEqual(f'', '')
359 self.assertEqual(f'a', 'a')
360 self.assertEqual(f' ', ' ')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400361
362 def test_unterminated_string(self):
363 self.assertAllRaise(SyntaxError, 'f-string: unterminated string',
364 [r"""f'{"x'""",
365 r"""f'{"x}'""",
366 r"""f'{("x'""",
367 r"""f'{("x}'""",
368 ])
369
370 def test_mismatched_parens(self):
371 self.assertAllRaise(SyntaxError, 'f-string: mismatched',
372 ["f'{((}'",
373 ])
374
375 def test_double_braces(self):
376 self.assertEqual(f'{{', '{')
377 self.assertEqual(f'a{{', 'a{')
378 self.assertEqual(f'{{b', '{b')
379 self.assertEqual(f'a{{b', 'a{b')
380 self.assertEqual(f'}}', '}')
381 self.assertEqual(f'a}}', 'a}')
382 self.assertEqual(f'}}b', '}b')
383 self.assertEqual(f'a}}b', 'a}b')
Eric V. Smith451d0e32016-09-09 21:56:20 -0400384 self.assertEqual(f'{{}}', '{}')
385 self.assertEqual(f'a{{}}', 'a{}')
386 self.assertEqual(f'{{b}}', '{b}')
387 self.assertEqual(f'{{}}c', '{}c')
388 self.assertEqual(f'a{{b}}', 'a{b}')
389 self.assertEqual(f'a{{}}c', 'a{}c')
390 self.assertEqual(f'{{b}}c', '{b}c')
391 self.assertEqual(f'a{{b}}c', 'a{b}c')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400392
393 self.assertEqual(f'{{{10}', '{10')
394 self.assertEqual(f'}}{10}', '}10')
395 self.assertEqual(f'}}{{{10}', '}{10')
396 self.assertEqual(f'}}a{{{10}', '}a{10')
397
398 self.assertEqual(f'{10}{{', '10{')
399 self.assertEqual(f'{10}}}', '10}')
400 self.assertEqual(f'{10}}}{{', '10}{')
401 self.assertEqual(f'{10}}}a{{' '}', '10}a{}')
402
403 # Inside of strings, don't interpret doubled brackets.
404 self.assertEqual(f'{"{{}}"}', '{{}}')
405
406 self.assertAllRaise(TypeError, 'unhashable type',
407 ["f'{ {{}} }'", # dict in a set
408 ])
409
410 def test_compile_time_concat(self):
411 x = 'def'
412 self.assertEqual('abc' f'## {x}ghi', 'abc## defghi')
413 self.assertEqual('abc' f'{x}' 'ghi', 'abcdefghi')
414 self.assertEqual('abc' f'{x}' 'gh' f'i{x:4}', 'abcdefghidef ')
415 self.assertEqual('{x}' f'{x}', '{x}def')
416 self.assertEqual('{x' f'{x}', '{xdef')
417 self.assertEqual('{x}' f'{x}', '{x}def')
418 self.assertEqual('{{x}}' f'{x}', '{{x}}def')
419 self.assertEqual('{{x' f'{x}', '{{xdef')
420 self.assertEqual('x}}' f'{x}', 'x}}def')
421 self.assertEqual(f'{x}' 'x}}', 'defx}}')
422 self.assertEqual(f'{x}' '', 'def')
423 self.assertEqual('' f'{x}' '', 'def')
424 self.assertEqual('' f'{x}', 'def')
425 self.assertEqual(f'{x}' '2', 'def2')
426 self.assertEqual('1' f'{x}' '2', '1def2')
427 self.assertEqual('1' f'{x}', '1def')
428 self.assertEqual(f'{x}' f'-{x}', 'def-def')
429 self.assertEqual('' f'', '')
430 self.assertEqual('' f'' '', '')
431 self.assertEqual('' f'' '' f'', '')
432 self.assertEqual(f'', '')
433 self.assertEqual(f'' '', '')
434 self.assertEqual(f'' '' f'', '')
435 self.assertEqual(f'' '' f'' '', '')
436
437 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
438 ["f'{3' f'}'", # can't concat to get a valid f-string
439 ])
440
441 def test_comments(self):
442 # These aren't comments, since they're in strings.
443 d = {'#': 'hash'}
444 self.assertEqual(f'{"#"}', '#')
445 self.assertEqual(f'{d["#"]}', 'hash')
446
Eric V. Smith09835dc2016-09-11 18:58:20 -0400447 self.assertAllRaise(SyntaxError, "f-string expression part cannot include '#'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400448 ["f'{1#}'", # error because the expression becomes "(1#)"
449 "f'{3(#)}'",
Eric V. Smith09835dc2016-09-11 18:58:20 -0400450 "f'{#}'",
Eric V. Smith35a24c52016-09-11 19:01:22 -0400451 "f'{)#}'", # When wrapped in parens, this becomes
452 # '()#)'. Make sure that doesn't compile.
Eric V. Smith235a6f02015-09-19 14:51:32 -0400453 ])
454
455 def test_many_expressions(self):
456 # Create a string with many expressions in it. Note that
457 # because we have a space in here as a literal, we're actually
458 # going to use twice as many ast nodes: one for each literal
459 # plus one for each expression.
460 def build_fstr(n, extra=''):
461 return "f'" + ('{x} ' * n) + extra + "'"
462
463 x = 'X'
464 width = 1
465
466 # Test around 256.
467 for i in range(250, 260):
468 self.assertEqual(eval(build_fstr(i)), (x+' ')*i)
469
470 # Test concatenating 2 largs fstrings.
471 self.assertEqual(eval(build_fstr(255)*256), (x+' ')*(255*256))
472
473 s = build_fstr(253, '{x:{width}} ')
474 self.assertEqual(eval(s), (x+' ')*254)
475
476 # Test lots of expressions and constants, concatenated.
477 s = "f'{1}' 'x' 'y'" * 1024
478 self.assertEqual(eval(s), '1xy' * 1024)
479
480 def test_format_specifier_expressions(self):
481 width = 10
482 precision = 4
483 value = decimal.Decimal('12.34567')
484 self.assertEqual(f'result: {value:{width}.{precision}}', 'result: 12.35')
485 self.assertEqual(f'result: {value:{width!r}.{precision}}', 'result: 12.35')
486 self.assertEqual(f'result: {value:{width:0}.{precision:1}}', 'result: 12.35')
487 self.assertEqual(f'result: {value:{1}{0:0}.{precision:1}}', 'result: 12.35')
488 self.assertEqual(f'result: {value:{ 1}{ 0:0}.{ precision:1}}', 'result: 12.35')
489 self.assertEqual(f'{10:#{1}0x}', ' 0xa')
490 self.assertEqual(f'{10:{"#"}1{0}{"x"}}', ' 0xa')
491 self.assertEqual(f'{-10:-{"#"}1{0}x}', ' -0xa')
492 self.assertEqual(f'{-10:{"-"}#{1}0{"x"}}', ' -0xa')
493 self.assertEqual(f'{10:#{3 != {4:5} and width}x}', ' 0xa')
494
495 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
496 ["""f'{"s"!r{":10"}}'""",
497
498 # This looks like a nested format spec.
499 ])
500
501 self.assertAllRaise(SyntaxError, "invalid syntax",
Martin Panter263893c2016-07-28 01:25:31 +0000502 [# Invalid syntax inside a nested spec.
Eric V. Smith235a6f02015-09-19 14:51:32 -0400503 "f'{4:{/5}}'",
504 ])
505
506 self.assertAllRaise(SyntaxError, "f-string: expressions nested too deeply",
507 [# Can't nest format specifiers.
508 "f'result: {value:{width:{0}}.{precision:1}}'",
509 ])
510
511 self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character',
512 [# No expansion inside conversion or for
513 # the : or ! itself.
514 """f'{"s"!{"r"}}'""",
515 ])
516
517 def test_side_effect_order(self):
518 class X:
519 def __init__(self):
520 self.i = 0
521 def __format__(self, spec):
522 self.i += 1
523 return str(self.i)
524
525 x = X()
526 self.assertEqual(f'{x} {x}', '1 2')
527
528 def test_missing_expression(self):
529 self.assertAllRaise(SyntaxError, 'f-string: empty expression not allowed',
530 ["f'{}'",
531 "f'{ }'"
532 "f' {} '",
533 "f'{!r}'",
534 "f'{ !r}'",
535 "f'{10:{ }}'",
536 "f' { } '",
Eric V. Smith1d44c412015-09-23 07:49:00 -0400537
Serhiy Storchaka2e9cd582017-06-08 23:43:54 +0300538 # The Python parser ignores also the following
539 # whitespace characters in additional to a space.
540 "f'''{\t\f\r\n}'''",
541
Eric V. Smith1d44c412015-09-23 07:49:00 -0400542 # Catch the empty expression before the
543 # invalid conversion.
544 "f'{!x}'",
545 "f'{ !xr}'",
546 "f'{!x:}'",
547 "f'{!x:a}'",
548 "f'{ !xr:}'",
549 "f'{ !xr:a}'",
Eric V. Smith548c4d32015-09-23 08:00:01 -0400550
551 "f'{!}'",
552 "f'{:}'",
Eric V. Smithb2080f62015-09-23 10:24:43 -0400553
554 # We find the empty expression before the
555 # missing closing brace.
556 "f'{!'",
557 "f'{!s:'",
558 "f'{:'",
559 "f'{:x'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400560 ])
561
Serhiy Storchaka2e9cd582017-06-08 23:43:54 +0300562 # Different error message is raised for other whitespace characters.
563 self.assertAllRaise(SyntaxError, 'invalid character in identifier',
564 ["f'''{\xa0}'''",
565 "\xa0",
566 ])
567
Eric V. Smith235a6f02015-09-19 14:51:32 -0400568 def test_parens_in_expressions(self):
569 self.assertEqual(f'{3,}', '(3,)')
570
571 # Add these because when an expression is evaluated, parens
572 # are added around it. But we shouldn't go from an invalid
573 # expression to a valid one. The added parens are just
574 # supposed to allow whitespace (including newlines).
575 self.assertAllRaise(SyntaxError, 'invalid syntax',
576 ["f'{,}'",
577 "f'{,}'", # this is (,), which is an error
578 ])
579
580 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
581 ["f'{3)+(4}'",
582 ])
583
584 self.assertAllRaise(SyntaxError, 'EOL while scanning string literal',
585 ["f'{\n}'",
586 ])
587
Eric V. Smith451d0e32016-09-09 21:56:20 -0400588 def test_backslashes_in_string_part(self):
589 self.assertEqual(f'\t', '\t')
590 self.assertEqual(r'\t', '\\t')
591 self.assertEqual(rf'\t', '\\t')
592 self.assertEqual(f'{2}\t', '2\t')
593 self.assertEqual(f'{2}\t{3}', '2\t3')
594 self.assertEqual(f'\t{3}', '\t3')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400595
Eric V. Smith451d0e32016-09-09 21:56:20 -0400596 self.assertEqual(f'\u0394', '\u0394')
597 self.assertEqual(r'\u0394', '\\u0394')
598 self.assertEqual(rf'\u0394', '\\u0394')
599 self.assertEqual(f'{2}\u0394', '2\u0394')
600 self.assertEqual(f'{2}\u0394{3}', '2\u03943')
601 self.assertEqual(f'\u0394{3}', '\u03943')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400602
Eric V. Smith451d0e32016-09-09 21:56:20 -0400603 self.assertEqual(f'\U00000394', '\u0394')
604 self.assertEqual(r'\U00000394', '\\U00000394')
605 self.assertEqual(rf'\U00000394', '\\U00000394')
606 self.assertEqual(f'{2}\U00000394', '2\u0394')
607 self.assertEqual(f'{2}\U00000394{3}', '2\u03943')
608 self.assertEqual(f'\U00000394{3}', '\u03943')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400609
Eric V. Smith451d0e32016-09-09 21:56:20 -0400610 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}', '\u0394')
611 self.assertEqual(f'{2}\N{GREEK CAPITAL LETTER DELTA}', '2\u0394')
612 self.assertEqual(f'{2}\N{GREEK CAPITAL LETTER DELTA}{3}', '2\u03943')
613 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}{3}', '\u03943')
614 self.assertEqual(f'2\N{GREEK CAPITAL LETTER DELTA}', '2\u0394')
615 self.assertEqual(f'2\N{GREEK CAPITAL LETTER DELTA}3', '2\u03943')
616 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}3', '\u03943')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400617
Eric V. Smith451d0e32016-09-09 21:56:20 -0400618 self.assertEqual(f'\x20', ' ')
619 self.assertEqual(r'\x20', '\\x20')
620 self.assertEqual(rf'\x20', '\\x20')
621 self.assertEqual(f'{2}\x20', '2 ')
622 self.assertEqual(f'{2}\x20{3}', '2 3')
623 self.assertEqual(f'\x20{3}', ' 3')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400624
Eric V. Smith451d0e32016-09-09 21:56:20 -0400625 self.assertEqual(f'2\x20', '2 ')
626 self.assertEqual(f'2\x203', '2 3')
627 self.assertEqual(f'\x203', ' 3')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400628
Serhiy Storchaka0cd7a3f2017-05-25 13:33:55 +0300629 with self.assertWarns(DeprecationWarning): # invalid escape sequence
630 value = eval(r"f'\{6*7}'")
631 self.assertEqual(value, '\\42')
632 self.assertEqual(f'\\{6*7}', '\\42')
633 self.assertEqual(fr'\{6*7}', '\\42')
634
635 AMPERSAND = 'spam'
636 # Get the right unicode character (&), or pick up local variable
637 # depending on the number of backslashes.
638 self.assertEqual(f'\N{AMPERSAND}', '&')
639 self.assertEqual(f'\\N{AMPERSAND}', '\\Nspam')
640 self.assertEqual(fr'\N{AMPERSAND}', '\\Nspam')
641 self.assertEqual(f'\\\N{AMPERSAND}', '\\&')
642
Eric V. Smith451d0e32016-09-09 21:56:20 -0400643 def test_misformed_unicode_character_name(self):
644 # These test are needed because unicode names are parsed
645 # differently inside f-strings.
646 self.assertAllRaise(SyntaxError, r"\(unicode error\) 'unicodeescape' codec can't decode bytes in position .*: malformed \\N character escape",
647 [r"f'\N'",
648 r"f'\N{'",
649 r"f'\N{GREEK CAPITAL LETTER DELTA'",
650
651 # Here are the non-f-string versions,
652 # which should give the same errors.
653 r"'\N'",
654 r"'\N{'",
655 r"'\N{GREEK CAPITAL LETTER DELTA'",
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400656 ])
657
Eric V. Smith451d0e32016-09-09 21:56:20 -0400658 def test_no_backslashes_in_expression_part(self):
659 self.assertAllRaise(SyntaxError, 'f-string expression part cannot include a backslash',
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400660 [r"f'{\'a\'}'",
661 r"f'{\t3}'",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400662 r"f'{\}'",
663 r"rf'{\'a\'}'",
664 r"rf'{\t3}'",
665 r"rf'{\}'",
666 r"""rf'{"\N{LEFT CURLY BRACKET}"}'""",
Jason R. Coombs45cab8c2016-11-06 11:01:08 -0500667 r"f'{\n}'",
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400668 ])
669
Eric V. Smith451d0e32016-09-09 21:56:20 -0400670 def test_no_escapes_for_braces(self):
Jason R. Coombs1c92a762016-11-06 11:25:54 -0500671 """
672 Only literal curly braces begin an expression.
673 """
674 # \x7b is '{'.
675 self.assertEqual(f'\x7b1+1}}', '{1+1}')
676 self.assertEqual(f'\x7b1+1', '{1+1')
677 self.assertEqual(f'\u007b1+1', '{1+1')
678 self.assertEqual(f'\N{LEFT CURLY BRACKET}1+1\N{RIGHT CURLY BRACKET}', '{1+1}')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400679
Eric V. Smith235a6f02015-09-19 14:51:32 -0400680 def test_newlines_in_expressions(self):
681 self.assertEqual(f'{0}', '0')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400682 self.assertEqual(rf'''{3+
6834}''', '7')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400684
685 def test_lambda(self):
686 x = 5
687 self.assertEqual(f'{(lambda y:x*y)("8")!r}', "'88888'")
688 self.assertEqual(f'{(lambda y:x*y)("8")!r:10}', "'88888' ")
689 self.assertEqual(f'{(lambda y:x*y)("8"):10}', "88888 ")
690
691 # lambda doesn't work without parens, because the colon
692 # makes the parser think it's a format_spec
693 self.assertAllRaise(SyntaxError, 'unexpected EOF while parsing',
694 ["f'{lambda x:x}'",
695 ])
696
697 def test_yield(self):
698 # Not terribly useful, but make sure the yield turns
699 # a function into a generator
700 def fn(y):
701 f'y:{yield y*2}'
702
703 g = fn(4)
704 self.assertEqual(next(g), 8)
705
706 def test_yield_send(self):
707 def fn(x):
708 yield f'x:{yield (lambda i: x * i)}'
709
710 g = fn(10)
711 the_lambda = next(g)
712 self.assertEqual(the_lambda(4), 40)
713 self.assertEqual(g.send('string'), 'x:string')
714
715 def test_expressions_with_triple_quoted_strings(self):
716 self.assertEqual(f"{'''x'''}", 'x')
717 self.assertEqual(f"{'''eric's'''}", "eric's")
Eric V. Smith235a6f02015-09-19 14:51:32 -0400718
719 # Test concatenation within an expression
720 self.assertEqual(f'{"x" """eric"s""" "y"}', 'xeric"sy')
721 self.assertEqual(f'{"x" """eric"s"""}', 'xeric"s')
722 self.assertEqual(f'{"""eric"s""" "y"}', 'eric"sy')
723 self.assertEqual(f'{"""x""" """eric"s""" "y"}', 'xeric"sy')
724 self.assertEqual(f'{"""x""" """eric"s""" """y"""}', 'xeric"sy')
725 self.assertEqual(f'{r"""x""" """eric"s""" """y"""}', 'xeric"sy')
726
727 def test_multiple_vars(self):
728 x = 98
729 y = 'abc'
730 self.assertEqual(f'{x}{y}', '98abc')
731
732 self.assertEqual(f'X{x}{y}', 'X98abc')
733 self.assertEqual(f'{x}X{y}', '98Xabc')
734 self.assertEqual(f'{x}{y}X', '98abcX')
735
736 self.assertEqual(f'X{x}Y{y}', 'X98Yabc')
737 self.assertEqual(f'X{x}{y}Y', 'X98abcY')
738 self.assertEqual(f'{x}X{y}Y', '98XabcY')
739
740 self.assertEqual(f'X{x}Y{y}Z', 'X98YabcZ')
741
742 def test_closure(self):
743 def outer(x):
744 def inner():
745 return f'x:{x}'
746 return inner
747
748 self.assertEqual(outer('987')(), 'x:987')
749 self.assertEqual(outer(7)(), 'x:7')
750
751 def test_arguments(self):
752 y = 2
753 def f(x, width):
754 return f'x={x*y:{width}}'
755
756 self.assertEqual(f('foo', 10), 'x=foofoo ')
757 x = 'bar'
758 self.assertEqual(f(10, 10), 'x= 20')
759
760 def test_locals(self):
761 value = 123
762 self.assertEqual(f'v:{value}', 'v:123')
763
764 def test_missing_variable(self):
765 with self.assertRaises(NameError):
766 f'v:{value}'
767
768 def test_missing_format_spec(self):
769 class O:
770 def __format__(self, spec):
771 if not spec:
772 return '*'
773 return spec
774
775 self.assertEqual(f'{O():x}', 'x')
776 self.assertEqual(f'{O()}', '*')
777 self.assertEqual(f'{O():}', '*')
778
779 self.assertEqual(f'{3:}', '3')
780 self.assertEqual(f'{3!s:}', '3')
781
782 def test_global(self):
783 self.assertEqual(f'g:{a_global}', 'g:global variable')
784 self.assertEqual(f'g:{a_global!r}', "g:'global variable'")
785
786 a_local = 'local variable'
787 self.assertEqual(f'g:{a_global} l:{a_local}',
788 'g:global variable l:local variable')
789 self.assertEqual(f'g:{a_global!r}',
790 "g:'global variable'")
791 self.assertEqual(f'g:{a_global} l:{a_local!r}',
792 "g:global variable l:'local variable'")
793
794 self.assertIn("module 'unittest' from", f'{unittest}')
795
796 def test_shadowed_global(self):
797 a_global = 'really a local'
798 self.assertEqual(f'g:{a_global}', 'g:really a local')
799 self.assertEqual(f'g:{a_global!r}', "g:'really a local'")
800
801 a_local = 'local variable'
802 self.assertEqual(f'g:{a_global} l:{a_local}',
803 'g:really a local l:local variable')
804 self.assertEqual(f'g:{a_global!r}',
805 "g:'really a local'")
806 self.assertEqual(f'g:{a_global} l:{a_local!r}',
807 "g:really a local l:'local variable'")
808
809 def test_call(self):
810 def foo(x):
811 return 'x=' + str(x)
812
813 self.assertEqual(f'{foo(10)}', 'x=10')
814
815 def test_nested_fstrings(self):
816 y = 5
817 self.assertEqual(f'{f"{0}"*3}', '000')
818 self.assertEqual(f'{f"{y}"*3}', '555')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400819
820 def test_invalid_string_prefixes(self):
821 self.assertAllRaise(SyntaxError, 'unexpected EOF while parsing',
822 ["fu''",
823 "uf''",
824 "Fu''",
825 "fU''",
826 "Uf''",
827 "uF''",
828 "ufr''",
829 "urf''",
830 "fur''",
831 "fru''",
832 "rfu''",
833 "ruf''",
834 "FUR''",
835 "Fur''",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400836 "fb''",
837 "fB''",
838 "Fb''",
839 "FB''",
840 "bf''",
841 "bF''",
842 "Bf''",
843 "BF''",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400844 ])
845
846 def test_leading_trailing_spaces(self):
847 self.assertEqual(f'{ 3}', '3')
848 self.assertEqual(f'{ 3}', '3')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400849 self.assertEqual(f'{3 }', '3')
850 self.assertEqual(f'{3 }', '3')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400851
852 self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]}}',
853 'expr={1: 2}')
854 self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]} }',
855 'expr={1: 2}')
856
Eric V. Smith235a6f02015-09-19 14:51:32 -0400857 def test_not_equal(self):
858 # There's a special test for this because there's a special
859 # case in the f-string parser to look for != as not ending an
860 # expression. Normally it would, while looking for !s or !r.
861
862 self.assertEqual(f'{3!=4}', 'True')
863 self.assertEqual(f'{3!=4:}', 'True')
864 self.assertEqual(f'{3!=4!s}', 'True')
865 self.assertEqual(f'{3!=4!s:.3}', 'Tru')
866
867 def test_conversions(self):
868 self.assertEqual(f'{3.14:10.10}', ' 3.14')
869 self.assertEqual(f'{3.14!s:10.10}', '3.14 ')
870 self.assertEqual(f'{3.14!r:10.10}', '3.14 ')
871 self.assertEqual(f'{3.14!a:10.10}', '3.14 ')
872
873 self.assertEqual(f'{"a"}', 'a')
874 self.assertEqual(f'{"a"!r}', "'a'")
875 self.assertEqual(f'{"a"!a}', "'a'")
876
877 # Not a conversion.
878 self.assertEqual(f'{"a!r"}', "a!r")
879
880 # Not a conversion, but show that ! is allowed in a format spec.
881 self.assertEqual(f'{3.14:!<10.10}', '3.14!!!!!!')
882
Eric V. Smith235a6f02015-09-19 14:51:32 -0400883 self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character',
884 ["f'{3!g}'",
885 "f'{3!A}'",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400886 "f'{3!3}'",
887 "f'{3!G}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400888 "f'{3!!}'",
889 "f'{3!:}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400890 "f'{3! s}'", # no space before conversion char
Eric V. Smith235a6f02015-09-19 14:51:32 -0400891 ])
892
893 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
894 ["f'{x!s{y}}'",
895 "f'{3!ss}'",
896 "f'{3!ss:}'",
897 "f'{3!ss:s}'",
898 ])
899
900 def test_assignment(self):
901 self.assertAllRaise(SyntaxError, 'invalid syntax',
902 ["f'' = 3",
903 "f'{0}' = x",
904 "f'{x}' = x",
905 ])
906
907 def test_del(self):
908 self.assertAllRaise(SyntaxError, 'invalid syntax',
909 ["del f''",
910 "del '' f''",
911 ])
912
913 def test_mismatched_braces(self):
914 self.assertAllRaise(SyntaxError, "f-string: single '}' is not allowed",
915 ["f'{{}'",
916 "f'{{}}}'",
917 "f'}'",
918 "f'x}'",
919 "f'x}x'",
Jason R. Coombsda25abf72016-11-06 11:14:48 -0500920 r"f'\u007b}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400921
922 # Can't have { or } in a format spec.
923 "f'{3:}>10}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400924 "f'{3:}}>10}'",
925 ])
926
927 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
928 ["f'{3:{{>10}'",
929 "f'{3'",
930 "f'{3!'",
931 "f'{3:'",
932 "f'{3!s'",
933 "f'{3!s:'",
934 "f'{3!s:3'",
935 "f'x{'",
936 "f'x{x'",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400937 "f'{x'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400938 "f'{3:s'",
939 "f'{{{'",
940 "f'{{}}{'",
941 "f'{'",
942 ])
943
Eric V. Smith235a6f02015-09-19 14:51:32 -0400944 # But these are just normal strings.
945 self.assertEqual(f'{"{"}', '{')
946 self.assertEqual(f'{"}"}', '}')
947 self.assertEqual(f'{3:{"}"}>10}', '}}}}}}}}}3')
948 self.assertEqual(f'{2:{"{"}>10}', '{{{{{{{{{2')
949
950 def test_if_conditional(self):
951 # There's special logic in compile.c to test if the
952 # conditional for an if (and while) are constants. Exercise
953 # that code.
954
955 def test_fstring(x, expected):
956 flag = 0
957 if f'{x}':
958 flag = 1
959 else:
960 flag = 2
961 self.assertEqual(flag, expected)
962
963 def test_concat_empty(x, expected):
964 flag = 0
965 if '' f'{x}':
966 flag = 1
967 else:
968 flag = 2
969 self.assertEqual(flag, expected)
970
971 def test_concat_non_empty(x, expected):
972 flag = 0
973 if ' ' f'{x}':
974 flag = 1
975 else:
976 flag = 2
977 self.assertEqual(flag, expected)
978
979 test_fstring('', 2)
980 test_fstring(' ', 1)
981
982 test_concat_empty('', 2)
983 test_concat_empty(' ', 1)
984
985 test_concat_non_empty('', 1)
986 test_concat_non_empty(' ', 1)
987
988 def test_empty_format_specifier(self):
989 x = 'test'
990 self.assertEqual(f'{x}', 'test')
991 self.assertEqual(f'{x:}', 'test')
992 self.assertEqual(f'{x!s:}', 'test')
993 self.assertEqual(f'{x!r:}', "'test'")
994
995 def test_str_format_differences(self):
996 d = {'a': 'string',
997 0: 'integer',
998 }
999 a = 0
1000 self.assertEqual(f'{d[0]}', 'integer')
1001 self.assertEqual(f'{d["a"]}', 'string')
1002 self.assertEqual(f'{d[a]}', 'integer')
1003 self.assertEqual('{d[a]}'.format(d=d), 'string')
1004 self.assertEqual('{d[0]}'.format(d=d), 'integer')
1005
1006 def test_invalid_expressions(self):
1007 self.assertAllRaise(SyntaxError, 'invalid syntax',
1008 [r"f'{a[4)}'",
1009 r"f'{a(4]}'",
1010 ])
1011
Eric V. Smith135d5f42016-02-05 18:23:08 -05001012 def test_errors(self):
1013 # see issue 26287
Serhiy Storchaka13c8f322016-10-31 08:13:00 +02001014 self.assertAllRaise(TypeError, 'unsupported',
Eric V. Smith135d5f42016-02-05 18:23:08 -05001015 [r"f'{(lambda: 0):x}'",
1016 r"f'{(0,):x}'",
1017 ])
1018 self.assertAllRaise(ValueError, 'Unknown format code',
1019 [r"f'{1000:j}'",
1020 r"f'{1000:j}'",
1021 ])
1022
Eric V. Smith235a6f02015-09-19 14:51:32 -04001023 def test_loop(self):
1024 for i in range(1000):
1025 self.assertEqual(f'i:{i}', 'i:' + str(i))
1026
1027 def test_dict(self):
1028 d = {'"': 'dquote',
1029 "'": 'squote',
1030 'foo': 'bar',
1031 }
Eric V. Smith235a6f02015-09-19 14:51:32 -04001032 self.assertEqual(f'''{d["'"]}''', 'squote')
1033 self.assertEqual(f"""{d['"']}""", 'dquote')
1034
1035 self.assertEqual(f'{d["foo"]}', 'bar')
1036 self.assertEqual(f"{d['foo']}", 'bar')
Eric V. Smith235a6f02015-09-19 14:51:32 -04001037
ericvsmith11e97f22017-06-16 06:19:32 -04001038 def test_backslash_char(self):
1039 # Check eval of a backslash followed by a control char.
1040 # See bpo-30682: this used to raise an assert in pydebug mode.
1041 self.assertEqual(eval('f"\\\n"'), '')
1042 self.assertEqual(eval('f"\\\r"'), '')
1043
Łukasz Langae7c566c2017-09-06 17:27:58 -07001044
Eric V. Smith235a6f02015-09-19 14:51:32 -04001045if __name__ == '__main__':
1046 unittest.main()