• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# -*- coding: utf-8 -*-
2# There are tests here with unicode string literals and
3# identifiers. There's a code in ast.c that was added because of a
4# failure with a non-ascii-only expression.  So, I have tests for
5# that.  There are workarounds that would let me run tests for that
6# code without unicode identifiers and strings, but just using them
7# directly seems like the easiest and therefore safest thing to do.
8# Unicode identifiers in tests is allowed by PEP 3131.
9
10import ast
11import types
12import decimal
13import unittest
14
15a_global = 'global variable'
16
17# You could argue that I'm too strict in looking for specific error
18#  values with assertRaisesRegex, but without it it's way too easy to
19#  make a syntax error in the test strings. Especially with all of the
20#  triple quotes, raw strings, backslashes, etc. I think it's a
21#  worthwhile tradeoff. When I switched to this method, I found many
22#  examples where I wasn't testing what I thought I was.
23
24class TestCase(unittest.TestCase):
25    def assertAllRaise(self, exception_type, regex, error_strings):
26        for str in error_strings:
27            with self.subTest(str=str):
28                with self.assertRaisesRegex(exception_type, regex):
29                    eval(str)
30
31    def test__format__lookup(self):
32        # Make sure __format__ is looked up on the type, not the instance.
33        class X:
34            def __format__(self, spec):
35                return 'class'
36
37        x = X()
38
39        # Add a bound __format__ method to the 'y' instance, but not
40        #  the 'x' instance.
41        y = X()
42        y.__format__ = types.MethodType(lambda self, spec: 'instance', y)
43
44        self.assertEqual(f'{y}', format(y))
45        self.assertEqual(f'{y}', 'class')
46        self.assertEqual(format(x), format(y))
47
48        # __format__ is not called this way, but still make sure it
49        #  returns what we expect (so we can make sure we're bypassing
50        #  it).
51        self.assertEqual(x.__format__(''), 'class')
52        self.assertEqual(y.__format__(''), 'instance')
53
54        # This is how __format__ is actually called.
55        self.assertEqual(type(x).__format__(x, ''), 'class')
56        self.assertEqual(type(y).__format__(y, ''), 'class')
57
58    def test_ast(self):
59        # Inspired by http://bugs.python.org/issue24975
60        class X:
61            def __init__(self):
62                self.called = False
63            def __call__(self):
64                self.called = True
65                return 4
66        x = X()
67        expr = """
68a = 10
69f'{a * x()}'"""
70        t = ast.parse(expr)
71        c = compile(t, '', 'exec')
72
73        # Make sure x was not called.
74        self.assertFalse(x.called)
75
76        # Actually run the code.
77        exec(c)
78
79        # Make sure x was called.
80        self.assertTrue(x.called)
81
82    def test_ast_line_numbers(self):
83        expr = """
84a = 10
85f'{a * x()}'"""
86        t = ast.parse(expr)
87        self.assertEqual(type(t), ast.Module)
88        self.assertEqual(len(t.body), 2)
89        # check `a = 10`
90        self.assertEqual(type(t.body[0]), ast.Assign)
91        self.assertEqual(t.body[0].lineno, 2)
92        # check `f'...'`
93        self.assertEqual(type(t.body[1]), ast.Expr)
94        self.assertEqual(type(t.body[1].value), ast.JoinedStr)
95        self.assertEqual(len(t.body[1].value.values), 1)
96        self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)
97        self.assertEqual(t.body[1].lineno, 3)
98        self.assertEqual(t.body[1].value.lineno, 3)
99        self.assertEqual(t.body[1].value.values[0].lineno, 3)
100        # check the binop location
101        binop = t.body[1].value.values[0].value
102        self.assertEqual(type(binop), ast.BinOp)
103        self.assertEqual(type(binop.left), ast.Name)
104        self.assertEqual(type(binop.op), ast.Mult)
105        self.assertEqual(type(binop.right), ast.Call)
106        self.assertEqual(binop.lineno, 3)
107        self.assertEqual(binop.left.lineno, 3)
108        self.assertEqual(binop.right.lineno, 3)
109        self.assertEqual(binop.col_offset, 3)
110        self.assertEqual(binop.left.col_offset, 3)
111        self.assertEqual(binop.right.col_offset, 7)
112
113    def test_ast_line_numbers_multiple_formattedvalues(self):
114        expr = """
115f'no formatted values'
116f'eggs {a * x()} spam {b + y()}'"""
117        t = ast.parse(expr)
118        self.assertEqual(type(t), ast.Module)
119        self.assertEqual(len(t.body), 2)
120        # check `f'no formatted value'`
121        self.assertEqual(type(t.body[0]), ast.Expr)
122        self.assertEqual(type(t.body[0].value), ast.JoinedStr)
123        self.assertEqual(t.body[0].lineno, 2)
124        # check `f'...'`
125        self.assertEqual(type(t.body[1]), ast.Expr)
126        self.assertEqual(type(t.body[1].value), ast.JoinedStr)
127        self.assertEqual(len(t.body[1].value.values), 4)
128        self.assertEqual(type(t.body[1].value.values[0]), ast.Constant)
129        self.assertEqual(type(t.body[1].value.values[0].value), str)
130        self.assertEqual(type(t.body[1].value.values[1]), ast.FormattedValue)
131        self.assertEqual(type(t.body[1].value.values[2]), ast.Constant)
132        self.assertEqual(type(t.body[1].value.values[2].value), str)
133        self.assertEqual(type(t.body[1].value.values[3]), ast.FormattedValue)
134        self.assertEqual(t.body[1].lineno, 3)
135        self.assertEqual(t.body[1].value.lineno, 3)
136        self.assertEqual(t.body[1].value.values[0].lineno, 3)
137        self.assertEqual(t.body[1].value.values[1].lineno, 3)
138        self.assertEqual(t.body[1].value.values[2].lineno, 3)
139        self.assertEqual(t.body[1].value.values[3].lineno, 3)
140        # check the first binop location
141        binop1 = t.body[1].value.values[1].value
142        self.assertEqual(type(binop1), ast.BinOp)
143        self.assertEqual(type(binop1.left), ast.Name)
144        self.assertEqual(type(binop1.op), ast.Mult)
145        self.assertEqual(type(binop1.right), ast.Call)
146        self.assertEqual(binop1.lineno, 3)
147        self.assertEqual(binop1.left.lineno, 3)
148        self.assertEqual(binop1.right.lineno, 3)
149        self.assertEqual(binop1.col_offset, 8)
150        self.assertEqual(binop1.left.col_offset, 8)
151        self.assertEqual(binop1.right.col_offset, 12)
152        # check the second binop location
153        binop2 = t.body[1].value.values[3].value
154        self.assertEqual(type(binop2), ast.BinOp)
155        self.assertEqual(type(binop2.left), ast.Name)
156        self.assertEqual(type(binop2.op), ast.Add)
157        self.assertEqual(type(binop2.right), ast.Call)
158        self.assertEqual(binop2.lineno, 3)
159        self.assertEqual(binop2.left.lineno, 3)
160        self.assertEqual(binop2.right.lineno, 3)
161        self.assertEqual(binop2.col_offset, 23)
162        self.assertEqual(binop2.left.col_offset, 23)
163        self.assertEqual(binop2.right.col_offset, 27)
164
165    def test_ast_line_numbers_nested(self):
166        expr = """
167a = 10
168f'{a * f"-{x()}-"}'"""
169        t = ast.parse(expr)
170        self.assertEqual(type(t), ast.Module)
171        self.assertEqual(len(t.body), 2)
172        # check `a = 10`
173        self.assertEqual(type(t.body[0]), ast.Assign)
174        self.assertEqual(t.body[0].lineno, 2)
175        # check `f'...'`
176        self.assertEqual(type(t.body[1]), ast.Expr)
177        self.assertEqual(type(t.body[1].value), ast.JoinedStr)
178        self.assertEqual(len(t.body[1].value.values), 1)
179        self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)
180        self.assertEqual(t.body[1].lineno, 3)
181        self.assertEqual(t.body[1].value.lineno, 3)
182        self.assertEqual(t.body[1].value.values[0].lineno, 3)
183        # check the binop location
184        binop = t.body[1].value.values[0].value
185        self.assertEqual(type(binop), ast.BinOp)
186        self.assertEqual(type(binop.left), ast.Name)
187        self.assertEqual(type(binop.op), ast.Mult)
188        self.assertEqual(type(binop.right), ast.JoinedStr)
189        self.assertEqual(binop.lineno, 3)
190        self.assertEqual(binop.left.lineno, 3)
191        self.assertEqual(binop.right.lineno, 3)
192        self.assertEqual(binop.col_offset, 3)
193        self.assertEqual(binop.left.col_offset, 3)
194        self.assertEqual(binop.right.col_offset, 7)
195        # check the nested call location
196        self.assertEqual(len(binop.right.values), 3)
197        self.assertEqual(type(binop.right.values[0]), ast.Constant)
198        self.assertEqual(type(binop.right.values[0].value), str)
199        self.assertEqual(type(binop.right.values[1]), ast.FormattedValue)
200        self.assertEqual(type(binop.right.values[2]), ast.Constant)
201        self.assertEqual(type(binop.right.values[2].value), str)
202        self.assertEqual(binop.right.values[0].lineno, 3)
203        self.assertEqual(binop.right.values[1].lineno, 3)
204        self.assertEqual(binop.right.values[2].lineno, 3)
205        call = binop.right.values[1].value
206        self.assertEqual(type(call), ast.Call)
207        self.assertEqual(call.lineno, 3)
208        self.assertEqual(call.col_offset, 11)
209
210    def test_ast_line_numbers_duplicate_expression(self):
211        """Duplicate expression
212
213        NOTE: this is currently broken, always sets location of the first
214        expression.
215        """
216        expr = """
217a = 10
218f'{a * x()} {a * x()} {a * x()}'
219"""
220        t = ast.parse(expr)
221        self.assertEqual(type(t), ast.Module)
222        self.assertEqual(len(t.body), 2)
223        # check `a = 10`
224        self.assertEqual(type(t.body[0]), ast.Assign)
225        self.assertEqual(t.body[0].lineno, 2)
226        # check `f'...'`
227        self.assertEqual(type(t.body[1]), ast.Expr)
228        self.assertEqual(type(t.body[1].value), ast.JoinedStr)
229        self.assertEqual(len(t.body[1].value.values), 5)
230        self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)
231        self.assertEqual(type(t.body[1].value.values[1]), ast.Constant)
232        self.assertEqual(type(t.body[1].value.values[1].value), str)
233        self.assertEqual(type(t.body[1].value.values[2]), ast.FormattedValue)
234        self.assertEqual(type(t.body[1].value.values[3]), ast.Constant)
235        self.assertEqual(type(t.body[1].value.values[3].value), str)
236        self.assertEqual(type(t.body[1].value.values[4]), ast.FormattedValue)
237        self.assertEqual(t.body[1].lineno, 3)
238        self.assertEqual(t.body[1].value.lineno, 3)
239        self.assertEqual(t.body[1].value.values[0].lineno, 3)
240        self.assertEqual(t.body[1].value.values[1].lineno, 3)
241        self.assertEqual(t.body[1].value.values[2].lineno, 3)
242        self.assertEqual(t.body[1].value.values[3].lineno, 3)
243        self.assertEqual(t.body[1].value.values[4].lineno, 3)
244        # check the first binop location
245        binop = t.body[1].value.values[0].value
246        self.assertEqual(type(binop), ast.BinOp)
247        self.assertEqual(type(binop.left), ast.Name)
248        self.assertEqual(type(binop.op), ast.Mult)
249        self.assertEqual(type(binop.right), ast.Call)
250        self.assertEqual(binop.lineno, 3)
251        self.assertEqual(binop.left.lineno, 3)
252        self.assertEqual(binop.right.lineno, 3)
253        self.assertEqual(binop.col_offset, 3)
254        self.assertEqual(binop.left.col_offset, 3)
255        self.assertEqual(binop.right.col_offset, 7)
256        # check the second binop location
257        binop = t.body[1].value.values[2].value
258        self.assertEqual(type(binop), ast.BinOp)
259        self.assertEqual(type(binop.left), ast.Name)
260        self.assertEqual(type(binop.op), ast.Mult)
261        self.assertEqual(type(binop.right), ast.Call)
262        self.assertEqual(binop.lineno, 3)
263        self.assertEqual(binop.left.lineno, 3)
264        self.assertEqual(binop.right.lineno, 3)
265        self.assertEqual(binop.col_offset, 3)  # FIXME: this is wrong
266        self.assertEqual(binop.left.col_offset, 3)  # FIXME: this is wrong
267        self.assertEqual(binop.right.col_offset, 7)  # FIXME: this is wrong
268        # check the third binop location
269        binop = t.body[1].value.values[4].value
270        self.assertEqual(type(binop), ast.BinOp)
271        self.assertEqual(type(binop.left), ast.Name)
272        self.assertEqual(type(binop.op), ast.Mult)
273        self.assertEqual(type(binop.right), ast.Call)
274        self.assertEqual(binop.lineno, 3)
275        self.assertEqual(binop.left.lineno, 3)
276        self.assertEqual(binop.right.lineno, 3)
277        self.assertEqual(binop.col_offset, 3)  # FIXME: this is wrong
278        self.assertEqual(binop.left.col_offset, 3)  # FIXME: this is wrong
279        self.assertEqual(binop.right.col_offset, 7)  # FIXME: this is wrong
280
281    def test_ast_line_numbers_multiline_fstring(self):
282        # See bpo-30465 for details.
283        expr = """
284a = 10
285f'''
286  {a
287     *
288       x()}
289non-important content
290'''
291"""
292        t = ast.parse(expr)
293        self.assertEqual(type(t), ast.Module)
294        self.assertEqual(len(t.body), 2)
295        # check `a = 10`
296        self.assertEqual(type(t.body[0]), ast.Assign)
297        self.assertEqual(t.body[0].lineno, 2)
298        # check `f'...'`
299        self.assertEqual(type(t.body[1]), ast.Expr)
300        self.assertEqual(type(t.body[1].value), ast.JoinedStr)
301        self.assertEqual(len(t.body[1].value.values), 3)
302        self.assertEqual(type(t.body[1].value.values[0]), ast.Constant)
303        self.assertEqual(type(t.body[1].value.values[0].value), str)
304        self.assertEqual(type(t.body[1].value.values[1]), ast.FormattedValue)
305        self.assertEqual(type(t.body[1].value.values[2]), ast.Constant)
306        self.assertEqual(type(t.body[1].value.values[2].value), str)
307        self.assertEqual(t.body[1].lineno, 3)
308        self.assertEqual(t.body[1].value.lineno, 3)
309        self.assertEqual(t.body[1].value.values[0].lineno, 3)
310        self.assertEqual(t.body[1].value.values[1].lineno, 3)
311        self.assertEqual(t.body[1].value.values[2].lineno, 3)
312        self.assertEqual(t.body[1].col_offset, 0)
313        self.assertEqual(t.body[1].value.col_offset, 0)
314        self.assertEqual(t.body[1].value.values[0].col_offset, 0)
315        self.assertEqual(t.body[1].value.values[1].col_offset, 0)
316        self.assertEqual(t.body[1].value.values[2].col_offset, 0)
317        # NOTE: the following lineno information and col_offset is correct for
318        # expressions within FormattedValues.
319        binop = t.body[1].value.values[1].value
320        self.assertEqual(type(binop), ast.BinOp)
321        self.assertEqual(type(binop.left), ast.Name)
322        self.assertEqual(type(binop.op), ast.Mult)
323        self.assertEqual(type(binop.right), ast.Call)
324        self.assertEqual(binop.lineno, 4)
325        self.assertEqual(binop.left.lineno, 4)
326        self.assertEqual(binop.right.lineno, 6)
327        self.assertEqual(binop.col_offset, 4)
328        self.assertEqual(binop.left.col_offset, 4)
329        self.assertEqual(binop.right.col_offset, 7)
330
331    def test_docstring(self):
332        def f():
333            f'''Not a docstring'''
334        self.assertIsNone(f.__doc__)
335        def g():
336            '''Not a docstring''' \
337            f''
338        self.assertIsNone(g.__doc__)
339
340    def test_literal_eval(self):
341        with self.assertRaisesRegex(ValueError, 'malformed node or string'):
342            ast.literal_eval("f'x'")
343
344    def test_ast_compile_time_concat(self):
345        x = ['']
346
347        expr = """x[0] = 'foo' f'{3}'"""
348        t = ast.parse(expr)
349        c = compile(t, '', 'exec')
350        exec(c)
351        self.assertEqual(x[0], 'foo3')
352
353    def test_compile_time_concat_errors(self):
354        self.assertAllRaise(SyntaxError,
355                            'cannot mix bytes and nonbytes literals',
356                            [r"""f'' b''""",
357                             r"""b'' f''""",
358                             ])
359
360    def test_literal(self):
361        self.assertEqual(f'', '')
362        self.assertEqual(f'a', 'a')
363        self.assertEqual(f' ', ' ')
364
365    def test_unterminated_string(self):
366        self.assertAllRaise(SyntaxError, 'f-string: unterminated string',
367                            [r"""f'{"x'""",
368                             r"""f'{"x}'""",
369                             r"""f'{("x'""",
370                             r"""f'{("x}'""",
371                             ])
372
373    def test_mismatched_parens(self):
374        self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\}' "
375                            r"does not match opening parenthesis '\('",
376                            ["f'{((}'",
377                             ])
378        self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\)' "
379                            r"does not match opening parenthesis '\['",
380                            ["f'{a[4)}'",
381                            ])
382        self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\]' "
383                            r"does not match opening parenthesis '\('",
384                            ["f'{a(4]}'",
385                            ])
386        self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\}' "
387                            r"does not match opening parenthesis '\['",
388                            ["f'{a[4}'",
389                            ])
390        self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\}' "
391                            r"does not match opening parenthesis '\('",
392                            ["f'{a(4}'",
393                            ])
394        self.assertRaises(SyntaxError, eval, "f'{" + "("*500 + "}'")
395
396    def test_double_braces(self):
397        self.assertEqual(f'{{', '{')
398        self.assertEqual(f'a{{', 'a{')
399        self.assertEqual(f'{{b', '{b')
400        self.assertEqual(f'a{{b', 'a{b')
401        self.assertEqual(f'}}', '}')
402        self.assertEqual(f'a}}', 'a}')
403        self.assertEqual(f'}}b', '}b')
404        self.assertEqual(f'a}}b', 'a}b')
405        self.assertEqual(f'{{}}', '{}')
406        self.assertEqual(f'a{{}}', 'a{}')
407        self.assertEqual(f'{{b}}', '{b}')
408        self.assertEqual(f'{{}}c', '{}c')
409        self.assertEqual(f'a{{b}}', 'a{b}')
410        self.assertEqual(f'a{{}}c', 'a{}c')
411        self.assertEqual(f'{{b}}c', '{b}c')
412        self.assertEqual(f'a{{b}}c', 'a{b}c')
413
414        self.assertEqual(f'{{{10}', '{10')
415        self.assertEqual(f'}}{10}', '}10')
416        self.assertEqual(f'}}{{{10}', '}{10')
417        self.assertEqual(f'}}a{{{10}', '}a{10')
418
419        self.assertEqual(f'{10}{{', '10{')
420        self.assertEqual(f'{10}}}', '10}')
421        self.assertEqual(f'{10}}}{{', '10}{')
422        self.assertEqual(f'{10}}}a{{' '}', '10}a{}')
423
424        # Inside of strings, don't interpret doubled brackets.
425        self.assertEqual(f'{"{{}}"}', '{{}}')
426
427        self.assertAllRaise(TypeError, 'unhashable type',
428                            ["f'{ {{}} }'", # dict in a set
429                             ])
430
431    def test_compile_time_concat(self):
432        x = 'def'
433        self.assertEqual('abc' f'## {x}ghi', 'abc## defghi')
434        self.assertEqual('abc' f'{x}' 'ghi', 'abcdefghi')
435        self.assertEqual('abc' f'{x}' 'gh' f'i{x:4}', 'abcdefghidef ')
436        self.assertEqual('{x}' f'{x}', '{x}def')
437        self.assertEqual('{x' f'{x}', '{xdef')
438        self.assertEqual('{x}' f'{x}', '{x}def')
439        self.assertEqual('{{x}}' f'{x}', '{{x}}def')
440        self.assertEqual('{{x' f'{x}', '{{xdef')
441        self.assertEqual('x}}' f'{x}', 'x}}def')
442        self.assertEqual(f'{x}' 'x}}', 'defx}}')
443        self.assertEqual(f'{x}' '', 'def')
444        self.assertEqual('' f'{x}' '', 'def')
445        self.assertEqual('' f'{x}', 'def')
446        self.assertEqual(f'{x}' '2', 'def2')
447        self.assertEqual('1' f'{x}' '2', '1def2')
448        self.assertEqual('1' f'{x}', '1def')
449        self.assertEqual(f'{x}' f'-{x}', 'def-def')
450        self.assertEqual('' f'', '')
451        self.assertEqual('' f'' '', '')
452        self.assertEqual('' f'' '' f'', '')
453        self.assertEqual(f'', '')
454        self.assertEqual(f'' '', '')
455        self.assertEqual(f'' '' f'', '')
456        self.assertEqual(f'' '' f'' '', '')
457
458        self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
459                            ["f'{3' f'}'",  # can't concat to get a valid f-string
460                             ])
461
462    def test_comments(self):
463        # These aren't comments, since they're in strings.
464        d = {'#': 'hash'}
465        self.assertEqual(f'{"#"}', '#')
466        self.assertEqual(f'{d["#"]}', 'hash')
467
468        self.assertAllRaise(SyntaxError, "f-string expression part cannot include '#'",
469                            ["f'{1#}'",   # error because the expression becomes "(1#)"
470                             "f'{3(#)}'",
471                             "f'{#}'",
472                             ])
473        self.assertAllRaise(SyntaxError, r"f-string: unmatched '\)'",
474                            ["f'{)#}'",   # When wrapped in parens, this becomes
475                                          #  '()#)'.  Make sure that doesn't compile.
476                             ])
477
478    def test_many_expressions(self):
479        # Create a string with many expressions in it. Note that
480        #  because we have a space in here as a literal, we're actually
481        #  going to use twice as many ast nodes: one for each literal
482        #  plus one for each expression.
483        def build_fstr(n, extra=''):
484            return "f'" + ('{x} ' * n) + extra + "'"
485
486        x = 'X'
487        width = 1
488
489        # Test around 256.
490        for i in range(250, 260):
491            self.assertEqual(eval(build_fstr(i)), (x+' ')*i)
492
493        # Test concatenating 2 largs fstrings.
494        self.assertEqual(eval(build_fstr(255)*256), (x+' ')*(255*256))
495
496        s = build_fstr(253, '{x:{width}} ')
497        self.assertEqual(eval(s), (x+' ')*254)
498
499        # Test lots of expressions and constants, concatenated.
500        s = "f'{1}' 'x' 'y'" * 1024
501        self.assertEqual(eval(s), '1xy' * 1024)
502
503    def test_format_specifier_expressions(self):
504        width = 10
505        precision = 4
506        value = decimal.Decimal('12.34567')
507        self.assertEqual(f'result: {value:{width}.{precision}}', 'result:      12.35')
508        self.assertEqual(f'result: {value:{width!r}.{precision}}', 'result:      12.35')
509        self.assertEqual(f'result: {value:{width:0}.{precision:1}}', 'result:      12.35')
510        self.assertEqual(f'result: {value:{1}{0:0}.{precision:1}}', 'result:      12.35')
511        self.assertEqual(f'result: {value:{ 1}{ 0:0}.{ precision:1}}', 'result:      12.35')
512        self.assertEqual(f'{10:#{1}0x}', '       0xa')
513        self.assertEqual(f'{10:{"#"}1{0}{"x"}}', '       0xa')
514        self.assertEqual(f'{-10:-{"#"}1{0}x}', '      -0xa')
515        self.assertEqual(f'{-10:{"-"}#{1}0{"x"}}', '      -0xa')
516        self.assertEqual(f'{10:#{3 != {4:5} and width}x}', '       0xa')
517
518        self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
519                            ["""f'{"s"!r{":10"}}'""",
520
521                             # This looks like a nested format spec.
522                             ])
523
524        self.assertAllRaise(SyntaxError, "invalid syntax",
525                            [# Invalid syntax inside a nested spec.
526                             "f'{4:{/5}}'",
527                             ])
528
529        self.assertAllRaise(SyntaxError, "f-string: expressions nested too deeply",
530                            [# Can't nest format specifiers.
531                             "f'result: {value:{width:{0}}.{precision:1}}'",
532                             ])
533
534        self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character',
535                            [# No expansion inside conversion or for
536                             #  the : or ! itself.
537                             """f'{"s"!{"r"}}'""",
538                             ])
539
540    def test_side_effect_order(self):
541        class X:
542            def __init__(self):
543                self.i = 0
544            def __format__(self, spec):
545                self.i += 1
546                return str(self.i)
547
548        x = X()
549        self.assertEqual(f'{x} {x}', '1 2')
550
551    def test_missing_expression(self):
552        self.assertAllRaise(SyntaxError, 'f-string: empty expression not allowed',
553                            ["f'{}'",
554                             "f'{ }'"
555                             "f' {} '",
556                             "f'{!r}'",
557                             "f'{ !r}'",
558                             "f'{10:{ }}'",
559                             "f' { } '",
560
561                             # The Python parser ignores also the following
562                             # whitespace characters in additional to a space.
563                             "f'''{\t\f\r\n}'''",
564
565                             # Catch the empty expression before the
566                             #  invalid conversion.
567                             "f'{!x}'",
568                             "f'{ !xr}'",
569                             "f'{!x:}'",
570                             "f'{!x:a}'",
571                             "f'{ !xr:}'",
572                             "f'{ !xr:a}'",
573
574                             "f'{!}'",
575                             "f'{:}'",
576
577                             # We find the empty expression before the
578                             #  missing closing brace.
579                             "f'{!'",
580                             "f'{!s:'",
581                             "f'{:'",
582                             "f'{:x'",
583                             ])
584
585        # Different error message is raised for other whitespace characters.
586        self.assertAllRaise(SyntaxError, 'invalid character in identifier',
587                            ["f'''{\xa0}'''",
588                             "\xa0",
589                             ])
590
591    def test_parens_in_expressions(self):
592        self.assertEqual(f'{3,}', '(3,)')
593
594        # Add these because when an expression is evaluated, parens
595        #  are added around it. But we shouldn't go from an invalid
596        #  expression to a valid one. The added parens are just
597        #  supposed to allow whitespace (including newlines).
598        self.assertAllRaise(SyntaxError, 'invalid syntax',
599                            ["f'{,}'",
600                             "f'{,}'",  # this is (,), which is an error
601                             ])
602
603        self.assertAllRaise(SyntaxError, r"f-string: unmatched '\)'",
604                            ["f'{3)+(4}'",
605                             ])
606
607        self.assertAllRaise(SyntaxError, 'EOL while scanning string literal',
608                            ["f'{\n}'",
609                             ])
610
611    def test_backslashes_in_string_part(self):
612        self.assertEqual(f'\t', '\t')
613        self.assertEqual(r'\t', '\\t')
614        self.assertEqual(rf'\t', '\\t')
615        self.assertEqual(f'{2}\t', '2\t')
616        self.assertEqual(f'{2}\t{3}', '2\t3')
617        self.assertEqual(f'\t{3}', '\t3')
618
619        self.assertEqual(f'\u0394', '\u0394')
620        self.assertEqual(r'\u0394', '\\u0394')
621        self.assertEqual(rf'\u0394', '\\u0394')
622        self.assertEqual(f'{2}\u0394', '2\u0394')
623        self.assertEqual(f'{2}\u0394{3}', '2\u03943')
624        self.assertEqual(f'\u0394{3}', '\u03943')
625
626        self.assertEqual(f'\U00000394', '\u0394')
627        self.assertEqual(r'\U00000394', '\\U00000394')
628        self.assertEqual(rf'\U00000394', '\\U00000394')
629        self.assertEqual(f'{2}\U00000394', '2\u0394')
630        self.assertEqual(f'{2}\U00000394{3}', '2\u03943')
631        self.assertEqual(f'\U00000394{3}', '\u03943')
632
633        self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}', '\u0394')
634        self.assertEqual(f'{2}\N{GREEK CAPITAL LETTER DELTA}', '2\u0394')
635        self.assertEqual(f'{2}\N{GREEK CAPITAL LETTER DELTA}{3}', '2\u03943')
636        self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}{3}', '\u03943')
637        self.assertEqual(f'2\N{GREEK CAPITAL LETTER DELTA}', '2\u0394')
638        self.assertEqual(f'2\N{GREEK CAPITAL LETTER DELTA}3', '2\u03943')
639        self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}3', '\u03943')
640
641        self.assertEqual(f'\x20', ' ')
642        self.assertEqual(r'\x20', '\\x20')
643        self.assertEqual(rf'\x20', '\\x20')
644        self.assertEqual(f'{2}\x20', '2 ')
645        self.assertEqual(f'{2}\x20{3}', '2 3')
646        self.assertEqual(f'\x20{3}', ' 3')
647
648        self.assertEqual(f'2\x20', '2 ')
649        self.assertEqual(f'2\x203', '2 3')
650        self.assertEqual(f'\x203', ' 3')
651
652        with self.assertWarns(DeprecationWarning):  # invalid escape sequence
653            value = eval(r"f'\{6*7}'")
654        self.assertEqual(value, '\\42')
655        self.assertEqual(f'\\{6*7}', '\\42')
656        self.assertEqual(fr'\{6*7}', '\\42')
657
658        AMPERSAND = 'spam'
659        # Get the right unicode character (&), or pick up local variable
660        # depending on the number of backslashes.
661        self.assertEqual(f'\N{AMPERSAND}', '&')
662        self.assertEqual(f'\\N{AMPERSAND}', '\\Nspam')
663        self.assertEqual(fr'\N{AMPERSAND}', '\\Nspam')
664        self.assertEqual(f'\\\N{AMPERSAND}', '\\&')
665
666    def test_misformed_unicode_character_name(self):
667        # These test are needed because unicode names are parsed
668        # differently inside f-strings.
669        self.assertAllRaise(SyntaxError, r"\(unicode error\) 'unicodeescape' codec can't decode bytes in position .*: malformed \\N character escape",
670                            [r"f'\N'",
671                             r"f'\N{'",
672                             r"f'\N{GREEK CAPITAL LETTER DELTA'",
673
674                             # Here are the non-f-string versions,
675                             #  which should give the same errors.
676                             r"'\N'",
677                             r"'\N{'",
678                             r"'\N{GREEK CAPITAL LETTER DELTA'",
679                             ])
680
681    def test_no_backslashes_in_expression_part(self):
682        self.assertAllRaise(SyntaxError, 'f-string expression part cannot include a backslash',
683                            [r"f'{\'a\'}'",
684                             r"f'{\t3}'",
685                             r"f'{\}'",
686                             r"rf'{\'a\'}'",
687                             r"rf'{\t3}'",
688                             r"rf'{\}'",
689                             r"""rf'{"\N{LEFT CURLY BRACKET}"}'""",
690                             r"f'{\n}'",
691                             ])
692
693    def test_no_escapes_for_braces(self):
694        """
695        Only literal curly braces begin an expression.
696        """
697        # \x7b is '{'.
698        self.assertEqual(f'\x7b1+1}}', '{1+1}')
699        self.assertEqual(f'\x7b1+1', '{1+1')
700        self.assertEqual(f'\u007b1+1', '{1+1')
701        self.assertEqual(f'\N{LEFT CURLY BRACKET}1+1\N{RIGHT CURLY BRACKET}', '{1+1}')
702
703    def test_newlines_in_expressions(self):
704        self.assertEqual(f'{0}', '0')
705        self.assertEqual(rf'''{3+
7064}''', '7')
707
708    def test_lambda(self):
709        x = 5
710        self.assertEqual(f'{(lambda y:x*y)("8")!r}', "'88888'")
711        self.assertEqual(f'{(lambda y:x*y)("8")!r:10}', "'88888'   ")
712        self.assertEqual(f'{(lambda y:x*y)("8"):10}', "88888     ")
713
714        # lambda doesn't work without parens, because the colon
715        #  makes the parser think it's a format_spec
716        self.assertAllRaise(SyntaxError, 'unexpected EOF while parsing',
717                            ["f'{lambda x:x}'",
718                             ])
719
720    def test_yield(self):
721        # Not terribly useful, but make sure the yield turns
722        #  a function into a generator
723        def fn(y):
724            f'y:{yield y*2}'
725
726        g = fn(4)
727        self.assertEqual(next(g), 8)
728
729    def test_yield_send(self):
730        def fn(x):
731            yield f'x:{yield (lambda i: x * i)}'
732
733        g = fn(10)
734        the_lambda = next(g)
735        self.assertEqual(the_lambda(4), 40)
736        self.assertEqual(g.send('string'), 'x:string')
737
738    def test_expressions_with_triple_quoted_strings(self):
739        self.assertEqual(f"{'''x'''}", 'x')
740        self.assertEqual(f"{'''eric's'''}", "eric's")
741
742        # Test concatenation within an expression
743        self.assertEqual(f'{"x" """eric"s""" "y"}', 'xeric"sy')
744        self.assertEqual(f'{"x" """eric"s"""}', 'xeric"s')
745        self.assertEqual(f'{"""eric"s""" "y"}', 'eric"sy')
746        self.assertEqual(f'{"""x""" """eric"s""" "y"}', 'xeric"sy')
747        self.assertEqual(f'{"""x""" """eric"s""" """y"""}', 'xeric"sy')
748        self.assertEqual(f'{r"""x""" """eric"s""" """y"""}', 'xeric"sy')
749
750    def test_multiple_vars(self):
751        x = 98
752        y = 'abc'
753        self.assertEqual(f'{x}{y}', '98abc')
754
755        self.assertEqual(f'X{x}{y}', 'X98abc')
756        self.assertEqual(f'{x}X{y}', '98Xabc')
757        self.assertEqual(f'{x}{y}X', '98abcX')
758
759        self.assertEqual(f'X{x}Y{y}', 'X98Yabc')
760        self.assertEqual(f'X{x}{y}Y', 'X98abcY')
761        self.assertEqual(f'{x}X{y}Y', '98XabcY')
762
763        self.assertEqual(f'X{x}Y{y}Z', 'X98YabcZ')
764
765    def test_closure(self):
766        def outer(x):
767            def inner():
768                return f'x:{x}'
769            return inner
770
771        self.assertEqual(outer('987')(), 'x:987')
772        self.assertEqual(outer(7)(), 'x:7')
773
774    def test_arguments(self):
775        y = 2
776        def f(x, width):
777            return f'x={x*y:{width}}'
778
779        self.assertEqual(f('foo', 10), 'x=foofoo    ')
780        x = 'bar'
781        self.assertEqual(f(10, 10), 'x=        20')
782
783    def test_locals(self):
784        value = 123
785        self.assertEqual(f'v:{value}', 'v:123')
786
787    def test_missing_variable(self):
788        with self.assertRaises(NameError):
789            f'v:{value}'
790
791    def test_missing_format_spec(self):
792        class O:
793            def __format__(self, spec):
794                if not spec:
795                    return '*'
796                return spec
797
798        self.assertEqual(f'{O():x}', 'x')
799        self.assertEqual(f'{O()}', '*')
800        self.assertEqual(f'{O():}', '*')
801
802        self.assertEqual(f'{3:}', '3')
803        self.assertEqual(f'{3!s:}', '3')
804
805    def test_global(self):
806        self.assertEqual(f'g:{a_global}', 'g:global variable')
807        self.assertEqual(f'g:{a_global!r}', "g:'global variable'")
808
809        a_local = 'local variable'
810        self.assertEqual(f'g:{a_global} l:{a_local}',
811                         'g:global variable l:local variable')
812        self.assertEqual(f'g:{a_global!r}',
813                         "g:'global variable'")
814        self.assertEqual(f'g:{a_global} l:{a_local!r}',
815                         "g:global variable l:'local variable'")
816
817        self.assertIn("module 'unittest' from", f'{unittest}')
818
819    def test_shadowed_global(self):
820        a_global = 'really a local'
821        self.assertEqual(f'g:{a_global}', 'g:really a local')
822        self.assertEqual(f'g:{a_global!r}', "g:'really a local'")
823
824        a_local = 'local variable'
825        self.assertEqual(f'g:{a_global} l:{a_local}',
826                         'g:really a local l:local variable')
827        self.assertEqual(f'g:{a_global!r}',
828                         "g:'really a local'")
829        self.assertEqual(f'g:{a_global} l:{a_local!r}',
830                         "g:really a local l:'local variable'")
831
832    def test_call(self):
833        def foo(x):
834            return 'x=' + str(x)
835
836        self.assertEqual(f'{foo(10)}', 'x=10')
837
838    def test_nested_fstrings(self):
839        y = 5
840        self.assertEqual(f'{f"{0}"*3}', '000')
841        self.assertEqual(f'{f"{y}"*3}', '555')
842
843    def test_invalid_string_prefixes(self):
844        self.assertAllRaise(SyntaxError, 'unexpected EOF while parsing',
845                            ["fu''",
846                             "uf''",
847                             "Fu''",
848                             "fU''",
849                             "Uf''",
850                             "uF''",
851                             "ufr''",
852                             "urf''",
853                             "fur''",
854                             "fru''",
855                             "rfu''",
856                             "ruf''",
857                             "FUR''",
858                             "Fur''",
859                             "fb''",
860                             "fB''",
861                             "Fb''",
862                             "FB''",
863                             "bf''",
864                             "bF''",
865                             "Bf''",
866                             "BF''",
867                             ])
868
869    def test_leading_trailing_spaces(self):
870        self.assertEqual(f'{ 3}', '3')
871        self.assertEqual(f'{  3}', '3')
872        self.assertEqual(f'{3 }', '3')
873        self.assertEqual(f'{3  }', '3')
874
875        self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]}}',
876                         'expr={1: 2}')
877        self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]} }',
878                         'expr={1: 2}')
879
880    def test_not_equal(self):
881        # There's a special test for this because there's a special
882        #  case in the f-string parser to look for != as not ending an
883        #  expression. Normally it would, while looking for !s or !r.
884
885        self.assertEqual(f'{3!=4}', 'True')
886        self.assertEqual(f'{3!=4:}', 'True')
887        self.assertEqual(f'{3!=4!s}', 'True')
888        self.assertEqual(f'{3!=4!s:.3}', 'Tru')
889
890    def test_equal_equal(self):
891        # Because an expression ending in = has special meaning,
892        # there's a special test for ==. Make sure it works.
893
894        self.assertEqual(f'{0==1}', 'False')
895
896    def test_conversions(self):
897        self.assertEqual(f'{3.14:10.10}', '      3.14')
898        self.assertEqual(f'{3.14!s:10.10}', '3.14      ')
899        self.assertEqual(f'{3.14!r:10.10}', '3.14      ')
900        self.assertEqual(f'{3.14!a:10.10}', '3.14      ')
901
902        self.assertEqual(f'{"a"}', 'a')
903        self.assertEqual(f'{"a"!r}', "'a'")
904        self.assertEqual(f'{"a"!a}', "'a'")
905
906        # Not a conversion.
907        self.assertEqual(f'{"a!r"}', "a!r")
908
909        # Not a conversion, but show that ! is allowed in a format spec.
910        self.assertEqual(f'{3.14:!<10.10}', '3.14!!!!!!')
911
912        self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character',
913                            ["f'{3!g}'",
914                             "f'{3!A}'",
915                             "f'{3!3}'",
916                             "f'{3!G}'",
917                             "f'{3!!}'",
918                             "f'{3!:}'",
919                             "f'{3! s}'",  # no space before conversion char
920                             ])
921
922        self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
923                            ["f'{x!s{y}}'",
924                             "f'{3!ss}'",
925                             "f'{3!ss:}'",
926                             "f'{3!ss:s}'",
927                             ])
928
929    def test_assignment(self):
930        self.assertAllRaise(SyntaxError, 'invalid syntax',
931                            ["f'' = 3",
932                             "f'{0}' = x",
933                             "f'{x}' = x",
934                             ])
935
936    def test_del(self):
937        self.assertAllRaise(SyntaxError, 'invalid syntax',
938                            ["del f''",
939                             "del '' f''",
940                             ])
941
942    def test_mismatched_braces(self):
943        self.assertAllRaise(SyntaxError, "f-string: single '}' is not allowed",
944                            ["f'{{}'",
945                             "f'{{}}}'",
946                             "f'}'",
947                             "f'x}'",
948                             "f'x}x'",
949                             r"f'\u007b}'",
950
951                             # Can't have { or } in a format spec.
952                             "f'{3:}>10}'",
953                             "f'{3:}}>10}'",
954                             ])
955
956        self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
957                            ["f'{3:{{>10}'",
958                             "f'{3'",
959                             "f'{3!'",
960                             "f'{3:'",
961                             "f'{3!s'",
962                             "f'{3!s:'",
963                             "f'{3!s:3'",
964                             "f'x{'",
965                             "f'x{x'",
966                             "f'{x'",
967                             "f'{3:s'",
968                             "f'{{{'",
969                             "f'{{}}{'",
970                             "f'{'",
971                             ])
972
973        # But these are just normal strings.
974        self.assertEqual(f'{"{"}', '{')
975        self.assertEqual(f'{"}"}', '}')
976        self.assertEqual(f'{3:{"}"}>10}', '}}}}}}}}}3')
977        self.assertEqual(f'{2:{"{"}>10}', '{{{{{{{{{2')
978
979    def test_if_conditional(self):
980        # There's special logic in compile.c to test if the
981        #  conditional for an if (and while) are constants. Exercise
982        #  that code.
983
984        def test_fstring(x, expected):
985            flag = 0
986            if f'{x}':
987                flag = 1
988            else:
989                flag = 2
990            self.assertEqual(flag, expected)
991
992        def test_concat_empty(x, expected):
993            flag = 0
994            if '' f'{x}':
995                flag = 1
996            else:
997                flag = 2
998            self.assertEqual(flag, expected)
999
1000        def test_concat_non_empty(x, expected):
1001            flag = 0
1002            if ' ' f'{x}':
1003                flag = 1
1004            else:
1005                flag = 2
1006            self.assertEqual(flag, expected)
1007
1008        test_fstring('', 2)
1009        test_fstring(' ', 1)
1010
1011        test_concat_empty('', 2)
1012        test_concat_empty(' ', 1)
1013
1014        test_concat_non_empty('', 1)
1015        test_concat_non_empty(' ', 1)
1016
1017    def test_empty_format_specifier(self):
1018        x = 'test'
1019        self.assertEqual(f'{x}', 'test')
1020        self.assertEqual(f'{x:}', 'test')
1021        self.assertEqual(f'{x!s:}', 'test')
1022        self.assertEqual(f'{x!r:}', "'test'")
1023
1024    def test_str_format_differences(self):
1025        d = {'a': 'string',
1026             0: 'integer',
1027             }
1028        a = 0
1029        self.assertEqual(f'{d[0]}', 'integer')
1030        self.assertEqual(f'{d["a"]}', 'string')
1031        self.assertEqual(f'{d[a]}', 'integer')
1032        self.assertEqual('{d[a]}'.format(d=d), 'string')
1033        self.assertEqual('{d[0]}'.format(d=d), 'integer')
1034
1035    def test_errors(self):
1036        # see issue 26287
1037        self.assertAllRaise(TypeError, 'unsupported',
1038                            [r"f'{(lambda: 0):x}'",
1039                             r"f'{(0,):x}'",
1040                             ])
1041        self.assertAllRaise(ValueError, 'Unknown format code',
1042                            [r"f'{1000:j}'",
1043                             r"f'{1000:j}'",
1044                            ])
1045
1046    def test_loop(self):
1047        for i in range(1000):
1048            self.assertEqual(f'i:{i}', 'i:' + str(i))
1049
1050    def test_dict(self):
1051        d = {'"': 'dquote',
1052             "'": 'squote',
1053             'foo': 'bar',
1054             }
1055        self.assertEqual(f'''{d["'"]}''', 'squote')
1056        self.assertEqual(f"""{d['"']}""", 'dquote')
1057
1058        self.assertEqual(f'{d["foo"]}', 'bar')
1059        self.assertEqual(f"{d['foo']}", 'bar')
1060
1061    def test_backslash_char(self):
1062        # Check eval of a backslash followed by a control char.
1063        # See bpo-30682: this used to raise an assert in pydebug mode.
1064        self.assertEqual(eval('f"\\\n"'), '')
1065        self.assertEqual(eval('f"\\\r"'), '')
1066
1067    def test_debug_conversion(self):
1068        x = 'A string'
1069        self.assertEqual(f'{x=}', 'x=' + repr(x))
1070        self.assertEqual(f'{x =}', 'x =' + repr(x))
1071        self.assertEqual(f'{x=!s}', 'x=' + str(x))
1072        self.assertEqual(f'{x=!r}', 'x=' + repr(x))
1073        self.assertEqual(f'{x=!a}', 'x=' + ascii(x))
1074
1075        x = 2.71828
1076        self.assertEqual(f'{x=:.2f}', 'x=' + format(x, '.2f'))
1077        self.assertEqual(f'{x=:}', 'x=' + format(x, ''))
1078        self.assertEqual(f'{x=!r:^20}', 'x=' + format(repr(x), '^20'))
1079        self.assertEqual(f'{x=!s:^20}', 'x=' + format(str(x), '^20'))
1080        self.assertEqual(f'{x=!a:^20}', 'x=' + format(ascii(x), '^20'))
1081
1082        x = 9
1083        self.assertEqual(f'{3*x+15=}', '3*x+15=42')
1084
1085        # There is code in ast.c that deals with non-ascii expression values.  So,
1086        # use a unicode identifier to trigger that.
1087        tenπ = 31.4
1088        self.assertEqual(f'{tenπ=:.2f}', 'tenπ=31.40')
1089
1090        # Also test with Unicode in non-identifiers.
1091        self.assertEqual(f'{"Σ"=}', '"Σ"=\'Σ\'')
1092
1093        # Make sure nested fstrings still work.
1094        self.assertEqual(f'{f"{3.1415=:.1f}":*^20}', '*****3.1415=3.1*****')
1095
1096        # Make sure text before and after an expression with = works
1097        # correctly.
1098        pi = 'π'
1099        self.assertEqual(f'alpha α {pi=} ω omega', "alpha α pi='π' ω omega")
1100
1101        # Check multi-line expressions.
1102        self.assertEqual(f'''{
11033
1104=}''', '\n3\n=3')
1105
1106        # Since = is handled specially, make sure all existing uses of
1107        # it still work.
1108
1109        self.assertEqual(f'{0==1}', 'False')
1110        self.assertEqual(f'{0!=1}', 'True')
1111        self.assertEqual(f'{0<=1}', 'True')
1112        self.assertEqual(f'{0>=1}', 'False')
1113        self.assertEqual(f'{(x:="5")}', '5')
1114        self.assertEqual(x, '5')
1115        self.assertEqual(f'{(x:=5)}', '5')
1116        self.assertEqual(x, 5)
1117        self.assertEqual(f'{"="}', '=')
1118
1119        x = 20
1120        # This isn't an assignment expression, it's 'x', with a format
1121        # spec of '=10'.  See test_walrus: you need to use parens.
1122        self.assertEqual(f'{x:=10}', '        20')
1123
1124        # Test named function parameters, to make sure '=' parsing works
1125        # there.
1126        def f(a):
1127            nonlocal x
1128            oldx = x
1129            x = a
1130            return oldx
1131        x = 0
1132        self.assertEqual(f'{f(a="3=")}', '0')
1133        self.assertEqual(x, '3=')
1134        self.assertEqual(f'{f(a=4)}', '3=')
1135        self.assertEqual(x, 4)
1136
1137        # Make sure __format__ is being called.
1138        class C:
1139            def __format__(self, s):
1140                return f'FORMAT-{s}'
1141            def __repr__(self):
1142                return 'REPR'
1143
1144        self.assertEqual(f'{C()=}', 'C()=REPR')
1145        self.assertEqual(f'{C()=!r}', 'C()=REPR')
1146        self.assertEqual(f'{C()=:}', 'C()=FORMAT-')
1147        self.assertEqual(f'{C()=: }', 'C()=FORMAT- ')
1148        self.assertEqual(f'{C()=:x}', 'C()=FORMAT-x')
1149        self.assertEqual(f'{C()=!r:*^20}', 'C()=********REPR********')
1150
1151        self.assertRaises(SyntaxError, eval, "f'{C=]'")
1152
1153        # Make sure leading and following text works.
1154        x = 'foo'
1155        self.assertEqual(f'X{x=}Y', 'Xx='+repr(x)+'Y')
1156
1157        # Make sure whitespace around the = works.
1158        self.assertEqual(f'X{x  =}Y', 'Xx  ='+repr(x)+'Y')
1159        self.assertEqual(f'X{x=  }Y', 'Xx=  '+repr(x)+'Y')
1160        self.assertEqual(f'X{x  =  }Y', 'Xx  =  '+repr(x)+'Y')
1161
1162        # These next lines contains tabs.  Backslash escapes don't
1163        # work in f-strings.
1164        # patchcheck doesn't like these tabs.  So the only way to test
1165        # this will be to dynamically created and exec the f-strings.  But
1166        # that's such a hassle I'll save it for another day.  For now, convert
1167        # the tabs to spaces just to shut up patchcheck.
1168        #self.assertEqual(f'X{x =}Y', 'Xx\t='+repr(x)+'Y')
1169        #self.assertEqual(f'X{x =       }Y', 'Xx\t=\t'+repr(x)+'Y')
1170
1171    def test_walrus(self):
1172        x = 20
1173        # This isn't an assignment expression, it's 'x', with a format
1174        # spec of '=10'.
1175        self.assertEqual(f'{x:=10}', '        20')
1176
1177        # This is an assignment expression, which requires parens.
1178        self.assertEqual(f'{(x:=10)}', '10')
1179        self.assertEqual(x, 10)
1180
1181
1182if __name__ == '__main__':
1183    unittest.main()
1184