cython

Форк
0
/
test_fstring.pyx 
1231 строка · 48.3 Кб
1
# cython: language_level=3
2
# mode: run
3
# tag: allow_unknown_names, f_strings, pep498
4

5
import ast
6
import os
7
import types
8
import decimal
9
import unittest
10

11
from Cython.Build.Inline import cython_inline
12
from Cython.TestUtils import CythonTest
13
from Cython.Compiler.Errors import CompileError, hold_errors, init_thread, held_errors
14

15
def cy_eval(s, **kwargs):
16
    return cython_inline('return ' + s, force=True, **kwargs)
17

18
a_global = 'global variable'
19

20
# You could argue that I'm too strict in looking for specific error
21
#  values with assertRaisesRegex, but without it it's way too easy to
22
#  make a syntax error in the test strings. Especially with all of the
23
#  triple quotes, raw strings, backslashes, etc. I think it's a
24
#  worthwhile tradeoff. When I switched to this method, I found many
25
#  examples where I wasn't testing what I thought I was.
26

27
class TestCase(CythonTest):
28
    def assertAllRaise(self, exception_type, regex, error_strings):
29
        for str in error_strings:
30
            hold_errors()
31
            if exception_type is SyntaxError:
32
                try:
33
                    self.fragment(str)
34
                except CompileError:
35
                    assert True
36
                else:
37
                    assert held_errors(), "Invalid Cython code failed to raise SyntaxError: %r" % str
38
                finally:
39
                    init_thread()  # reset error status
40
            else:
41
                try:
42
                    cython_inline(str, quiet=True)
43
                except exception_type:
44
                    assert True
45
                else:
46
                    assert False, "Invalid Cython code failed to raise %s: %r" % (exception_type, str)
47
                finally:
48
                    init_thread()  # reset error status
49

50
    def test__format__lookup(self):
51
        # Make sure __format__ is looked up on the type, not the instance.
52
        class X:
53
            def __format__(self, spec):
54
                return 'class'
55

56
        x = X()
57

58
        # Add a bound __format__ method to the 'y' instance, but not
59
        #  the 'x' instance.
60
        y = X()
61
        y.__format__ = types.MethodType(lambda self, spec: 'instance', y)
62

63
        self.assertEqual(f'{y}', format(y))
64
        self.assertEqual(f'{y}', 'class')
65
        self.assertEqual(format(x), format(y))
66

67
        # __format__ is not called this way, but still make sure it
68
        #  returns what we expect (so we can make sure we're bypassing
69
        #  it).
70
        self.assertEqual(x.__format__(''), 'class')
71
        self.assertEqual(y.__format__(''), 'instance')
72

73
        # This is how __format__ is actually called.
74
        self.assertEqual(type(x).__format__(x, ''), 'class')
75
        self.assertEqual(type(y).__format__(y, ''), 'class')
76

77
    def __test_ast(self):
78
        # Inspired by https://bugs.python.org/issue24975
79
        class X:
80
            def __init__(self):
81
                self.called = False
82
            def __call__(self):
83
                self.called = True
84
                return 4
85
        x = X()
86
        expr = """
87
a = 10
88
f'{a * x()}'"""
89
        t = ast.parse(expr)
90
        c = compile(t, '', 'exec')
91

92
        # Make sure x was not called.
93
        self.assertFalse(x.called)
94

95
        # Actually run the code.
96
        exec(c)
97

98
        # Make sure x was called.
99
        self.assertTrue(x.called)
100

101
    def __test_ast_line_numbers(self):
102
        expr = """
103
a = 10
104
f'{a * x()}'"""
105
        t = ast.parse(expr)
106
        self.assertEqual(type(t), ast.Module)
107
        self.assertEqual(len(t.body), 2)
108
        # check `a = 10`
109
        self.assertEqual(type(t.body[0]), ast.Assign)
110
        self.assertEqual(t.body[0].lineno, 2)
111
        # check `f'...'`
112
        self.assertEqual(type(t.body[1]), ast.Expr)
113
        self.assertEqual(type(t.body[1].value), ast.JoinedStr)
114
        self.assertEqual(len(t.body[1].value.values), 1)
115
        self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)
116
        self.assertEqual(t.body[1].lineno, 3)
117
        self.assertEqual(t.body[1].value.lineno, 3)
118
        self.assertEqual(t.body[1].value.values[0].lineno, 3)
119
        # check the binop location
120
        binop = t.body[1].value.values[0].value
121
        self.assertEqual(type(binop), ast.BinOp)
122
        self.assertEqual(type(binop.left), ast.Name)
123
        self.assertEqual(type(binop.op), ast.Mult)
124
        self.assertEqual(type(binop.right), ast.Call)
125
        self.assertEqual(binop.lineno, 3)
126
        self.assertEqual(binop.left.lineno, 3)
127
        self.assertEqual(binop.right.lineno, 3)
128
        self.assertEqual(binop.col_offset, 3)
129
        self.assertEqual(binop.left.col_offset, 3)
130
        self.assertEqual(binop.right.col_offset, 7)
131

132
    def __test_ast_line_numbers_multiple_formattedvalues(self):
133
        expr = """
134
f'no formatted values'
135
f'eggs {a * x()} spam {b + y()}'"""
136
        t = ast.parse(expr)
137
        self.assertEqual(type(t), ast.Module)
138
        self.assertEqual(len(t.body), 2)
139
        # check `f'no formatted value'`
140
        self.assertEqual(type(t.body[0]), ast.Expr)
141
        self.assertEqual(type(t.body[0].value), ast.JoinedStr)
142
        self.assertEqual(t.body[0].lineno, 2)
143
        # check `f'...'`
144
        self.assertEqual(type(t.body[1]), ast.Expr)
145
        self.assertEqual(type(t.body[1].value), ast.JoinedStr)
146
        self.assertEqual(len(t.body[1].value.values), 4)
147
        self.assertEqual(type(t.body[1].value.values[0]), ast.Constant)
148
        self.assertEqual(type(t.body[1].value.values[0].value), str)
149
        self.assertEqual(type(t.body[1].value.values[1]), ast.FormattedValue)
150
        self.assertEqual(type(t.body[1].value.values[2]), ast.Constant)
151
        self.assertEqual(type(t.body[1].value.values[2].value), str)
152
        self.assertEqual(type(t.body[1].value.values[3]), ast.FormattedValue)
153
        self.assertEqual(t.body[1].lineno, 3)
154
        self.assertEqual(t.body[1].value.lineno, 3)
155
        self.assertEqual(t.body[1].value.values[0].lineno, 3)
156
        self.assertEqual(t.body[1].value.values[1].lineno, 3)
157
        self.assertEqual(t.body[1].value.values[2].lineno, 3)
158
        self.assertEqual(t.body[1].value.values[3].lineno, 3)
159
        # check the first binop location
160
        binop1 = t.body[1].value.values[1].value
161
        self.assertEqual(type(binop1), ast.BinOp)
162
        self.assertEqual(type(binop1.left), ast.Name)
163
        self.assertEqual(type(binop1.op), ast.Mult)
164
        self.assertEqual(type(binop1.right), ast.Call)
165
        self.assertEqual(binop1.lineno, 3)
166
        self.assertEqual(binop1.left.lineno, 3)
167
        self.assertEqual(binop1.right.lineno, 3)
168
        self.assertEqual(binop1.col_offset, 8)
169
        self.assertEqual(binop1.left.col_offset, 8)
170
        self.assertEqual(binop1.right.col_offset, 12)
171
        # check the second binop location
172
        binop2 = t.body[1].value.values[3].value
173
        self.assertEqual(type(binop2), ast.BinOp)
174
        self.assertEqual(type(binop2.left), ast.Name)
175
        self.assertEqual(type(binop2.op), ast.Add)
176
        self.assertEqual(type(binop2.right), ast.Call)
177
        self.assertEqual(binop2.lineno, 3)
178
        self.assertEqual(binop2.left.lineno, 3)
179
        self.assertEqual(binop2.right.lineno, 3)
180
        self.assertEqual(binop2.col_offset, 23)
181
        self.assertEqual(binop2.left.col_offset, 23)
182
        self.assertEqual(binop2.right.col_offset, 27)
183

184
    def __test_ast_line_numbers_nested(self):
185
        expr = """
186
a = 10
187
f'{a * f"-{x()}-"}'"""
188
        t = ast.parse(expr)
189
        self.assertEqual(type(t), ast.Module)
190
        self.assertEqual(len(t.body), 2)
191
        # check `a = 10`
192
        self.assertEqual(type(t.body[0]), ast.Assign)
193
        self.assertEqual(t.body[0].lineno, 2)
194
        # check `f'...'`
195
        self.assertEqual(type(t.body[1]), ast.Expr)
196
        self.assertEqual(type(t.body[1].value), ast.JoinedStr)
197
        self.assertEqual(len(t.body[1].value.values), 1)
198
        self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)
199
        self.assertEqual(t.body[1].lineno, 3)
200
        self.assertEqual(t.body[1].value.lineno, 3)
201
        self.assertEqual(t.body[1].value.values[0].lineno, 3)
202
        # check the binop location
203
        binop = t.body[1].value.values[0].value
204
        self.assertEqual(type(binop), ast.BinOp)
205
        self.assertEqual(type(binop.left), ast.Name)
206
        self.assertEqual(type(binop.op), ast.Mult)
207
        self.assertEqual(type(binop.right), ast.JoinedStr)
208
        self.assertEqual(binop.lineno, 3)
209
        self.assertEqual(binop.left.lineno, 3)
210
        self.assertEqual(binop.right.lineno, 3)
211
        self.assertEqual(binop.col_offset, 3)
212
        self.assertEqual(binop.left.col_offset, 3)
213
        self.assertEqual(binop.right.col_offset, 7)
214
        # check the nested call location
215
        self.assertEqual(len(binop.right.values), 3)
216
        self.assertEqual(type(binop.right.values[0]), ast.Constant)
217
        self.assertEqual(type(binop.right.values[0].value), str)
218
        self.assertEqual(type(binop.right.values[1]), ast.FormattedValue)
219
        self.assertEqual(type(binop.right.values[2]), ast.Constant)
220
        self.assertEqual(type(binop.right.values[2].value), str)
221
        self.assertEqual(binop.right.values[0].lineno, 3)
222
        self.assertEqual(binop.right.values[1].lineno, 3)
223
        self.assertEqual(binop.right.values[2].lineno, 3)
224
        call = binop.right.values[1].value
225
        self.assertEqual(type(call), ast.Call)
226
        self.assertEqual(call.lineno, 3)
227
        self.assertEqual(call.col_offset, 11)
228

229
    def __test_ast_line_numbers_duplicate_expression(self):
230
        """Duplicate expression
231

232
        NOTE: this is currently broken, always sets location of the first
233
        expression.
234
        """
235
        expr = """
236
a = 10
237
f'{a * x()} {a * x()} {a * x()}'
238
"""
239
        t = ast.parse(expr)
240
        self.assertEqual(type(t), ast.Module)
241
        self.assertEqual(len(t.body), 2)
242
        # check `a = 10`
243
        self.assertEqual(type(t.body[0]), ast.Assign)
244
        self.assertEqual(t.body[0].lineno, 2)
245
        # check `f'...'`
246
        self.assertEqual(type(t.body[1]), ast.Expr)
247
        self.assertEqual(type(t.body[1].value), ast.JoinedStr)
248
        self.assertEqual(len(t.body[1].value.values), 5)
249
        self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)
250
        self.assertEqual(type(t.body[1].value.values[1]), ast.Constant)
251
        self.assertEqual(type(t.body[1].value.values[1].value), str)
252
        self.assertEqual(type(t.body[1].value.values[2]), ast.FormattedValue)
253
        self.assertEqual(type(t.body[1].value.values[3]), ast.Constant)
254
        self.assertEqual(type(t.body[1].value.values[3].value), str)
255
        self.assertEqual(type(t.body[1].value.values[4]), ast.FormattedValue)
256
        self.assertEqual(t.body[1].lineno, 3)
257
        self.assertEqual(t.body[1].value.lineno, 3)
258
        self.assertEqual(t.body[1].value.values[0].lineno, 3)
259
        self.assertEqual(t.body[1].value.values[1].lineno, 3)
260
        self.assertEqual(t.body[1].value.values[2].lineno, 3)
261
        self.assertEqual(t.body[1].value.values[3].lineno, 3)
262
        self.assertEqual(t.body[1].value.values[4].lineno, 3)
263
        # check the first binop location
264
        binop = t.body[1].value.values[0].value
265
        self.assertEqual(type(binop), ast.BinOp)
266
        self.assertEqual(type(binop.left), ast.Name)
267
        self.assertEqual(type(binop.op), ast.Mult)
268
        self.assertEqual(type(binop.right), ast.Call)
269
        self.assertEqual(binop.lineno, 3)
270
        self.assertEqual(binop.left.lineno, 3)
271
        self.assertEqual(binop.right.lineno, 3)
272
        self.assertEqual(binop.col_offset, 3)
273
        self.assertEqual(binop.left.col_offset, 3)
274
        self.assertEqual(binop.right.col_offset, 7)
275
        # check the second binop location
276
        binop = t.body[1].value.values[2].value
277
        self.assertEqual(type(binop), ast.BinOp)
278
        self.assertEqual(type(binop.left), ast.Name)
279
        self.assertEqual(type(binop.op), ast.Mult)
280
        self.assertEqual(type(binop.right), ast.Call)
281
        self.assertEqual(binop.lineno, 3)
282
        self.assertEqual(binop.left.lineno, 3)
283
        self.assertEqual(binop.right.lineno, 3)
284
        self.assertEqual(binop.col_offset, 3)  # FIXME: this is wrong
285
        self.assertEqual(binop.left.col_offset, 3)  # FIXME: this is wrong
286
        self.assertEqual(binop.right.col_offset, 7)  # FIXME: this is wrong
287
        # check the third binop location
288
        binop = t.body[1].value.values[4].value
289
        self.assertEqual(type(binop), ast.BinOp)
290
        self.assertEqual(type(binop.left), ast.Name)
291
        self.assertEqual(type(binop.op), ast.Mult)
292
        self.assertEqual(type(binop.right), ast.Call)
293
        self.assertEqual(binop.lineno, 3)
294
        self.assertEqual(binop.left.lineno, 3)
295
        self.assertEqual(binop.right.lineno, 3)
296
        self.assertEqual(binop.col_offset, 3)  # FIXME: this is wrong
297
        self.assertEqual(binop.left.col_offset, 3)  # FIXME: this is wrong
298
        self.assertEqual(binop.right.col_offset, 7)  # FIXME: this is wrong
299

300
    def __test_ast_line_numbers_multiline_fstring(self):
301
        # See bpo-30465 for details.
302
        expr = """
303
a = 10
304
f'''
305
  {a
306
     *
307
       x()}
308
non-important content
309
'''
310
"""
311
        t = ast.parse(expr)
312
        self.assertEqual(type(t), ast.Module)
313
        self.assertEqual(len(t.body), 2)
314
        # check `a = 10`
315
        self.assertEqual(type(t.body[0]), ast.Assign)
316
        self.assertEqual(t.body[0].lineno, 2)
317
        # check `f'...'`
318
        self.assertEqual(type(t.body[1]), ast.Expr)
319
        self.assertEqual(type(t.body[1].value), ast.JoinedStr)
320
        self.assertEqual(len(t.body[1].value.values), 3)
321
        self.assertEqual(type(t.body[1].value.values[0]), ast.Constant)
322
        self.assertEqual(type(t.body[1].value.values[0].value), str)
323
        self.assertEqual(type(t.body[1].value.values[1]), ast.FormattedValue)
324
        self.assertEqual(type(t.body[1].value.values[2]), ast.Constant)
325
        self.assertEqual(type(t.body[1].value.values[2].value), str)
326
        self.assertEqual(t.body[1].lineno, 3)
327
        self.assertEqual(t.body[1].value.lineno, 3)
328
        self.assertEqual(t.body[1].value.values[0].lineno, 3)
329
        self.assertEqual(t.body[1].value.values[1].lineno, 3)
330
        self.assertEqual(t.body[1].value.values[2].lineno, 3)
331
        self.assertEqual(t.body[1].col_offset, 0)
332
        self.assertEqual(t.body[1].value.col_offset, 0)
333
        self.assertEqual(t.body[1].value.values[0].col_offset, 0)
334
        self.assertEqual(t.body[1].value.values[1].col_offset, 0)
335
        self.assertEqual(t.body[1].value.values[2].col_offset, 0)
336
        # NOTE: the following lineno information and col_offset is correct for
337
        # expressions within FormattedValues.
338
        binop = t.body[1].value.values[1].value
339
        self.assertEqual(type(binop), ast.BinOp)
340
        self.assertEqual(type(binop.left), ast.Name)
341
        self.assertEqual(type(binop.op), ast.Mult)
342
        self.assertEqual(type(binop.right), ast.Call)
343
        self.assertEqual(binop.lineno, 4)
344
        self.assertEqual(binop.left.lineno, 4)
345
        self.assertEqual(binop.right.lineno, 6)
346
        self.assertEqual(binop.col_offset, 4)
347
        self.assertEqual(binop.left.col_offset, 4)
348
        self.assertEqual(binop.right.col_offset, 7)
349

350
    def test_docstring(self):
351
        def f():
352
            f'''Not a docstring'''
353
        self.assertIsNone(f.__doc__)
354
        def g():
355
            '''Not a docstring''' \
356
            f''
357
        self.assertIsNone(g.__doc__)
358

359
    def __test_literal_eval(self):
360
        with self.assertRaisesRegex(ValueError, 'malformed node or string'):
361
            ast.literal_eval("f'x'")
362

363
    def __test_ast_compile_time_concat(self):
364
        x = ['']
365

366
        expr = """x[0] = 'foo' f'{3}'"""
367
        t = ast.parse(expr)
368
        c = compile(t, '', 'exec')
369
        exec(c)
370
        self.assertEqual(x[0], 'foo3')
371

372
    def test_compile_time_concat_errors(self):
373
        self.assertAllRaise(SyntaxError,
374
                            'cannot mix bytes and nonbytes literals',
375
                            [r"""f'' b''""",
376
                             r"""b'' f''""",
377
                             ])
378

379
    def test_literal(self):
380
        self.assertEqual(f'', '')
381
        self.assertEqual(f'a', 'a')
382
        self.assertEqual(f' ', ' ')
383

384
    def test_unterminated_string(self):
385
        self.assertAllRaise(SyntaxError, 'f-string: unterminated string',
386
                            [r"""f'{"x'""",
387
                             r"""f'{"x}'""",
388
                             r"""f'{("x'""",
389
                             r"""f'{("x}'""",
390
                             ])
391

392
    def test_mismatched_parens(self):
393
        self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\}' "
394
                            r"does not match opening parenthesis '\('",
395
                            ["f'{((}'",
396
                             ])
397
        self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\)' "
398
                            r"does not match opening parenthesis '\['",
399
                            ["f'{a[4)}'",
400
                            ])
401
        self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\]' "
402
                            r"does not match opening parenthesis '\('",
403
                            ["f'{a(4]}'",
404
                            ])
405
        self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\}' "
406
                            r"does not match opening parenthesis '\['",
407
                            ["f'{a[4}'",
408
                            ])
409
        self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\}' "
410
                            r"does not match opening parenthesis '\('",
411
                            ["f'{a(4}'",
412
                            ])
413
        self.assertRaises(SyntaxError, eval, "f'{" + "("*500 + "}'")
414

415
    def test_double_braces(self):
416
        self.assertEqual(f'{{', '{')
417
        self.assertEqual(f'a{{', 'a{')
418
        self.assertEqual(f'{{b', '{b')
419
        self.assertEqual(f'a{{b', 'a{b')
420
        self.assertEqual(f'}}', '}')
421
        self.assertEqual(f'a}}', 'a}')
422
        self.assertEqual(f'}}b', '}b')
423
        self.assertEqual(f'a}}b', 'a}b')
424
        self.assertEqual(f'{{}}', '{}')
425
        self.assertEqual(f'a{{}}', 'a{}')
426
        self.assertEqual(f'{{b}}', '{b}')
427
        self.assertEqual(f'{{}}c', '{}c')
428
        self.assertEqual(f'a{{b}}', 'a{b}')
429
        self.assertEqual(f'a{{}}c', 'a{}c')
430
        self.assertEqual(f'{{b}}c', '{b}c')
431
        self.assertEqual(f'a{{b}}c', 'a{b}c')
432

433
        self.assertEqual(f'{{{10}', '{10')
434
        self.assertEqual(f'}}{10}', '}10')
435
        self.assertEqual(f'}}{{{10}', '}{10')
436
        self.assertEqual(f'}}a{{{10}', '}a{10')
437

438
        self.assertEqual(f'{10}{{', '10{')
439
        self.assertEqual(f'{10}}}', '10}')
440
        self.assertEqual(f'{10}}}{{', '10}{')
441
        self.assertEqual(f'{10}}}a{{' '}', '10}a{}')
442

443
        # Inside of strings, don't interpret doubled brackets.
444
        self.assertEqual(f'{"{{}}"}', '{{}}')
445

446
        self.assertAllRaise(TypeError, 'unhashable type',
447
                            ["f'{ {{}} }'", # dict in a set
448
                             ])
449

450
    def test_compile_time_concat(self):
451
        x = 'def'
452
        self.assertEqual('abc' f'## {x}ghi', 'abc## defghi')
453
        self.assertEqual('abc' f'{x}' 'ghi', 'abcdefghi')
454
        self.assertEqual('abc' f'{x}' 'gh' f'i{x:4}', 'abcdefghidef ')
455
        self.assertEqual('{x}' f'{x}', '{x}def')
456
        self.assertEqual('{x' f'{x}', '{xdef')
457
        self.assertEqual('{x}' f'{x}', '{x}def')
458
        self.assertEqual('{{x}}' f'{x}', '{{x}}def')
459
        self.assertEqual('{{x' f'{x}', '{{xdef')
460
        self.assertEqual('x}}' f'{x}', 'x}}def')
461
        self.assertEqual(f'{x}' 'x}}', 'defx}}')
462
        self.assertEqual(f'{x}' '', 'def')
463
        self.assertEqual('' f'{x}' '', 'def')
464
        self.assertEqual('' f'{x}', 'def')
465
        self.assertEqual(f'{x}' '2', 'def2')
466
        self.assertEqual('1' f'{x}' '2', '1def2')
467
        self.assertEqual('1' f'{x}', '1def')
468
        self.assertEqual(f'{x}' f'-{x}', 'def-def')
469
        self.assertEqual('' f'', '')
470
        self.assertEqual('' f'' '', '')
471
        self.assertEqual('' f'' '' f'', '')
472
        self.assertEqual(f'', '')
473
        self.assertEqual(f'' '', '')
474
        self.assertEqual(f'' '' f'', '')
475
        self.assertEqual(f'' '' f'' '', '')
476

477
        self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
478
                            ["f'{3' f'}'",  # can't concat to get a valid f-string
479
                             ])
480

481
    def test_comments(self):
482
        # These aren't comments, since they're in strings.
483
        d = {'#': 'hash'}
484
        self.assertEqual(f'{"#"}', '#')
485
        self.assertEqual(f'{d["#"]}', 'hash')
486

487
        self.assertAllRaise(SyntaxError, "f-string expression part cannot include '#'",
488
                            ["f'{1#}'",   # error because the expression becomes "(1#)"
489
                             "f'{3(#)}'",
490
                             "f'{#}'",
491
                             ])
492
        self.assertAllRaise(SyntaxError, r"f-string: unmatched '\)'",
493
                            ["f'{)#}'",   # When wrapped in parens, this becomes
494
                                          #  '()#)'.  Make sure that doesn't compile.
495
                             ])
496

497
    def test_many_expressions(self):
498
        # Create a string with many expressions in it. Note that
499
        #  because we have a space in here as a literal, we're actually
500
        #  going to use twice as many ast nodes: one for each literal
501
        #  plus one for each expression.
502
        def build_fstr(n, extra=''):
503
            return "f'" + ('{x} ' * n) + extra + "'"
504

505
        x = 'X'
506
        width = 1
507

508
        # Test around 256.
509
        # for i in range(250, 260):
510
        #     self.assertEqual(eval(build_fstr(i)), (x+' ')*i)
511
        self.assertEqual(
512
            cy_eval('[' + ', '.join(build_fstr(i) for i in range(250, 260)) + ']', x=x, width=width),
513
            [(x+' ')*i for i in range(250, 260)],
514
        )
515

516
        # Test concatenating 2 largs fstrings.
517
        # self.assertEqual(eval(build_fstr(255)*256), (x+' ')*(255*256))
518
        self.assertEqual(cy_eval(build_fstr(255)*3, x=x, width=width), (x+' ')*(255*3))  # CPython uses 255*256
519

520
        s = build_fstr(253, '{x:{width}} ')
521
        # self.assertEqual(eval(s), (x+' ')*254)
522
        self.assertEqual(cy_eval(s, x=x, width=width), (x+' ')*254)
523

524
        # Test lots of expressions and constants, concatenated.
525
        s = "f'{1}' 'x' 'y'" * 1024
526
        # self.assertEqual(eval(s), '1xy' * 1024)
527
        self.assertEqual(cy_eval(s, x=x, width=width), '1xy' * 1024)
528

529
    def test_format_specifier_expressions(self):
530
        width = 10
531
        precision = 4
532
        value = decimal.Decimal('12.34567')
533
        self.assertEqual(f'result: {value:{width}.{precision}}', 'result:      12.35')
534
        self.assertEqual(f'result: {value:{width!r}.{precision}}', 'result:      12.35')
535
        self.assertEqual(f'result: {value:{width:0}.{precision:1}}', 'result:      12.35')
536
        self.assertEqual(f'result: {value:{1}{0:0}.{precision:1}}', 'result:      12.35')
537
        self.assertEqual(f'result: {value:{ 1}{ 0:0}.{ precision:1}}', 'result:      12.35')
538
        self.assertEqual(f'{10:#{1}0x}', '       0xa')
539
        self.assertEqual(f'{10:{"#"}1{0}{"x"}}', '       0xa')
540
        self.assertEqual(f'{-10:-{"#"}1{0}x}', '      -0xa')
541
        self.assertEqual(f'{-10:{"-"}#{1}0{"x"}}', '      -0xa')
542
        self.assertEqual(f'{10:#{3 != {4:5} and width}x}', '       0xa')
543

544
        self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
545
                            ["""f'{"s"!r{":10"}}'""",
546

547
                             # This looks like a nested format spec.
548
                             ])
549

550
        self.assertAllRaise(SyntaxError, "f-string: invalid syntax",
551
                            [# Invalid syntax inside a nested spec.
552
                             "f'{4:{/5}}'",
553
                             ])
554

555
        # CYTHON: The nesting restriction seems rather arbitrary. Ignoring it for now and instead test that it works.
556
        self.assertEqual(f'result: {value:{width:{0}}.{precision:1}}', 'result:      12.35')
557
        #self.assertAllRaise(SyntaxError, "f-string: expressions nested too deeply",
558
        #                    [# Can't nest format specifiers.
559
        #                     "f'result: {value:{width:{0}}.{precision:1}}'",
560
        #                     ])
561

562
        self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character',
563
                            [# No expansion inside conversion or for
564
                             #  the : or ! itself.
565
                             """f'{"s"!{"r"}}'""",
566
                             ])
567

568
    def test_side_effect_order(self):
569
        class X:
570
            def __init__(self):
571
                self.i = 0
572
            def __format__(self, spec):
573
                self.i += 1
574
                return str(self.i)
575

576
        x = X()
577
        self.assertEqual(f'{x} {x}', '1 2')
578

579
    def test_missing_expression(self):
580
        self.assertAllRaise(SyntaxError, 'f-string: empty expression not allowed',
581
                            ["f'{}'",
582
                             "f'{ }'"
583
                             "f' {} '",
584
                             "f'{!r}'",
585
                             "f'{ !r}'",
586
                             "f'{10:{ }}'",
587
                             "f' { } '",
588

589
                             # The Python parser ignores also the following
590
                             # whitespace characters in additional to a space.
591
                             "f'''{\t\f\r\n}'''",
592

593
                             # Catch the empty expression before the
594
                             #  invalid conversion.
595
                             "f'{!x}'",
596
                             "f'{ !xr}'",
597
                             "f'{!x:}'",
598
                             "f'{!x:a}'",
599
                             "f'{ !xr:}'",
600
                             "f'{ !xr:a}'",
601

602
                             "f'{!}'",
603
                             "f'{:}'",
604

605
                             # We find the empty expression before the
606
                             #  missing closing brace.
607
                             "f'{!'",
608
                             "f'{!s:'",
609
                             "f'{:'",
610
                             "f'{:x'",
611
                             ])
612

613
        # Different error message is raised for other whitespace characters.
614
        self.assertAllRaise(SyntaxError, r"invalid non-printable character U\+00A0",
615
                            ["f'''{\xa0}'''",
616
                             #"\xa0",
617
                             ])
618

619
    def test_parens_in_expressions(self):
620
        self.assertEqual(f'{3,}', '(3,)')
621

622
        # Add these because when an expression is evaluated, parens
623
        #  are added around it. But we shouldn't go from an invalid
624
        #  expression to a valid one. The added parens are just
625
        #  supposed to allow whitespace (including newlines).
626
        self.assertAllRaise(SyntaxError, 'f-string: invalid syntax',
627
                            ["f'{,}'",
628
                             "f'{,}'",  # this is (,), which is an error
629
                             ])
630

631
        self.assertAllRaise(SyntaxError, r"f-string: unmatched '\)'",
632
                            ["f'{3)+(4}'",
633
                             ])
634

635
        self.assertAllRaise(SyntaxError, 'EOL while scanning string literal',
636
                            ["f'{\n}'",
637
                             ])
638

639
    def test_backslashes_in_string_part(self):
640
        self.assertEqual(f'\t', '\t')
641
        self.assertEqual(r'\t', '\\t')
642
        self.assertEqual(rf'\t', '\\t')
643
        self.assertEqual(f'{2}\t', '2\t')
644
        self.assertEqual(f'{2}\t{3}', '2\t3')
645
        self.assertEqual(f'\t{3}', '\t3')
646

647
        self.assertEqual(f'\u0394', '\u0394')
648
        self.assertEqual(r'\u0394', '\\u0394')
649
        self.assertEqual(rf'\u0394', '\\u0394')
650
        self.assertEqual(f'{2}\u0394', '2\u0394')
651
        self.assertEqual(f'{2}\u0394{3}', '2\u03943')
652
        self.assertEqual(f'\u0394{3}', '\u03943')
653

654
        self.assertEqual(f'\U00000394', '\u0394')
655
        self.assertEqual(r'\U00000394', '\\U00000394')
656
        self.assertEqual(rf'\U00000394', '\\U00000394')
657
        self.assertEqual(f'{2}\U00000394', '2\u0394')
658
        self.assertEqual(f'{2}\U00000394{3}', '2\u03943')
659
        self.assertEqual(f'\U00000394{3}', '\u03943')
660

661
        self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}', '\u0394')
662
        self.assertEqual(f'{2}\N{GREEK CAPITAL LETTER DELTA}', '2\u0394')
663
        self.assertEqual(f'{2}\N{GREEK CAPITAL LETTER DELTA}{3}', '2\u03943')
664
        self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}{3}', '\u03943')
665
        self.assertEqual(f'2\N{GREEK CAPITAL LETTER DELTA}', '2\u0394')
666
        self.assertEqual(f'2\N{GREEK CAPITAL LETTER DELTA}3', '2\u03943')
667
        self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}3', '\u03943')
668

669
        self.assertEqual(f'\x20', ' ')
670
        self.assertEqual(r'\x20', '\\x20')
671
        self.assertEqual(rf'\x20', '\\x20')
672
        self.assertEqual(f'{2}\x20', '2 ')
673
        self.assertEqual(f'{2}\x20{3}', '2 3')
674
        self.assertEqual(f'\x20{3}', ' 3')
675

676
        self.assertEqual(f'2\x20', '2 ')
677
        self.assertEqual(f'2\x203', '2 3')
678
        self.assertEqual(f'\x203', ' 3')
679

680
        #with self.assertWarns(DeprecationWarning):  # invalid escape sequence
681
        #    value = cy_eval(r"f'\{6*7}'")
682
        #self.assertEqual(value, '\\42')
683
        self.assertEqual(f'\\{6*7}', '\\42')
684
        self.assertEqual(fr'\{6*7}', '\\42')
685

686
        AMPERSAND = 'spam'
687
        # Get the right unicode character (&), or pick up local variable
688
        # depending on the number of backslashes.
689
        self.assertEqual(f'\N{AMPERSAND}', '&')
690
        self.assertEqual(f'\\N{AMPERSAND}', '\\Nspam')
691
        self.assertEqual(fr'\N{AMPERSAND}', '\\Nspam')
692
        self.assertEqual(f'\\\N{AMPERSAND}', '\\&')
693

694
    def test_misformed_unicode_character_name(self):
695
        # These test are needed because unicode names are parsed
696
        # differently inside f-strings.
697
        self.assertAllRaise(SyntaxError, r"\(unicode error\) 'unicodeescape' codec can't decode bytes in position .*: malformed \\N character escape",
698
                            [r"f'\N'",
699
                             r"f'\N{'",
700
                             r"f'\N{GREEK CAPITAL LETTER DELTA'",
701

702
                             # Here are the non-f-string versions,
703
                             #  which should give the same errors.
704
                             r"'\N'",
705
                             r"'\N{'",
706
                             r"'\N{GREEK CAPITAL LETTER DELTA'",
707
                             ])
708

709
    def test_no_backslashes_in_expression_part(self):
710
        self.assertAllRaise(SyntaxError, 'f-string expression part cannot include a backslash',
711
                            [r"f'{\'a\'}'",
712
                             r"f'{\t3}'",
713
                             r"f'{\}'",
714
                             r"rf'{\'a\'}'",
715
                             r"rf'{\t3}'",
716
                             r"rf'{\}'",
717
                             r"""rf'{"\N{LEFT CURLY BRACKET}"}'""",
718
                             r"f'{\n}'",
719
                             ])
720

721
    def test_no_escapes_for_braces(self):
722
        """
723
        Only literal curly braces begin an expression.
724
        """
725
        # \x7b is '{'.
726
        self.assertEqual(f'\x7b1+1}}', '{1+1}')
727
        self.assertEqual(f'\x7b1+1', '{1+1')
728
        self.assertEqual(f'\u007b1+1', '{1+1')
729
        self.assertEqual(f'\N{LEFT CURLY BRACKET}1+1\N{RIGHT CURLY BRACKET}', '{1+1}')
730

731
    def test_newlines_in_expressions(self):
732
        self.assertEqual(f'{0}', '0')
733
        self.assertEqual(rf'''{3+
734
4}''', '7')
735

736
    def test_lambda(self):
737
        x = 5
738
        self.assertEqual(f'{(lambda y:x*y)("8")!r}', "'88888'")
739
        self.assertEqual(f'{(lambda y:x*y)("8")!r:10}', "'88888'   ")
740
        self.assertEqual(f'{(lambda y:x*y)("8"):10}', "88888     ")
741

742
        # lambda doesn't work without parens, because the colon
743
        #  makes the parser think it's a format_spec
744
        self.assertAllRaise(SyntaxError, 'f-string: invalid syntax',
745
                            ["f'{lambda x:x}'",
746
                             ])
747

748
    def test_yield(self):
749
        # Not terribly useful, but make sure the yield turns
750
        #  a function into a generator
751
        def fn(y):
752
            f'y:{yield y*2}'
753
            f'{yield}'
754

755
        g = fn(4)
756
        self.assertEqual(next(g), 8)
757
        self.assertEqual(next(g), None)
758

759
    def test_yield_send(self):
760
        def fn(x):
761
            yield f'x:{yield (lambda i: x * i)}'
762

763
        g = fn(10)
764
        the_lambda = next(g)
765
        self.assertEqual(the_lambda(4), 40)
766
        self.assertEqual(g.send('string'), 'x:string')
767

768
    def test_expressions_with_triple_quoted_strings(self):
769
        self.assertEqual(f"{'''x'''}", 'x')
770
        self.assertEqual(f"{'''eric's'''}", "eric's")
771

772
        # Test concatenation within an expression
773
        self.assertEqual(f'{"x" """eric"s""" "y"}', 'xeric"sy')
774
        self.assertEqual(f'{"x" """eric"s"""}', 'xeric"s')
775
        self.assertEqual(f'{"""eric"s""" "y"}', 'eric"sy')
776
        self.assertEqual(f'{"""x""" """eric"s""" "y"}', 'xeric"sy')
777
        self.assertEqual(f'{"""x""" """eric"s""" """y"""}', 'xeric"sy')
778
        self.assertEqual(f'{r"""x""" """eric"s""" """y"""}', 'xeric"sy')
779

780
    def test_multiple_vars(self):
781
        x = 98
782
        y = 'abc'
783
        self.assertEqual(f'{x}{y}', '98abc')
784

785
        self.assertEqual(f'X{x}{y}', 'X98abc')
786
        self.assertEqual(f'{x}X{y}', '98Xabc')
787
        self.assertEqual(f'{x}{y}X', '98abcX')
788

789
        self.assertEqual(f'X{x}Y{y}', 'X98Yabc')
790
        self.assertEqual(f'X{x}{y}Y', 'X98abcY')
791
        self.assertEqual(f'{x}X{y}Y', '98XabcY')
792

793
        self.assertEqual(f'X{x}Y{y}Z', 'X98YabcZ')
794

795
    def test_closure(self):
796
        def outer(x):
797
            def inner():
798
                return f'x:{x}'
799
            return inner
800

801
        self.assertEqual(outer('987')(), 'x:987')
802
        self.assertEqual(outer(7)(), 'x:7')
803

804
    def test_arguments(self):
805
        y = 2
806
        def f(x, width):
807
            return f'x={x*y:{width}}'
808

809
        self.assertEqual(f('foo', 10), 'x=foofoo    ')
810
        x = 'bar'
811
        self.assertEqual(f(10, 10), 'x=        20')
812

813
    def test_locals(self):
814
        value = 123
815
        self.assertEqual(f'v:{value}', 'v:123')
816

817
    def test_missing_variable(self):
818
        with self.assertRaises(NameError):
819
            f'v:{value}'
820

821
    def test_missing_format_spec(self):
822
        class O:
823
            def __format__(self, spec):
824
                if not spec:
825
                    return '*'
826
                return spec
827

828
        self.assertEqual(f'{O():x}', 'x')
829
        self.assertEqual(f'{O()}', '*')
830
        self.assertEqual(f'{O():}', '*')
831

832
        self.assertEqual(f'{3:}', '3')
833
        self.assertEqual(f'{3!s:}', '3')
834

835
    def test_global(self):
836
        self.assertEqual(f'g:{a_global}', 'g:global variable')
837
        self.assertEqual(f'g:{a_global!r}', "g:'global variable'")
838

839
        a_local = 'local variable'
840
        self.assertEqual(f'g:{a_global} l:{a_local}',
841
                         'g:global variable l:local variable')
842
        self.assertEqual(f'g:{a_global!r}',
843
                         "g:'global variable'")
844
        self.assertEqual(f'g:{a_global} l:{a_local!r}',
845
                         "g:global variable l:'local variable'")
846

847
        self.assertIn("module 'unittest' from", f'{unittest}')
848

849
    def test_shadowed_global(self):
850
        a_global = 'really a local'
851
        self.assertEqual(f'g:{a_global}', 'g:really a local')
852
        self.assertEqual(f'g:{a_global!r}', "g:'really a local'")
853

854
        a_local = 'local variable'
855
        self.assertEqual(f'g:{a_global} l:{a_local}',
856
                         'g:really a local l:local variable')
857
        self.assertEqual(f'g:{a_global!r}',
858
                         "g:'really a local'")
859
        self.assertEqual(f'g:{a_global} l:{a_local!r}',
860
                         "g:really a local l:'local variable'")
861

862
    def test_call(self):
863
        def foo(x):
864
            return 'x=' + str(x)
865

866
        self.assertEqual(f'{foo(10)}', 'x=10')
867

868
    def test_nested_fstrings(self):
869
        y = 5
870
        self.assertEqual(f'{f"{0}"*3}', '000')
871
        self.assertEqual(f'{f"{y}"*3}', '555')
872

873
    def test_invalid_string_prefixes(self):
874
        single_quote_cases = ["fu''",
875
                             "uf''",
876
                             "Fu''",
877
                             "fU''",
878
                             "Uf''",
879
                             "uF''",
880
                             "ufr''",
881
                             "urf''",
882
                             "fur''",
883
                             "fru''",
884
                             "rfu''",
885
                             "ruf''",
886
                             "FUR''",
887
                             "Fur''",
888
                             "fb''",
889
                             "fB''",
890
                             "Fb''",
891
                             "FB''",
892
                             "bf''",
893
                             "bF''",
894
                             "Bf''",
895
                             "BF''",]
896
        double_quote_cases = [case.replace("'", '"') for case in single_quote_cases]
897
        self.assertAllRaise(SyntaxError, 'unexpected EOF while parsing',
898
                            single_quote_cases + double_quote_cases)
899

900
    def test_leading_trailing_spaces(self):
901
        self.assertEqual(f'{ 3}', '3')
902
        self.assertEqual(f'{  3}', '3')
903
        self.assertEqual(f'{3 }', '3')
904
        self.assertEqual(f'{3  }', '3')
905

906
        self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]}}',
907
                         'expr={1: 2}')
908
        self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]} }',
909
                         'expr={1: 2}')
910

911
    def test_not_equal(self):
912
        # There's a special test for this because there's a special
913
        #  case in the f-string parser to look for != as not ending an
914
        #  expression. Normally it would, while looking for !s or !r.
915

916
        self.assertEqual(f'{3!=4}', 'True')
917
        self.assertEqual(f'{3!=4:}', 'True')
918
        self.assertEqual(f'{3!=4!s}', 'True')
919
        self.assertEqual(f'{3!=4!s:.3}', 'Tru')
920

921
    def test_equal_equal(self):
922
        # Because an expression ending in = has special meaning,
923
        # there's a special test for ==. Make sure it works.
924

925
        self.assertEqual(f'{0==1}', 'False')
926

927
    def test_conversions(self):
928
        self.assertEqual(f'{3.14:10.10}', '      3.14')
929
        self.assertEqual(f'{3.14!s:10.10}', '3.14      ')
930
        self.assertEqual(f'{3.14!r:10.10}', '3.14      ')
931
        self.assertEqual(f'{3.14!a:10.10}', '3.14      ')
932

933
        self.assertEqual(f'{"a"}', 'a')
934
        self.assertEqual(f'{"a"!r}', "'a'")
935
        self.assertEqual(f'{"a"!a}', "'a'")
936

937
        # Not a conversion.
938
        self.assertEqual(f'{"a!r"}', "a!r")
939

940
        # Not a conversion, but show that ! is allowed in a format spec.
941
        self.assertEqual(f'{3.14:!<10.10}', '3.14!!!!!!')
942

943
        self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character',
944
                            ["f'{3!g}'",
945
                             "f'{3!A}'",
946
                             "f'{3!3}'",
947
                             "f'{3!G}'",
948
                             "f'{3!!}'",
949
                             "f'{3!:}'",
950
                             "f'{3! s}'",  # no space before conversion char
951
                             ])
952

953
        self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
954
                            ["f'{x!s{y}}'",
955
                             "f'{3!ss}'",
956
                             "f'{3!ss:}'",
957
                             "f'{3!ss:s}'",
958
                             ])
959

960
    def test_assignment(self):
961
        self.assertAllRaise(SyntaxError, 'invalid syntax',
962
                            ["f'' = 3",
963
                             "f'{0}' = x",
964
                             "f'{x}' = x",
965
                             ])
966

967
    def test_del(self):
968
        self.assertAllRaise(CompileError, 'invalid syntax',   # CPython raises SyntaxError
969
                            ["del f''",
970
                             "del '' f''",
971
                             ])
972

973
    def test_mismatched_braces(self):
974
        self.assertAllRaise(SyntaxError, "f-string: single '}' is not allowed",
975
                            ["f'{{}'",
976
                             "f'{{}}}'",
977
                             "f'}'",
978
                             "f'x}'",
979
                             "f'x}x'",
980
                             r"f'\u007b}'",
981

982
                             # Can't have { or } in a format spec.
983
                             "f'{3:}>10}'",
984
                             "f'{3:}}>10}'",
985
                             ])
986

987
        self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
988
                            ["f'{3:{{>10}'",
989
                             "f'{3'",
990
                             "f'{3!'",
991
                             "f'{3:'",
992
                             "f'{3!s'",
993
                             "f'{3!s:'",
994
                             "f'{3!s:3'",
995
                             "f'x{'",
996
                             "f'x{x'",
997
                             "f'{x'",
998
                             "f'{3:s'",
999
                             "f'{{{'",
1000
                             "f'{{}}{'",
1001
                             "f'{'",
1002
                             ])
1003

1004
        # But these are just normal strings.
1005
        self.assertEqual(f'{"{"}', '{')
1006
        self.assertEqual(f'{"}"}', '}')
1007
        self.assertEqual(f'{3:{"}"}>10}', '}}}}}}}}}3')
1008
        self.assertEqual(f'{2:{"{"}>10}', '{{{{{{{{{2')
1009

1010
    def test_if_conditional(self):
1011
        # There's special logic in compile.c to test if the
1012
        #  conditional for an if (and while) are constants. Exercise
1013
        #  that code.
1014

1015
        def test_fstring(x, expected):
1016
            flag = 0
1017
            if f'{x}':
1018
                flag = 1
1019
            else:
1020
                flag = 2
1021
            self.assertEqual(flag, expected)
1022

1023
        def test_concat_empty(x, expected):
1024
            flag = 0
1025
            if '' f'{x}':
1026
                flag = 1
1027
            else:
1028
                flag = 2
1029
            self.assertEqual(flag, expected)
1030

1031
        def test_concat_non_empty(x, expected):
1032
            flag = 0
1033
            if ' ' f'{x}':
1034
                flag = 1
1035
            else:
1036
                flag = 2
1037
            self.assertEqual(flag, expected)
1038

1039
        test_fstring('', 2)
1040
        test_fstring(' ', 1)
1041

1042
        test_concat_empty('', 2)
1043
        test_concat_empty(' ', 1)
1044

1045
        test_concat_non_empty('', 1)
1046
        test_concat_non_empty(' ', 1)
1047

1048
    def test_empty_format_specifier(self):
1049
        x = 'test'
1050
        self.assertEqual(f'{x}', 'test')
1051
        self.assertEqual(f'{x:}', 'test')
1052
        self.assertEqual(f'{x!s:}', 'test')
1053
        self.assertEqual(f'{x!r:}', "'test'")
1054

1055
    def test_str_format_differences(self):
1056
        d = {'a': 'string',
1057
             0: 'integer',
1058
             }
1059
        a = 0
1060
        self.assertEqual(f'{d[0]}', 'integer')
1061
        self.assertEqual(f'{d["a"]}', 'string')
1062
        self.assertEqual(f'{d[a]}', 'integer')
1063
        self.assertEqual('{d[a]}'.format(d=d), 'string')
1064
        self.assertEqual('{d[0]}'.format(d=d), 'integer')
1065

1066
    def test_errors(self):
1067
        # see issue 26287
1068
        exc = TypeError
1069
        self.assertAllRaise(exc, 'unsupported',
1070
                            [r"f'{(lambda: 0):x}'",
1071
                             r"f'{(0,):x}'",
1072
                             ])
1073
        self.assertAllRaise(ValueError, 'Unknown format code',
1074
                            [r"f'{1000:j}'",
1075
                             r"f'{1000:j}'",
1076
                            ])
1077

1078
    def __test_filename_in_syntaxerror(self):
1079
        # see issue 38964
1080
        with temp_cwd() as cwd:
1081
            file_path = os.path.join(cwd, 't.py')
1082
            with open(file_path, 'w') as f:
1083
                f.write('f"{a b}"') # This generates a SyntaxError
1084
            _, _, stderr = assert_python_failure(file_path,
1085
                                                 PYTHONIOENCODING='ascii')
1086
        self.assertIn(file_path.encode('ascii', 'backslashreplace'), stderr)
1087

1088
    def test_loop(self):
1089
        for i in range(1000):
1090
            self.assertEqual(f'i:{i}', 'i:' + str(i))
1091

1092
    def test_dict(self):
1093
        d = {'"': 'dquote',
1094
             "'": 'squote',
1095
             'foo': 'bar',
1096
             }
1097
        self.assertEqual(f'''{d["'"]}''', 'squote')
1098
        self.assertEqual(f"""{d['"']}""", 'dquote')
1099

1100
        self.assertEqual(f'{d["foo"]}', 'bar')
1101
        self.assertEqual(f"{d['foo']}", 'bar')
1102

1103
    def __test_backslash_char(self):
1104
        # Check eval of a backslash followed by a control char.
1105
        # See bpo-30682: this used to raise an assert in pydebug mode.
1106
        self.assertEqual(cy_eval('f"\\\n"'), '')
1107
        self.assertEqual(cy_eval('f"\\\r"'), '')
1108

1109
    def test_debug_conversion(self):
1110
        x = 'A string'
1111
        self.assertEqual(f'{x=}', 'x=' + repr(x))
1112
        self.assertEqual(f'{x =}', 'x =' + repr(x))
1113
        self.assertEqual(f'{x=!s}', 'x=' + str(x))
1114
        self.assertEqual(f'{x=!r}', 'x=' + repr(x))
1115
        self.assertEqual(f'{x=!a}', 'x=' + ascii(x))
1116

1117
        x = 2.71828
1118
        self.assertEqual(f'{x=:.2f}', 'x=' + format(x, '.2f'))
1119
        self.assertEqual(f'{x=:}', 'x=' + format(x, ''))
1120
        self.assertEqual(f'{x=!r:^20}', 'x=' + format(repr(x), '^20'))
1121
        self.assertEqual(f'{x=!s:^20}', 'x=' + format(str(x), '^20'))
1122
        self.assertEqual(f'{x=!a:^20}', 'x=' + format(ascii(x), '^20'))
1123

1124
        x = 9
1125
        self.assertEqual(f'{3*x+15=}', '3*x+15=42')
1126

1127
        # There is code in ast.c that deals with non-ascii expression values.  So,
1128
        # use a unicode identifier to trigger that.
1129
        tenπ = 31.4
1130
        self.assertEqual(f'{tenπ=:.2f}', 'tenπ=31.40')
1131

1132
        # Also test with Unicode in non-identifiers.
1133
        self.assertEqual(f'{"Σ"=}', '"Σ"=\'Σ\'')
1134

1135
        # Make sure nested fstrings still work.
1136
        self.assertEqual(f'{f"{3.1415=:.1f}":*^20}', '*****3.1415=3.1*****')
1137

1138
        # Make sure text before and after an expression with = works
1139
        # correctly.
1140
        pi = 'π'
1141
        self.assertEqual(f'alpha α {pi=} ω omega', u"alpha α pi='π' ω omega")
1142

1143
        # Check multi-line expressions.
1144
        self.assertEqual(f'''{
1145
3
1146
=}''', '\n3\n=3')
1147

1148
        # Since = is handled specially, make sure all existing uses of
1149
        # it still work.
1150

1151
        self.assertEqual(f'{0==1}', 'False')
1152
        self.assertEqual(f'{0!=1}', 'True')
1153
        self.assertEqual(f'{0<=1}', 'True')
1154
        self.assertEqual(f'{0>=1}', 'False')
1155
        self.assertEqual(f'{(x:="5")}', '5')
1156
        self.assertEqual(x, '5')
1157
        self.assertEqual(f'{(x:=5)}', '5')
1158
        self.assertEqual(x, 5)
1159
        self.assertEqual(f'{"="}', '=')
1160

1161
        x = 20
1162
        # This isn't an assignment expression, it's 'x', with a format
1163
        # spec of '=10'.  See test_walrus: you need to use parens.
1164
        self.assertEqual(f'{x:=10}', '        20')
1165

1166
        # Test named function parameters, to make sure '=' parsing works
1167
        # there.
1168
        def f(a):
1169
            nonlocal x
1170
            oldx = x
1171
            x = a
1172
            return oldx
1173
        x = 0
1174
        self.assertEqual(f'{f(a="3=")}', '0')
1175
        self.assertEqual(x, '3=')
1176
        self.assertEqual(f'{f(a=4)}', '3=')
1177
        self.assertEqual(x, 4)
1178

1179
        # Make sure __format__ is being called.
1180
        class C:
1181
            def __format__(self, s):
1182
                return f'FORMAT-{s}'
1183
            def __repr__(self):
1184
                return 'REPR'
1185

1186
        self.assertEqual(f'{C()=}', 'C()=REPR')
1187
        self.assertEqual(f'{C()=!r}', 'C()=REPR')
1188
        self.assertEqual(f'{C()=:}', 'C()=FORMAT-')
1189
        self.assertEqual(f'{C()=: }', 'C()=FORMAT- ')
1190
        self.assertEqual(f'{C()=:x}', 'C()=FORMAT-x')
1191
        self.assertEqual(f'{C()=!r:*^20}', 'C()=********REPR********')
1192

1193
        self.assertRaises(SyntaxError, eval, "f'{C=]'")
1194

1195
        # Make sure leading and following text works.
1196
        x = 'foo'
1197
        self.assertEqual(f'X{x=}Y', 'Xx='+repr(x)+'Y')
1198

1199
        # Make sure whitespace around the = works.
1200
        self.assertEqual(f'X{x  =}Y', 'Xx  ='+repr(x)+'Y')
1201
        self.assertEqual(f'X{x=  }Y', 'Xx=  '+repr(x)+'Y')
1202
        self.assertEqual(f'X{x  =  }Y', 'Xx  =  '+repr(x)+'Y')
1203

1204
        # These next lines contains tabs.  Backslash escapes don't
1205
        # work in f-strings.
1206
        # patchcheck doesn't like these tabs.  So the only way to test
1207
        # this will be to dynamically created and exec the f-strings.  But
1208
        # that's such a hassle I'll save it for another day.  For now, convert
1209
        # the tabs to spaces just to shut up patchcheck.
1210
        #self.assertEqual(f'X{x =}Y', 'Xx\t='+repr(x)+'Y')
1211
        #self.assertEqual(f'X{x =       }Y', 'Xx\t=\t'+repr(x)+'Y')
1212

1213

1214
    def test_walrus(self):
1215
        x = 20
1216
        # This isn't an assignment expression, it's 'x', with a format
1217
        # spec of '=10'.
1218
        self.assertEqual(f'{x:=10}', '        20')
1219

1220
        # This is an assignment expression, which requires parens.
1221
        self.assertEqual(f'{(x:=10)}', '10')
1222
        self.assertEqual(x, 10)
1223

1224
    def test_invalid_syntax_error_message(self):
1225
        # with self.assertRaisesRegex(SyntaxError, "f-string: invalid syntax"):
1226
        #     compile("f'{a $ b}'", "?", "exec")
1227
        self.assertAllRaise(CompileError, "f-string: invalid syntax", ["f'{a $ b}'"])
1228

1229

1230
if __name__ == '__main__':
1231
    unittest.main()
1232

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.