1
# cython: language_level=3
3
# tag: allow_unknown_names, f_strings, pep498
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
15
def cy_eval(s, **kwargs):
16
return cython_inline('return ' + s, force=True, **kwargs)
18
a_global = 'global variable'
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.
27
class TestCase(CythonTest):
28
def assertAllRaise(self, exception_type, regex, error_strings):
29
for str in error_strings:
31
if exception_type is SyntaxError:
37
assert held_errors(), "Invalid Cython code failed to raise SyntaxError: %r" % str
39
init_thread() # reset error status
42
cython_inline(str, quiet=True)
43
except exception_type:
46
assert False, "Invalid Cython code failed to raise %s: %r" % (exception_type, str)
48
init_thread() # reset error status
50
def test__format__lookup(self):
51
# Make sure __format__ is looked up on the type, not the instance.
53
def __format__(self, spec):
58
# Add a bound __format__ method to the 'y' instance, but not
61
y.__format__ = types.MethodType(lambda self, spec: 'instance', y)
63
self.assertEqual(f'{y}', format(y))
64
self.assertEqual(f'{y}', 'class')
65
self.assertEqual(format(x), format(y))
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
70
self.assertEqual(x.__format__(''), 'class')
71
self.assertEqual(y.__format__(''), 'instance')
73
# This is how __format__ is actually called.
74
self.assertEqual(type(x).__format__(x, ''), 'class')
75
self.assertEqual(type(y).__format__(y, ''), 'class')
78
# Inspired by https://bugs.python.org/issue24975
90
c = compile(t, '', 'exec')
92
# Make sure x was not called.
93
self.assertFalse(x.called)
95
# Actually run the code.
98
# Make sure x was called.
99
self.assertTrue(x.called)
101
def __test_ast_line_numbers(self):
106
self.assertEqual(type(t), ast.Module)
107
self.assertEqual(len(t.body), 2)
109
self.assertEqual(type(t.body[0]), ast.Assign)
110
self.assertEqual(t.body[0].lineno, 2)
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)
132
def __test_ast_line_numbers_multiple_formattedvalues(self):
134
f'no formatted values'
135
f'eggs {a * x()} spam {b + y()}'"""
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)
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)
184
def __test_ast_line_numbers_nested(self):
187
f'{a * f"-{x()}-"}'"""
189
self.assertEqual(type(t), ast.Module)
190
self.assertEqual(len(t.body), 2)
192
self.assertEqual(type(t.body[0]), ast.Assign)
193
self.assertEqual(t.body[0].lineno, 2)
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)
229
def __test_ast_line_numbers_duplicate_expression(self):
230
"""Duplicate expression
232
NOTE: this is currently broken, always sets location of the first
237
f'{a * x()} {a * x()} {a * x()}'
240
self.assertEqual(type(t), ast.Module)
241
self.assertEqual(len(t.body), 2)
243
self.assertEqual(type(t.body[0]), ast.Assign)
244
self.assertEqual(t.body[0].lineno, 2)
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
300
def __test_ast_line_numbers_multiline_fstring(self):
301
# See bpo-30465 for details.
312
self.assertEqual(type(t), ast.Module)
313
self.assertEqual(len(t.body), 2)
315
self.assertEqual(type(t.body[0]), ast.Assign)
316
self.assertEqual(t.body[0].lineno, 2)
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)
350
def test_docstring(self):
352
f'''Not a docstring'''
353
self.assertIsNone(f.__doc__)
355
'''Not a docstring''' \
357
self.assertIsNone(g.__doc__)
359
def __test_literal_eval(self):
360
with self.assertRaisesRegex(ValueError, 'malformed node or string'):
361
ast.literal_eval("f'x'")
363
def __test_ast_compile_time_concat(self):
366
expr = """x[0] = 'foo' f'{3}'"""
368
c = compile(t, '', 'exec')
370
self.assertEqual(x[0], 'foo3')
372
def test_compile_time_concat_errors(self):
373
self.assertAllRaise(SyntaxError,
374
'cannot mix bytes and nonbytes literals',
379
def test_literal(self):
380
self.assertEqual(f'', '')
381
self.assertEqual(f'a', 'a')
382
self.assertEqual(f' ', ' ')
384
def test_unterminated_string(self):
385
self.assertAllRaise(SyntaxError, 'f-string: unterminated string',
392
def test_mismatched_parens(self):
393
self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\}' "
394
r"does not match opening parenthesis '\('",
397
self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\)' "
398
r"does not match opening parenthesis '\['",
401
self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\]' "
402
r"does not match opening parenthesis '\('",
405
self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\}' "
406
r"does not match opening parenthesis '\['",
409
self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\}' "
410
r"does not match opening parenthesis '\('",
413
self.assertRaises(SyntaxError, eval, "f'{" + "("*500 + "}'")
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')
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')
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{}')
443
# Inside of strings, don't interpret doubled brackets.
444
self.assertEqual(f'{"{{}}"}', '{{}}')
446
self.assertAllRaise(TypeError, 'unhashable type',
447
["f'{ {{}} }'", # dict in a set
450
def test_compile_time_concat(self):
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'' '', '')
477
self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
478
["f'{3' f'}'", # can't concat to get a valid f-string
481
def test_comments(self):
482
# These aren't comments, since they're in strings.
484
self.assertEqual(f'{"#"}', '#')
485
self.assertEqual(f'{d["#"]}', 'hash')
487
self.assertAllRaise(SyntaxError, "f-string expression part cannot include '#'",
488
["f'{1#}'", # error because the expression becomes "(1#)"
492
self.assertAllRaise(SyntaxError, r"f-string: unmatched '\)'",
493
["f'{)#}'", # When wrapped in parens, this becomes
494
# '()#)'. Make sure that doesn't compile.
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 + "'"
509
# for i in range(250, 260):
510
# self.assertEqual(eval(build_fstr(i)), (x+' ')*i)
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)],
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
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)
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)
529
def test_format_specifier_expressions(self):
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')
544
self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
545
["""f'{"s"!r{":10"}}'""",
547
# This looks like a nested format spec.
550
self.assertAllRaise(SyntaxError, "f-string: invalid syntax",
551
[# Invalid syntax inside a nested spec.
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}}'",
562
self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character',
563
[# No expansion inside conversion or for
565
"""f'{"s"!{"r"}}'""",
568
def test_side_effect_order(self):
572
def __format__(self, spec):
577
self.assertEqual(f'{x} {x}', '1 2')
579
def test_missing_expression(self):
580
self.assertAllRaise(SyntaxError, 'f-string: empty expression not allowed',
589
# The Python parser ignores also the following
590
# whitespace characters in additional to a space.
593
# Catch the empty expression before the
594
# invalid conversion.
605
# We find the empty expression before the
606
# missing closing brace.
613
# Different error message is raised for other whitespace characters.
614
self.assertAllRaise(SyntaxError, r"invalid non-printable character U\+00A0",
619
def test_parens_in_expressions(self):
620
self.assertEqual(f'{3,}', '(3,)')
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',
628
"f'{,}'", # this is (,), which is an error
631
self.assertAllRaise(SyntaxError, r"f-string: unmatched '\)'",
635
self.assertAllRaise(SyntaxError, 'EOL while scanning string literal',
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')
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')
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')
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')
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')
676
self.assertEqual(f'2\x20', '2 ')
677
self.assertEqual(f'2\x203', '2 3')
678
self.assertEqual(f'\x203', ' 3')
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')
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}', '\\&')
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",
700
r"f'\N{GREEK CAPITAL LETTER DELTA'",
702
# Here are the non-f-string versions,
703
# which should give the same errors.
706
r"'\N{GREEK CAPITAL LETTER DELTA'",
709
def test_no_backslashes_in_expression_part(self):
710
self.assertAllRaise(SyntaxError, 'f-string expression part cannot include a backslash',
717
r"""rf'{"\N{LEFT CURLY BRACKET}"}'""",
721
def test_no_escapes_for_braces(self):
723
Only literal curly braces begin an expression.
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}')
731
def test_newlines_in_expressions(self):
732
self.assertEqual(f'{0}', '0')
733
self.assertEqual(rf'''{3+
736
def test_lambda(self):
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 ")
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',
748
def test_yield(self):
749
# Not terribly useful, but make sure the yield turns
750
# a function into a generator
756
self.assertEqual(next(g), 8)
757
self.assertEqual(next(g), None)
759
def test_yield_send(self):
761
yield f'x:{yield (lambda i: x * i)}'
765
self.assertEqual(the_lambda(4), 40)
766
self.assertEqual(g.send('string'), 'x:string')
768
def test_expressions_with_triple_quoted_strings(self):
769
self.assertEqual(f"{'''x'''}", 'x')
770
self.assertEqual(f"{'''eric's'''}", "eric's")
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')
780
def test_multiple_vars(self):
783
self.assertEqual(f'{x}{y}', '98abc')
785
self.assertEqual(f'X{x}{y}', 'X98abc')
786
self.assertEqual(f'{x}X{y}', '98Xabc')
787
self.assertEqual(f'{x}{y}X', '98abcX')
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')
793
self.assertEqual(f'X{x}Y{y}Z', 'X98YabcZ')
795
def test_closure(self):
801
self.assertEqual(outer('987')(), 'x:987')
802
self.assertEqual(outer(7)(), 'x:7')
804
def test_arguments(self):
807
return f'x={x*y:{width}}'
809
self.assertEqual(f('foo', 10), 'x=foofoo ')
811
self.assertEqual(f(10, 10), 'x= 20')
813
def test_locals(self):
815
self.assertEqual(f'v:{value}', 'v:123')
817
def test_missing_variable(self):
818
with self.assertRaises(NameError):
821
def test_missing_format_spec(self):
823
def __format__(self, spec):
828
self.assertEqual(f'{O():x}', 'x')
829
self.assertEqual(f'{O()}', '*')
830
self.assertEqual(f'{O():}', '*')
832
self.assertEqual(f'{3:}', '3')
833
self.assertEqual(f'{3!s:}', '3')
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'")
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'")
847
self.assertIn("module 'unittest' from", f'{unittest}')
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'")
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'")
866
self.assertEqual(f'{foo(10)}', 'x=10')
868
def test_nested_fstrings(self):
870
self.assertEqual(f'{f"{0}"*3}', '000')
871
self.assertEqual(f'{f"{y}"*3}', '555')
873
def test_invalid_string_prefixes(self):
874
single_quote_cases = ["fu''",
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)
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')
906
self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]}}',
908
self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]} }',
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.
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')
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.
925
self.assertEqual(f'{0==1}', 'False')
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 ')
933
self.assertEqual(f'{"a"}', 'a')
934
self.assertEqual(f'{"a"!r}', "'a'")
935
self.assertEqual(f'{"a"!a}', "'a'")
938
self.assertEqual(f'{"a!r"}', "a!r")
940
# Not a conversion, but show that ! is allowed in a format spec.
941
self.assertEqual(f'{3.14:!<10.10}', '3.14!!!!!!')
943
self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character',
950
"f'{3! s}'", # no space before conversion char
953
self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
960
def test_assignment(self):
961
self.assertAllRaise(SyntaxError, 'invalid syntax',
968
self.assertAllRaise(CompileError, 'invalid syntax', # CPython raises SyntaxError
973
def test_mismatched_braces(self):
974
self.assertAllRaise(SyntaxError, "f-string: single '}' is not allowed",
982
# Can't have { or } in a format spec.
987
self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
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')
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
1015
def test_fstring(x, expected):
1021
self.assertEqual(flag, expected)
1023
def test_concat_empty(x, expected):
1029
self.assertEqual(flag, expected)
1031
def test_concat_non_empty(x, expected):
1037
self.assertEqual(flag, expected)
1040
test_fstring(' ', 1)
1042
test_concat_empty('', 2)
1043
test_concat_empty(' ', 1)
1045
test_concat_non_empty('', 1)
1046
test_concat_non_empty(' ', 1)
1048
def test_empty_format_specifier(self):
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'")
1055
def test_str_format_differences(self):
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')
1066
def test_errors(self):
1069
self.assertAllRaise(exc, 'unsupported',
1070
[r"f'{(lambda: 0):x}'",
1073
self.assertAllRaise(ValueError, 'Unknown format code',
1078
def __test_filename_in_syntaxerror(self):
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)
1088
def test_loop(self):
1089
for i in range(1000):
1090
self.assertEqual(f'i:{i}', 'i:' + str(i))
1092
def test_dict(self):
1097
self.assertEqual(f'''{d["'"]}''', 'squote')
1098
self.assertEqual(f"""{d['"']}""", 'dquote')
1100
self.assertEqual(f'{d["foo"]}', 'bar')
1101
self.assertEqual(f"{d['foo']}", 'bar')
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"'), '')
1109
def test_debug_conversion(self):
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))
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'))
1125
self.assertEqual(f'{3*x+15=}', '3*x+15=42')
1127
# There is code in ast.c that deals with non-ascii expression values. So,
1128
# use a unicode identifier to trigger that.
1130
self.assertEqual(f'{tenπ=:.2f}', 'tenπ=31.40')
1132
# Also test with Unicode in non-identifiers.
1133
self.assertEqual(f'{"Σ"=}', '"Σ"=\'Σ\'')
1135
# Make sure nested fstrings still work.
1136
self.assertEqual(f'{f"{3.1415=:.1f}":*^20}', '*****3.1415=3.1*****')
1138
# Make sure text before and after an expression with = works
1141
self.assertEqual(f'alpha α {pi=} ω omega', u"alpha α pi='π' ω omega")
1143
# Check multi-line expressions.
1144
self.assertEqual(f'''{
1148
# Since = is handled specially, make sure all existing uses of
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'{"="}', '=')
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')
1166
# Test named function parameters, to make sure '=' parsing works
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)
1179
# Make sure __format__ is being called.
1181
def __format__(self, s):
1182
return f'FORMAT-{s}'
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********')
1193
self.assertRaises(SyntaxError, eval, "f'{C=]'")
1195
# Make sure leading and following text works.
1197
self.assertEqual(f'X{x=}Y', 'Xx='+repr(x)+'Y')
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')
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')
1214
def test_walrus(self):
1216
# This isn't an assignment expression, it's 'x', with a format
1218
self.assertEqual(f'{x:=10}', ' 20')
1220
# This is an assignment expression, which requires parens.
1221
self.assertEqual(f'{(x:=10)}', '10')
1222
self.assertEqual(x, 10)
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}'"])
1230
if __name__ == '__main__':