blob: bd1ca943c7c09407828be82b9ee8bc01a5848ff2 [file] [log] [blame]
Eric V. Smith9a4135e2019-05-08 16:28:48 -04001# -*- 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
Eric V. Smith235a6f02015-09-19 14:51:32 -040010import ast
Lysandros Nikolaouf7b1e462020-05-26 03:32:18 +030011import os
han-solo0d6aa7f2020-09-01 10:34:29 -040012import re
Eric V. Smith235a6f02015-09-19 14:51:32 -040013import types
14import decimal
15import unittest
Hai Shia089d212020-07-06 17:15:08 +080016from test.support.os_helper import temp_cwd
Lysandros Nikolaouf7b1e462020-05-26 03:32:18 +030017from test.support.script_helper import assert_python_failure
Eric V. Smith235a6f02015-09-19 14:51:32 -040018
19a_global = 'global variable'
20
21# You could argue that I'm too strict in looking for specific error
22# values with assertRaisesRegex, but without it it's way too easy to
23# make a syntax error in the test strings. Especially with all of the
24# triple quotes, raw strings, backslashes, etc. I think it's a
25# worthwhile tradeoff. When I switched to this method, I found many
26# examples where I wasn't testing what I thought I was.
27
28class TestCase(unittest.TestCase):
29 def assertAllRaise(self, exception_type, regex, error_strings):
30 for str in error_strings:
31 with self.subTest(str=str):
32 with self.assertRaisesRegex(exception_type, regex):
33 eval(str)
34
35 def test__format__lookup(self):
36 # Make sure __format__ is looked up on the type, not the instance.
37 class X:
38 def __format__(self, spec):
39 return 'class'
40
41 x = X()
42
43 # Add a bound __format__ method to the 'y' instance, but not
44 # the 'x' instance.
45 y = X()
46 y.__format__ = types.MethodType(lambda self, spec: 'instance', y)
47
48 self.assertEqual(f'{y}', format(y))
49 self.assertEqual(f'{y}', 'class')
50 self.assertEqual(format(x), format(y))
51
52 # __format__ is not called this way, but still make sure it
53 # returns what we expect (so we can make sure we're bypassing
54 # it).
55 self.assertEqual(x.__format__(''), 'class')
56 self.assertEqual(y.__format__(''), 'instance')
57
58 # This is how __format__ is actually called.
59 self.assertEqual(type(x).__format__(x, ''), 'class')
60 self.assertEqual(type(y).__format__(y, ''), 'class')
61
62 def test_ast(self):
63 # Inspired by http://bugs.python.org/issue24975
64 class X:
65 def __init__(self):
66 self.called = False
67 def __call__(self):
68 self.called = True
69 return 4
70 x = X()
71 expr = """
72a = 10
73f'{a * x()}'"""
74 t = ast.parse(expr)
75 c = compile(t, '', 'exec')
76
77 # Make sure x was not called.
78 self.assertFalse(x.called)
79
80 # Actually run the code.
81 exec(c)
82
83 # Make sure x was called.
84 self.assertTrue(x.called)
85
Łukasz Langae7c566c2017-09-06 17:27:58 -070086 def test_ast_line_numbers(self):
87 expr = """
88a = 10
89f'{a * x()}'"""
90 t = ast.parse(expr)
91 self.assertEqual(type(t), ast.Module)
92 self.assertEqual(len(t.body), 2)
93 # check `a = 10`
94 self.assertEqual(type(t.body[0]), ast.Assign)
95 self.assertEqual(t.body[0].lineno, 2)
96 # check `f'...'`
97 self.assertEqual(type(t.body[1]), ast.Expr)
98 self.assertEqual(type(t.body[1].value), ast.JoinedStr)
99 self.assertEqual(len(t.body[1].value.values), 1)
100 self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)
101 self.assertEqual(t.body[1].lineno, 3)
102 self.assertEqual(t.body[1].value.lineno, 3)
103 self.assertEqual(t.body[1].value.values[0].lineno, 3)
104 # check the binop location
105 binop = t.body[1].value.values[0].value
106 self.assertEqual(type(binop), ast.BinOp)
107 self.assertEqual(type(binop.left), ast.Name)
108 self.assertEqual(type(binop.op), ast.Mult)
109 self.assertEqual(type(binop.right), ast.Call)
110 self.assertEqual(binop.lineno, 3)
111 self.assertEqual(binop.left.lineno, 3)
112 self.assertEqual(binop.right.lineno, 3)
113 self.assertEqual(binop.col_offset, 3)
114 self.assertEqual(binop.left.col_offset, 3)
115 self.assertEqual(binop.right.col_offset, 7)
116
117 def test_ast_line_numbers_multiple_formattedvalues(self):
118 expr = """
119f'no formatted values'
120f'eggs {a * x()} spam {b + y()}'"""
121 t = ast.parse(expr)
122 self.assertEqual(type(t), ast.Module)
123 self.assertEqual(len(t.body), 2)
124 # check `f'no formatted value'`
125 self.assertEqual(type(t.body[0]), ast.Expr)
126 self.assertEqual(type(t.body[0].value), ast.JoinedStr)
127 self.assertEqual(t.body[0].lineno, 2)
128 # check `f'...'`
129 self.assertEqual(type(t.body[1]), ast.Expr)
130 self.assertEqual(type(t.body[1].value), ast.JoinedStr)
131 self.assertEqual(len(t.body[1].value.values), 4)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300132 self.assertEqual(type(t.body[1].value.values[0]), ast.Constant)
133 self.assertEqual(type(t.body[1].value.values[0].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700134 self.assertEqual(type(t.body[1].value.values[1]), ast.FormattedValue)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300135 self.assertEqual(type(t.body[1].value.values[2]), ast.Constant)
136 self.assertEqual(type(t.body[1].value.values[2].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700137 self.assertEqual(type(t.body[1].value.values[3]), ast.FormattedValue)
138 self.assertEqual(t.body[1].lineno, 3)
139 self.assertEqual(t.body[1].value.lineno, 3)
140 self.assertEqual(t.body[1].value.values[0].lineno, 3)
141 self.assertEqual(t.body[1].value.values[1].lineno, 3)
142 self.assertEqual(t.body[1].value.values[2].lineno, 3)
143 self.assertEqual(t.body[1].value.values[3].lineno, 3)
144 # check the first binop location
145 binop1 = t.body[1].value.values[1].value
146 self.assertEqual(type(binop1), ast.BinOp)
147 self.assertEqual(type(binop1.left), ast.Name)
148 self.assertEqual(type(binop1.op), ast.Mult)
149 self.assertEqual(type(binop1.right), ast.Call)
150 self.assertEqual(binop1.lineno, 3)
151 self.assertEqual(binop1.left.lineno, 3)
152 self.assertEqual(binop1.right.lineno, 3)
153 self.assertEqual(binop1.col_offset, 8)
154 self.assertEqual(binop1.left.col_offset, 8)
155 self.assertEqual(binop1.right.col_offset, 12)
156 # check the second binop location
157 binop2 = t.body[1].value.values[3].value
158 self.assertEqual(type(binop2), ast.BinOp)
159 self.assertEqual(type(binop2.left), ast.Name)
160 self.assertEqual(type(binop2.op), ast.Add)
161 self.assertEqual(type(binop2.right), ast.Call)
162 self.assertEqual(binop2.lineno, 3)
163 self.assertEqual(binop2.left.lineno, 3)
164 self.assertEqual(binop2.right.lineno, 3)
165 self.assertEqual(binop2.col_offset, 23)
166 self.assertEqual(binop2.left.col_offset, 23)
167 self.assertEqual(binop2.right.col_offset, 27)
168
169 def test_ast_line_numbers_nested(self):
170 expr = """
171a = 10
172f'{a * f"-{x()}-"}'"""
173 t = ast.parse(expr)
174 self.assertEqual(type(t), ast.Module)
175 self.assertEqual(len(t.body), 2)
176 # check `a = 10`
177 self.assertEqual(type(t.body[0]), ast.Assign)
178 self.assertEqual(t.body[0].lineno, 2)
179 # check `f'...'`
180 self.assertEqual(type(t.body[1]), ast.Expr)
181 self.assertEqual(type(t.body[1].value), ast.JoinedStr)
182 self.assertEqual(len(t.body[1].value.values), 1)
183 self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)
184 self.assertEqual(t.body[1].lineno, 3)
185 self.assertEqual(t.body[1].value.lineno, 3)
186 self.assertEqual(t.body[1].value.values[0].lineno, 3)
187 # check the binop location
188 binop = t.body[1].value.values[0].value
189 self.assertEqual(type(binop), ast.BinOp)
190 self.assertEqual(type(binop.left), ast.Name)
191 self.assertEqual(type(binop.op), ast.Mult)
192 self.assertEqual(type(binop.right), ast.JoinedStr)
193 self.assertEqual(binop.lineno, 3)
194 self.assertEqual(binop.left.lineno, 3)
195 self.assertEqual(binop.right.lineno, 3)
196 self.assertEqual(binop.col_offset, 3)
197 self.assertEqual(binop.left.col_offset, 3)
198 self.assertEqual(binop.right.col_offset, 7)
199 # check the nested call location
200 self.assertEqual(len(binop.right.values), 3)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300201 self.assertEqual(type(binop.right.values[0]), ast.Constant)
202 self.assertEqual(type(binop.right.values[0].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700203 self.assertEqual(type(binop.right.values[1]), ast.FormattedValue)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300204 self.assertEqual(type(binop.right.values[2]), ast.Constant)
205 self.assertEqual(type(binop.right.values[2].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700206 self.assertEqual(binop.right.values[0].lineno, 3)
207 self.assertEqual(binop.right.values[1].lineno, 3)
208 self.assertEqual(binop.right.values[2].lineno, 3)
209 call = binop.right.values[1].value
210 self.assertEqual(type(call), ast.Call)
211 self.assertEqual(call.lineno, 3)
Lysandros Nikolaou37af21b2020-04-29 03:43:50 +0300212 self.assertEqual(call.col_offset, 11)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700213
214 def test_ast_line_numbers_duplicate_expression(self):
Łukasz Langae7c566c2017-09-06 17:27:58 -0700215 expr = """
216a = 10
217f'{a * x()} {a * x()} {a * x()}'
218"""
219 t = ast.parse(expr)
220 self.assertEqual(type(t), ast.Module)
221 self.assertEqual(len(t.body), 2)
222 # check `a = 10`
223 self.assertEqual(type(t.body[0]), ast.Assign)
224 self.assertEqual(t.body[0].lineno, 2)
225 # check `f'...'`
226 self.assertEqual(type(t.body[1]), ast.Expr)
227 self.assertEqual(type(t.body[1].value), ast.JoinedStr)
228 self.assertEqual(len(t.body[1].value.values), 5)
229 self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300230 self.assertEqual(type(t.body[1].value.values[1]), ast.Constant)
231 self.assertEqual(type(t.body[1].value.values[1].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700232 self.assertEqual(type(t.body[1].value.values[2]), ast.FormattedValue)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300233 self.assertEqual(type(t.body[1].value.values[3]), ast.Constant)
234 self.assertEqual(type(t.body[1].value.values[3].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700235 self.assertEqual(type(t.body[1].value.values[4]), ast.FormattedValue)
236 self.assertEqual(t.body[1].lineno, 3)
237 self.assertEqual(t.body[1].value.lineno, 3)
238 self.assertEqual(t.body[1].value.values[0].lineno, 3)
239 self.assertEqual(t.body[1].value.values[1].lineno, 3)
240 self.assertEqual(t.body[1].value.values[2].lineno, 3)
241 self.assertEqual(t.body[1].value.values[3].lineno, 3)
242 self.assertEqual(t.body[1].value.values[4].lineno, 3)
243 # check the first binop location
244 binop = t.body[1].value.values[0].value
245 self.assertEqual(type(binop), ast.BinOp)
246 self.assertEqual(type(binop.left), ast.Name)
247 self.assertEqual(type(binop.op), ast.Mult)
248 self.assertEqual(type(binop.right), ast.Call)
249 self.assertEqual(binop.lineno, 3)
250 self.assertEqual(binop.left.lineno, 3)
251 self.assertEqual(binop.right.lineno, 3)
252 self.assertEqual(binop.col_offset, 3)
253 self.assertEqual(binop.left.col_offset, 3)
254 self.assertEqual(binop.right.col_offset, 7)
255 # check the second binop location
256 binop = t.body[1].value.values[2].value
257 self.assertEqual(type(binop), ast.BinOp)
258 self.assertEqual(type(binop.left), ast.Name)
259 self.assertEqual(type(binop.op), ast.Mult)
260 self.assertEqual(type(binop.right), ast.Call)
261 self.assertEqual(binop.lineno, 3)
262 self.assertEqual(binop.left.lineno, 3)
263 self.assertEqual(binop.right.lineno, 3)
Pablo Galindo Salgadoc28c2e12021-08-12 17:41:21 +0100264 self.assertEqual(binop.col_offset, 13)
265 self.assertEqual(binop.left.col_offset, 13)
266 self.assertEqual(binop.right.col_offset, 17)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700267 # check the third binop location
268 binop = t.body[1].value.values[4].value
269 self.assertEqual(type(binop), ast.BinOp)
270 self.assertEqual(type(binop.left), ast.Name)
271 self.assertEqual(type(binop.op), ast.Mult)
272 self.assertEqual(type(binop.right), ast.Call)
273 self.assertEqual(binop.lineno, 3)
274 self.assertEqual(binop.left.lineno, 3)
275 self.assertEqual(binop.right.lineno, 3)
Pablo Galindo Salgadoc28c2e12021-08-12 17:41:21 +0100276 self.assertEqual(binop.col_offset, 23)
277 self.assertEqual(binop.left.col_offset, 23)
278 self.assertEqual(binop.right.col_offset, 27)
279
280 def test_ast_numbers_fstring_with_formatting(self):
281
282 t = ast.parse('f"Here is that pesky {xxx:.3f} again"')
283 self.assertEqual(len(t.body), 1)
284 self.assertEqual(t.body[0].lineno, 1)
285
286 self.assertEqual(type(t.body[0]), ast.Expr)
287 self.assertEqual(type(t.body[0].value), ast.JoinedStr)
288 self.assertEqual(len(t.body[0].value.values), 3)
289
290 self.assertEqual(type(t.body[0].value.values[0]), ast.Constant)
291 self.assertEqual(type(t.body[0].value.values[1]), ast.FormattedValue)
292 self.assertEqual(type(t.body[0].value.values[2]), ast.Constant)
293
294 _, expr, _ = t.body[0].value.values
295
296 name = expr.value
297 self.assertEqual(type(name), ast.Name)
298 self.assertEqual(name.lineno, 1)
299 self.assertEqual(name.end_lineno, 1)
300 self.assertEqual(name.col_offset, 22)
301 self.assertEqual(name.end_col_offset, 25)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700302
303 def test_ast_line_numbers_multiline_fstring(self):
Anthony Sottile995d9b92019-01-12 20:05:13 -0800304 # See bpo-30465 for details.
Łukasz Langae7c566c2017-09-06 17:27:58 -0700305 expr = """
306a = 10
307f'''
308 {a
309 *
310 x()}
311non-important content
312'''
313"""
314 t = ast.parse(expr)
315 self.assertEqual(type(t), ast.Module)
316 self.assertEqual(len(t.body), 2)
317 # check `a = 10`
318 self.assertEqual(type(t.body[0]), ast.Assign)
319 self.assertEqual(t.body[0].lineno, 2)
320 # check `f'...'`
321 self.assertEqual(type(t.body[1]), ast.Expr)
322 self.assertEqual(type(t.body[1].value), ast.JoinedStr)
323 self.assertEqual(len(t.body[1].value.values), 3)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300324 self.assertEqual(type(t.body[1].value.values[0]), ast.Constant)
325 self.assertEqual(type(t.body[1].value.values[0].value), str)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700326 self.assertEqual(type(t.body[1].value.values[1]), ast.FormattedValue)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300327 self.assertEqual(type(t.body[1].value.values[2]), ast.Constant)
328 self.assertEqual(type(t.body[1].value.values[2].value), str)
Anthony Sottile995d9b92019-01-12 20:05:13 -0800329 self.assertEqual(t.body[1].lineno, 3)
330 self.assertEqual(t.body[1].value.lineno, 3)
331 self.assertEqual(t.body[1].value.values[0].lineno, 3)
332 self.assertEqual(t.body[1].value.values[1].lineno, 3)
333 self.assertEqual(t.body[1].value.values[2].lineno, 3)
334 self.assertEqual(t.body[1].col_offset, 0)
335 self.assertEqual(t.body[1].value.col_offset, 0)
336 self.assertEqual(t.body[1].value.values[0].col_offset, 0)
337 self.assertEqual(t.body[1].value.values[1].col_offset, 0)
338 self.assertEqual(t.body[1].value.values[2].col_offset, 0)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700339 # NOTE: the following lineno information and col_offset is correct for
340 # expressions within FormattedValues.
341 binop = t.body[1].value.values[1].value
342 self.assertEqual(type(binop), ast.BinOp)
343 self.assertEqual(type(binop.left), ast.Name)
344 self.assertEqual(type(binop.op), ast.Mult)
345 self.assertEqual(type(binop.right), ast.Call)
346 self.assertEqual(binop.lineno, 4)
347 self.assertEqual(binop.left.lineno, 4)
348 self.assertEqual(binop.right.lineno, 6)
Anthony Sottile995d9b92019-01-12 20:05:13 -0800349 self.assertEqual(binop.col_offset, 4)
350 self.assertEqual(binop.left.col_offset, 4)
Łukasz Langae7c566c2017-09-06 17:27:58 -0700351 self.assertEqual(binop.right.col_offset, 7)
352
Pablo Galindobd2728b2021-01-03 01:11:41 +0000353 def test_ast_line_numbers_with_parentheses(self):
354 expr = """
355x = (
356 f" {test(t)}"
357)"""
358 t = ast.parse(expr)
359 self.assertEqual(type(t), ast.Module)
360 self.assertEqual(len(t.body), 1)
361 # check the test(t) location
362 call = t.body[0].value.values[1].value
363 self.assertEqual(type(call), ast.Call)
364 self.assertEqual(call.lineno, 3)
365 self.assertEqual(call.end_lineno, 3)
366 self.assertEqual(call.col_offset, 8)
367 self.assertEqual(call.end_col_offset, 15)
368
369 expr = """
370x = (
371 'PERL_MM_OPT', (
372 f'wat'
373 f'some_string={f(x)} '
374 f'wat'
375 ),
376)
377"""
378 t = ast.parse(expr)
379 self.assertEqual(type(t), ast.Module)
380 self.assertEqual(len(t.body), 1)
381 # check the fstring
382 fstring = t.body[0].value.elts[1]
383 self.assertEqual(type(fstring), ast.JoinedStr)
384 self.assertEqual(len(fstring.values), 3)
385 wat1, middle, wat2 = fstring.values
386 # check the first wat
387 self.assertEqual(type(wat1), ast.Constant)
388 self.assertEqual(wat1.lineno, 4)
389 self.assertEqual(wat1.end_lineno, 6)
390 self.assertEqual(wat1.col_offset, 12)
391 self.assertEqual(wat1.end_col_offset, 18)
392 # check the call
393 call = middle.value
394 self.assertEqual(type(call), ast.Call)
395 self.assertEqual(call.lineno, 5)
396 self.assertEqual(call.end_lineno, 5)
397 self.assertEqual(call.col_offset, 27)
398 self.assertEqual(call.end_col_offset, 31)
399 # check the second wat
400 self.assertEqual(type(wat2), ast.Constant)
401 self.assertEqual(wat2.lineno, 4)
402 self.assertEqual(wat2.end_lineno, 6)
403 self.assertEqual(wat2.col_offset, 12)
404 self.assertEqual(wat2.end_col_offset, 18)
405
Serhiy Storchaka4cc30ae2016-12-11 19:37:19 +0200406 def test_docstring(self):
407 def f():
408 f'''Not a docstring'''
409 self.assertIsNone(f.__doc__)
410 def g():
411 '''Not a docstring''' \
412 f''
413 self.assertIsNone(g.__doc__)
414
Eric V. Smith235a6f02015-09-19 14:51:32 -0400415 def test_literal_eval(self):
Eric V. Smith235a6f02015-09-19 14:51:32 -0400416 with self.assertRaisesRegex(ValueError, 'malformed node or string'):
Serhiy Storchaka4cc30ae2016-12-11 19:37:19 +0200417 ast.literal_eval("f'x'")
Eric V. Smith235a6f02015-09-19 14:51:32 -0400418
419 def test_ast_compile_time_concat(self):
420 x = ['']
421
422 expr = """x[0] = 'foo' f'{3}'"""
423 t = ast.parse(expr)
424 c = compile(t, '', 'exec')
425 exec(c)
426 self.assertEqual(x[0], 'foo3')
427
Eric V. Smith9b88fdf2016-11-07 17:54:01 -0500428 def test_compile_time_concat_errors(self):
429 self.assertAllRaise(SyntaxError,
430 'cannot mix bytes and nonbytes literals',
431 [r"""f'' b''""",
432 r"""b'' f''""",
433 ])
434
Eric V. Smith235a6f02015-09-19 14:51:32 -0400435 def test_literal(self):
436 self.assertEqual(f'', '')
437 self.assertEqual(f'a', 'a')
438 self.assertEqual(f' ', ' ')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400439
440 def test_unterminated_string(self):
441 self.assertAllRaise(SyntaxError, 'f-string: unterminated string',
442 [r"""f'{"x'""",
443 r"""f'{"x}'""",
444 r"""f'{("x'""",
445 r"""f'{("x}'""",
446 ])
447
448 def test_mismatched_parens(self):
Serhiy Storchaka58159ef2019-01-12 09:46:50 +0200449 self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\}' "
450 r"does not match opening parenthesis '\('",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400451 ["f'{((}'",
452 ])
Serhiy Storchaka58159ef2019-01-12 09:46:50 +0200453 self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\)' "
454 r"does not match opening parenthesis '\['",
455 ["f'{a[4)}'",
456 ])
457 self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\]' "
458 r"does not match opening parenthesis '\('",
459 ["f'{a(4]}'",
460 ])
461 self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\}' "
462 r"does not match opening parenthesis '\['",
463 ["f'{a[4}'",
464 ])
465 self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\}' "
466 r"does not match opening parenthesis '\('",
467 ["f'{a(4}'",
468 ])
469 self.assertRaises(SyntaxError, eval, "f'{" + "("*500 + "}'")
Eric V. Smith235a6f02015-09-19 14:51:32 -0400470
471 def test_double_braces(self):
472 self.assertEqual(f'{{', '{')
473 self.assertEqual(f'a{{', 'a{')
474 self.assertEqual(f'{{b', '{b')
475 self.assertEqual(f'a{{b', 'a{b')
476 self.assertEqual(f'}}', '}')
477 self.assertEqual(f'a}}', 'a}')
478 self.assertEqual(f'}}b', '}b')
479 self.assertEqual(f'a}}b', 'a}b')
Eric V. Smith451d0e32016-09-09 21:56:20 -0400480 self.assertEqual(f'{{}}', '{}')
481 self.assertEqual(f'a{{}}', 'a{}')
482 self.assertEqual(f'{{b}}', '{b}')
483 self.assertEqual(f'{{}}c', '{}c')
484 self.assertEqual(f'a{{b}}', 'a{b}')
485 self.assertEqual(f'a{{}}c', 'a{}c')
486 self.assertEqual(f'{{b}}c', '{b}c')
487 self.assertEqual(f'a{{b}}c', 'a{b}c')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400488
489 self.assertEqual(f'{{{10}', '{10')
490 self.assertEqual(f'}}{10}', '}10')
491 self.assertEqual(f'}}{{{10}', '}{10')
492 self.assertEqual(f'}}a{{{10}', '}a{10')
493
494 self.assertEqual(f'{10}{{', '10{')
495 self.assertEqual(f'{10}}}', '10}')
496 self.assertEqual(f'{10}}}{{', '10}{')
497 self.assertEqual(f'{10}}}a{{' '}', '10}a{}')
498
499 # Inside of strings, don't interpret doubled brackets.
500 self.assertEqual(f'{"{{}}"}', '{{}}')
501
502 self.assertAllRaise(TypeError, 'unhashable type',
503 ["f'{ {{}} }'", # dict in a set
504 ])
505
506 def test_compile_time_concat(self):
507 x = 'def'
508 self.assertEqual('abc' f'## {x}ghi', 'abc## defghi')
509 self.assertEqual('abc' f'{x}' 'ghi', 'abcdefghi')
510 self.assertEqual('abc' f'{x}' 'gh' f'i{x:4}', 'abcdefghidef ')
511 self.assertEqual('{x}' f'{x}', '{x}def')
512 self.assertEqual('{x' f'{x}', '{xdef')
513 self.assertEqual('{x}' f'{x}', '{x}def')
514 self.assertEqual('{{x}}' f'{x}', '{{x}}def')
515 self.assertEqual('{{x' f'{x}', '{{xdef')
516 self.assertEqual('x}}' f'{x}', 'x}}def')
517 self.assertEqual(f'{x}' 'x}}', 'defx}}')
518 self.assertEqual(f'{x}' '', 'def')
519 self.assertEqual('' f'{x}' '', 'def')
520 self.assertEqual('' f'{x}', 'def')
521 self.assertEqual(f'{x}' '2', 'def2')
522 self.assertEqual('1' f'{x}' '2', '1def2')
523 self.assertEqual('1' f'{x}', '1def')
524 self.assertEqual(f'{x}' f'-{x}', 'def-def')
525 self.assertEqual('' f'', '')
526 self.assertEqual('' f'' '', '')
527 self.assertEqual('' f'' '' f'', '')
528 self.assertEqual(f'', '')
529 self.assertEqual(f'' '', '')
530 self.assertEqual(f'' '' f'', '')
531 self.assertEqual(f'' '' f'' '', '')
532
533 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
534 ["f'{3' f'}'", # can't concat to get a valid f-string
535 ])
536
537 def test_comments(self):
538 # These aren't comments, since they're in strings.
539 d = {'#': 'hash'}
540 self.assertEqual(f'{"#"}', '#')
541 self.assertEqual(f'{d["#"]}', 'hash')
542
Eric V. Smith09835dc2016-09-11 18:58:20 -0400543 self.assertAllRaise(SyntaxError, "f-string expression part cannot include '#'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400544 ["f'{1#}'", # error because the expression becomes "(1#)"
545 "f'{3(#)}'",
Eric V. Smith09835dc2016-09-11 18:58:20 -0400546 "f'{#}'",
Serhiy Storchaka58159ef2019-01-12 09:46:50 +0200547 ])
548 self.assertAllRaise(SyntaxError, r"f-string: unmatched '\)'",
549 ["f'{)#}'", # When wrapped in parens, this becomes
Eric V. Smith35a24c52016-09-11 19:01:22 -0400550 # '()#)'. Make sure that doesn't compile.
Eric V. Smith235a6f02015-09-19 14:51:32 -0400551 ])
552
553 def test_many_expressions(self):
554 # Create a string with many expressions in it. Note that
555 # because we have a space in here as a literal, we're actually
556 # going to use twice as many ast nodes: one for each literal
557 # plus one for each expression.
558 def build_fstr(n, extra=''):
559 return "f'" + ('{x} ' * n) + extra + "'"
560
561 x = 'X'
562 width = 1
563
564 # Test around 256.
565 for i in range(250, 260):
566 self.assertEqual(eval(build_fstr(i)), (x+' ')*i)
567
568 # Test concatenating 2 largs fstrings.
569 self.assertEqual(eval(build_fstr(255)*256), (x+' ')*(255*256))
570
571 s = build_fstr(253, '{x:{width}} ')
572 self.assertEqual(eval(s), (x+' ')*254)
573
574 # Test lots of expressions and constants, concatenated.
575 s = "f'{1}' 'x' 'y'" * 1024
576 self.assertEqual(eval(s), '1xy' * 1024)
577
578 def test_format_specifier_expressions(self):
579 width = 10
580 precision = 4
581 value = decimal.Decimal('12.34567')
582 self.assertEqual(f'result: {value:{width}.{precision}}', 'result: 12.35')
583 self.assertEqual(f'result: {value:{width!r}.{precision}}', 'result: 12.35')
584 self.assertEqual(f'result: {value:{width:0}.{precision:1}}', 'result: 12.35')
585 self.assertEqual(f'result: {value:{1}{0:0}.{precision:1}}', 'result: 12.35')
586 self.assertEqual(f'result: {value:{ 1}{ 0:0}.{ precision:1}}', 'result: 12.35')
587 self.assertEqual(f'{10:#{1}0x}', ' 0xa')
588 self.assertEqual(f'{10:{"#"}1{0}{"x"}}', ' 0xa')
589 self.assertEqual(f'{-10:-{"#"}1{0}x}', ' -0xa')
590 self.assertEqual(f'{-10:{"-"}#{1}0{"x"}}', ' -0xa')
591 self.assertEqual(f'{10:#{3 != {4:5} and width}x}', ' 0xa')
592
593 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
594 ["""f'{"s"!r{":10"}}'""",
595
596 # This looks like a nested format spec.
597 ])
598
Lysandros Nikolaou2e0a9202020-06-26 14:24:05 +0300599 self.assertAllRaise(SyntaxError, "f-string: invalid syntax",
Martin Panter263893c2016-07-28 01:25:31 +0000600 [# Invalid syntax inside a nested spec.
Eric V. Smith235a6f02015-09-19 14:51:32 -0400601 "f'{4:{/5}}'",
602 ])
603
604 self.assertAllRaise(SyntaxError, "f-string: expressions nested too deeply",
605 [# Can't nest format specifiers.
606 "f'result: {value:{width:{0}}.{precision:1}}'",
607 ])
608
609 self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character',
610 [# No expansion inside conversion or for
611 # the : or ! itself.
612 """f'{"s"!{"r"}}'""",
613 ])
614
615 def test_side_effect_order(self):
616 class X:
617 def __init__(self):
618 self.i = 0
619 def __format__(self, spec):
620 self.i += 1
621 return str(self.i)
622
623 x = X()
624 self.assertEqual(f'{x} {x}', '1 2')
625
626 def test_missing_expression(self):
627 self.assertAllRaise(SyntaxError, 'f-string: empty expression not allowed',
628 ["f'{}'",
629 "f'{ }'"
630 "f' {} '",
631 "f'{!r}'",
632 "f'{ !r}'",
633 "f'{10:{ }}'",
634 "f' { } '",
Eric V. Smith1d44c412015-09-23 07:49:00 -0400635
Serhiy Storchaka2e9cd582017-06-08 23:43:54 +0300636 # The Python parser ignores also the following
637 # whitespace characters in additional to a space.
638 "f'''{\t\f\r\n}'''",
639
Eric V. Smith1d44c412015-09-23 07:49:00 -0400640 # Catch the empty expression before the
641 # invalid conversion.
642 "f'{!x}'",
643 "f'{ !xr}'",
644 "f'{!x:}'",
645 "f'{!x:a}'",
646 "f'{ !xr:}'",
647 "f'{ !xr:a}'",
Eric V. Smith548c4d32015-09-23 08:00:01 -0400648
649 "f'{!}'",
650 "f'{:}'",
Eric V. Smithb2080f62015-09-23 10:24:43 -0400651
652 # We find the empty expression before the
653 # missing closing brace.
654 "f'{!'",
655 "f'{!s:'",
656 "f'{:'",
657 "f'{:x'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400658 ])
659
Serhiy Storchaka2e9cd582017-06-08 23:43:54 +0300660 # Different error message is raised for other whitespace characters.
Serhiy Storchaka74ea6b52020-05-12 12:42:04 +0300661 self.assertAllRaise(SyntaxError, r"invalid non-printable character U\+00A0",
Serhiy Storchaka2e9cd582017-06-08 23:43:54 +0300662 ["f'''{\xa0}'''",
663 "\xa0",
664 ])
665
Eric V. Smith235a6f02015-09-19 14:51:32 -0400666 def test_parens_in_expressions(self):
667 self.assertEqual(f'{3,}', '(3,)')
668
669 # Add these because when an expression is evaluated, parens
670 # are added around it. But we shouldn't go from an invalid
671 # expression to a valid one. The added parens are just
672 # supposed to allow whitespace (including newlines).
Lysandros Nikolaou2e0a9202020-06-26 14:24:05 +0300673 self.assertAllRaise(SyntaxError, 'f-string: invalid syntax',
Eric V. Smith235a6f02015-09-19 14:51:32 -0400674 ["f'{,}'",
675 "f'{,}'", # this is (,), which is an error
676 ])
677
Serhiy Storchaka58159ef2019-01-12 09:46:50 +0200678 self.assertAllRaise(SyntaxError, r"f-string: unmatched '\)'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400679 ["f'{3)+(4}'",
680 ])
681
Batuhan Taskayaa698d522021-01-21 00:38:47 +0300682 self.assertAllRaise(SyntaxError, 'unterminated string literal',
Eric V. Smith235a6f02015-09-19 14:51:32 -0400683 ["f'{\n}'",
684 ])
Pablo Galindo40901512021-01-31 22:48:23 +0000685 def test_newlines_before_syntax_error(self):
686 self.assertAllRaise(SyntaxError, "invalid syntax",
687 ["f'{.}'", "\nf'{.}'", "\n\nf'{.}'"])
Eric V. Smith235a6f02015-09-19 14:51:32 -0400688
Eric V. Smith451d0e32016-09-09 21:56:20 -0400689 def test_backslashes_in_string_part(self):
690 self.assertEqual(f'\t', '\t')
691 self.assertEqual(r'\t', '\\t')
692 self.assertEqual(rf'\t', '\\t')
693 self.assertEqual(f'{2}\t', '2\t')
694 self.assertEqual(f'{2}\t{3}', '2\t3')
695 self.assertEqual(f'\t{3}', '\t3')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400696
Eric V. Smith451d0e32016-09-09 21:56:20 -0400697 self.assertEqual(f'\u0394', '\u0394')
698 self.assertEqual(r'\u0394', '\\u0394')
699 self.assertEqual(rf'\u0394', '\\u0394')
700 self.assertEqual(f'{2}\u0394', '2\u0394')
701 self.assertEqual(f'{2}\u0394{3}', '2\u03943')
702 self.assertEqual(f'\u0394{3}', '\u03943')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400703
Eric V. Smith451d0e32016-09-09 21:56:20 -0400704 self.assertEqual(f'\U00000394', '\u0394')
705 self.assertEqual(r'\U00000394', '\\U00000394')
706 self.assertEqual(rf'\U00000394', '\\U00000394')
707 self.assertEqual(f'{2}\U00000394', '2\u0394')
708 self.assertEqual(f'{2}\U00000394{3}', '2\u03943')
709 self.assertEqual(f'\U00000394{3}', '\u03943')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400710
Eric V. Smith451d0e32016-09-09 21:56:20 -0400711 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}', '\u0394')
712 self.assertEqual(f'{2}\N{GREEK CAPITAL LETTER DELTA}', '2\u0394')
713 self.assertEqual(f'{2}\N{GREEK CAPITAL LETTER DELTA}{3}', '2\u03943')
714 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}{3}', '\u03943')
715 self.assertEqual(f'2\N{GREEK CAPITAL LETTER DELTA}', '2\u0394')
716 self.assertEqual(f'2\N{GREEK CAPITAL LETTER DELTA}3', '2\u03943')
717 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}3', '\u03943')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400718
Eric V. Smith451d0e32016-09-09 21:56:20 -0400719 self.assertEqual(f'\x20', ' ')
720 self.assertEqual(r'\x20', '\\x20')
721 self.assertEqual(rf'\x20', '\\x20')
722 self.assertEqual(f'{2}\x20', '2 ')
723 self.assertEqual(f'{2}\x20{3}', '2 3')
724 self.assertEqual(f'\x20{3}', ' 3')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400725
Eric V. Smith451d0e32016-09-09 21:56:20 -0400726 self.assertEqual(f'2\x20', '2 ')
727 self.assertEqual(f'2\x203', '2 3')
728 self.assertEqual(f'\x203', ' 3')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400729
Gregory P. Smithb4be87a2019-08-10 00:19:07 -0700730 with self.assertWarns(DeprecationWarning): # invalid escape sequence
Serhiy Storchaka0cd7a3f2017-05-25 13:33:55 +0300731 value = eval(r"f'\{6*7}'")
732 self.assertEqual(value, '\\42')
733 self.assertEqual(f'\\{6*7}', '\\42')
734 self.assertEqual(fr'\{6*7}', '\\42')
735
736 AMPERSAND = 'spam'
737 # Get the right unicode character (&), or pick up local variable
738 # depending on the number of backslashes.
739 self.assertEqual(f'\N{AMPERSAND}', '&')
740 self.assertEqual(f'\\N{AMPERSAND}', '\\Nspam')
741 self.assertEqual(fr'\N{AMPERSAND}', '\\Nspam')
742 self.assertEqual(f'\\\N{AMPERSAND}', '\\&')
743
Eric V. Smith451d0e32016-09-09 21:56:20 -0400744 def test_misformed_unicode_character_name(self):
745 # These test are needed because unicode names are parsed
746 # differently inside f-strings.
747 self.assertAllRaise(SyntaxError, r"\(unicode error\) 'unicodeescape' codec can't decode bytes in position .*: malformed \\N character escape",
748 [r"f'\N'",
749 r"f'\N{'",
750 r"f'\N{GREEK CAPITAL LETTER DELTA'",
751
752 # Here are the non-f-string versions,
753 # which should give the same errors.
754 r"'\N'",
755 r"'\N{'",
756 r"'\N{GREEK CAPITAL LETTER DELTA'",
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400757 ])
758
Eric V. Smith451d0e32016-09-09 21:56:20 -0400759 def test_no_backslashes_in_expression_part(self):
760 self.assertAllRaise(SyntaxError, 'f-string expression part cannot include a backslash',
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400761 [r"f'{\'a\'}'",
762 r"f'{\t3}'",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400763 r"f'{\}'",
764 r"rf'{\'a\'}'",
765 r"rf'{\t3}'",
766 r"rf'{\}'",
767 r"""rf'{"\N{LEFT CURLY BRACKET}"}'""",
Jason R. Coombs45cab8c2016-11-06 11:01:08 -0500768 r"f'{\n}'",
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400769 ])
770
Eric V. Smith451d0e32016-09-09 21:56:20 -0400771 def test_no_escapes_for_braces(self):
Jason R. Coombs1c92a762016-11-06 11:25:54 -0500772 """
773 Only literal curly braces begin an expression.
774 """
775 # \x7b is '{'.
776 self.assertEqual(f'\x7b1+1}}', '{1+1}')
777 self.assertEqual(f'\x7b1+1', '{1+1')
778 self.assertEqual(f'\u007b1+1', '{1+1')
779 self.assertEqual(f'\N{LEFT CURLY BRACKET}1+1\N{RIGHT CURLY BRACKET}', '{1+1}')
Eric V. Smith6a4efce2016-09-03 09:18:34 -0400780
Eric V. Smith235a6f02015-09-19 14:51:32 -0400781 def test_newlines_in_expressions(self):
782 self.assertEqual(f'{0}', '0')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400783 self.assertEqual(rf'''{3+
7844}''', '7')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400785
786 def test_lambda(self):
787 x = 5
788 self.assertEqual(f'{(lambda y:x*y)("8")!r}', "'88888'")
789 self.assertEqual(f'{(lambda y:x*y)("8")!r:10}', "'88888' ")
790 self.assertEqual(f'{(lambda y:x*y)("8"):10}', "88888 ")
791
792 # lambda doesn't work without parens, because the colon
793 # makes the parser think it's a format_spec
Lysandros Nikolaou2e0a9202020-06-26 14:24:05 +0300794 self.assertAllRaise(SyntaxError, 'f-string: invalid syntax',
Eric V. Smith235a6f02015-09-19 14:51:32 -0400795 ["f'{lambda x:x}'",
796 ])
797
798 def test_yield(self):
799 # Not terribly useful, but make sure the yield turns
800 # a function into a generator
801 def fn(y):
802 f'y:{yield y*2}'
Pablo Galindo972ab032020-06-08 01:47:37 +0100803 f'{yield}'
Eric V. Smith235a6f02015-09-19 14:51:32 -0400804
805 g = fn(4)
806 self.assertEqual(next(g), 8)
Pablo Galindo972ab032020-06-08 01:47:37 +0100807 self.assertEqual(next(g), None)
Eric V. Smith235a6f02015-09-19 14:51:32 -0400808
809 def test_yield_send(self):
810 def fn(x):
811 yield f'x:{yield (lambda i: x * i)}'
812
813 g = fn(10)
814 the_lambda = next(g)
815 self.assertEqual(the_lambda(4), 40)
816 self.assertEqual(g.send('string'), 'x:string')
817
818 def test_expressions_with_triple_quoted_strings(self):
819 self.assertEqual(f"{'''x'''}", 'x')
820 self.assertEqual(f"{'''eric's'''}", "eric's")
Eric V. Smith235a6f02015-09-19 14:51:32 -0400821
822 # Test concatenation within an expression
823 self.assertEqual(f'{"x" """eric"s""" "y"}', 'xeric"sy')
824 self.assertEqual(f'{"x" """eric"s"""}', 'xeric"s')
825 self.assertEqual(f'{"""eric"s""" "y"}', 'eric"sy')
826 self.assertEqual(f'{"""x""" """eric"s""" "y"}', 'xeric"sy')
827 self.assertEqual(f'{"""x""" """eric"s""" """y"""}', 'xeric"sy')
828 self.assertEqual(f'{r"""x""" """eric"s""" """y"""}', 'xeric"sy')
829
830 def test_multiple_vars(self):
831 x = 98
832 y = 'abc'
833 self.assertEqual(f'{x}{y}', '98abc')
834
835 self.assertEqual(f'X{x}{y}', 'X98abc')
836 self.assertEqual(f'{x}X{y}', '98Xabc')
837 self.assertEqual(f'{x}{y}X', '98abcX')
838
839 self.assertEqual(f'X{x}Y{y}', 'X98Yabc')
840 self.assertEqual(f'X{x}{y}Y', 'X98abcY')
841 self.assertEqual(f'{x}X{y}Y', '98XabcY')
842
843 self.assertEqual(f'X{x}Y{y}Z', 'X98YabcZ')
844
845 def test_closure(self):
846 def outer(x):
847 def inner():
848 return f'x:{x}'
849 return inner
850
851 self.assertEqual(outer('987')(), 'x:987')
852 self.assertEqual(outer(7)(), 'x:7')
853
854 def test_arguments(self):
855 y = 2
856 def f(x, width):
857 return f'x={x*y:{width}}'
858
859 self.assertEqual(f('foo', 10), 'x=foofoo ')
860 x = 'bar'
861 self.assertEqual(f(10, 10), 'x= 20')
862
863 def test_locals(self):
864 value = 123
865 self.assertEqual(f'v:{value}', 'v:123')
866
867 def test_missing_variable(self):
868 with self.assertRaises(NameError):
869 f'v:{value}'
870
871 def test_missing_format_spec(self):
872 class O:
873 def __format__(self, spec):
874 if not spec:
875 return '*'
876 return spec
877
878 self.assertEqual(f'{O():x}', 'x')
879 self.assertEqual(f'{O()}', '*')
880 self.assertEqual(f'{O():}', '*')
881
882 self.assertEqual(f'{3:}', '3')
883 self.assertEqual(f'{3!s:}', '3')
884
885 def test_global(self):
886 self.assertEqual(f'g:{a_global}', 'g:global variable')
887 self.assertEqual(f'g:{a_global!r}', "g:'global variable'")
888
889 a_local = 'local variable'
890 self.assertEqual(f'g:{a_global} l:{a_local}',
891 'g:global variable l:local variable')
892 self.assertEqual(f'g:{a_global!r}',
893 "g:'global variable'")
894 self.assertEqual(f'g:{a_global} l:{a_local!r}',
895 "g:global variable l:'local variable'")
896
897 self.assertIn("module 'unittest' from", f'{unittest}')
898
899 def test_shadowed_global(self):
900 a_global = 'really a local'
901 self.assertEqual(f'g:{a_global}', 'g:really a local')
902 self.assertEqual(f'g:{a_global!r}', "g:'really a local'")
903
904 a_local = 'local variable'
905 self.assertEqual(f'g:{a_global} l:{a_local}',
906 'g:really a local l:local variable')
907 self.assertEqual(f'g:{a_global!r}',
908 "g:'really a local'")
909 self.assertEqual(f'g:{a_global} l:{a_local!r}',
910 "g:really a local l:'local variable'")
911
912 def test_call(self):
913 def foo(x):
914 return 'x=' + str(x)
915
916 self.assertEqual(f'{foo(10)}', 'x=10')
917
918 def test_nested_fstrings(self):
919 y = 5
920 self.assertEqual(f'{f"{0}"*3}', '000')
921 self.assertEqual(f'{f"{y}"*3}', '555')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400922
923 def test_invalid_string_prefixes(self):
Pablo Galindo70c188e2020-04-13 02:47:35 +0100924 single_quote_cases = ["fu''",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400925 "uf''",
926 "Fu''",
927 "fU''",
928 "Uf''",
929 "uF''",
930 "ufr''",
931 "urf''",
932 "fur''",
933 "fru''",
934 "rfu''",
935 "ruf''",
936 "FUR''",
937 "Fur''",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400938 "fb''",
939 "fB''",
940 "Fb''",
941 "FB''",
942 "bf''",
943 "bF''",
944 "Bf''",
Pablo Galindo70c188e2020-04-13 02:47:35 +0100945 "BF''",]
946 double_quote_cases = [case.replace("'", '"') for case in single_quote_cases]
Pablo Galindo Salgadoc72311d2021-11-25 01:01:40 +0000947 self.assertAllRaise(SyntaxError, 'invalid syntax',
Pablo Galindo70c188e2020-04-13 02:47:35 +0100948 single_quote_cases + double_quote_cases)
Eric V. Smith235a6f02015-09-19 14:51:32 -0400949
950 def test_leading_trailing_spaces(self):
951 self.assertEqual(f'{ 3}', '3')
952 self.assertEqual(f'{ 3}', '3')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400953 self.assertEqual(f'{3 }', '3')
954 self.assertEqual(f'{3 }', '3')
Eric V. Smith235a6f02015-09-19 14:51:32 -0400955
956 self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]}}',
957 'expr={1: 2}')
958 self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]} }',
959 'expr={1: 2}')
960
Eric V. Smith235a6f02015-09-19 14:51:32 -0400961 def test_not_equal(self):
962 # There's a special test for this because there's a special
963 # case in the f-string parser to look for != as not ending an
964 # expression. Normally it would, while looking for !s or !r.
965
966 self.assertEqual(f'{3!=4}', 'True')
967 self.assertEqual(f'{3!=4:}', 'True')
968 self.assertEqual(f'{3!=4!s}', 'True')
969 self.assertEqual(f'{3!=4!s:.3}', 'Tru')
970
Eric V. Smith9a4135e2019-05-08 16:28:48 -0400971 def test_equal_equal(self):
972 # Because an expression ending in = has special meaning,
973 # there's a special test for ==. Make sure it works.
974
975 self.assertEqual(f'{0==1}', 'False')
976
Eric V. Smith235a6f02015-09-19 14:51:32 -0400977 def test_conversions(self):
978 self.assertEqual(f'{3.14:10.10}', ' 3.14')
979 self.assertEqual(f'{3.14!s:10.10}', '3.14 ')
980 self.assertEqual(f'{3.14!r:10.10}', '3.14 ')
981 self.assertEqual(f'{3.14!a:10.10}', '3.14 ')
982
983 self.assertEqual(f'{"a"}', 'a')
984 self.assertEqual(f'{"a"!r}', "'a'")
985 self.assertEqual(f'{"a"!a}', "'a'")
986
987 # Not a conversion.
988 self.assertEqual(f'{"a!r"}', "a!r")
989
990 # Not a conversion, but show that ! is allowed in a format spec.
991 self.assertEqual(f'{3.14:!<10.10}', '3.14!!!!!!')
992
Eric V. Smith235a6f02015-09-19 14:51:32 -0400993 self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character',
994 ["f'{3!g}'",
995 "f'{3!A}'",
Eric V. Smith451d0e32016-09-09 21:56:20 -0400996 "f'{3!3}'",
997 "f'{3!G}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -0400998 "f'{3!!}'",
999 "f'{3!:}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -04001000 "f'{3! s}'", # no space before conversion char
Eric V. Smith235a6f02015-09-19 14:51:32 -04001001 ])
1002
1003 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
1004 ["f'{x!s{y}}'",
1005 "f'{3!ss}'",
1006 "f'{3!ss:}'",
1007 "f'{3!ss:s}'",
1008 ])
1009
1010 def test_assignment(self):
Pablo Galindob86ed8e2021-04-12 16:59:30 +01001011 self.assertAllRaise(SyntaxError, r'invalid syntax',
Eric V. Smith235a6f02015-09-19 14:51:32 -04001012 ["f'' = 3",
1013 "f'{0}' = x",
1014 "f'{x}' = x",
1015 ])
1016
1017 def test_del(self):
1018 self.assertAllRaise(SyntaxError, 'invalid syntax',
1019 ["del f''",
1020 "del '' f''",
1021 ])
1022
1023 def test_mismatched_braces(self):
1024 self.assertAllRaise(SyntaxError, "f-string: single '}' is not allowed",
1025 ["f'{{}'",
1026 "f'{{}}}'",
1027 "f'}'",
1028 "f'x}'",
1029 "f'x}x'",
Jason R. Coombsda25abf72016-11-06 11:14:48 -05001030 r"f'\u007b}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -04001031
1032 # Can't have { or } in a format spec.
1033 "f'{3:}>10}'",
Eric V. Smith235a6f02015-09-19 14:51:32 -04001034 "f'{3:}}>10}'",
1035 ])
1036
1037 self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
1038 ["f'{3:{{>10}'",
1039 "f'{3'",
1040 "f'{3!'",
1041 "f'{3:'",
1042 "f'{3!s'",
1043 "f'{3!s:'",
1044 "f'{3!s:3'",
1045 "f'x{'",
1046 "f'x{x'",
Eric V. Smith451d0e32016-09-09 21:56:20 -04001047 "f'{x'",
Eric V. Smith235a6f02015-09-19 14:51:32 -04001048 "f'{3:s'",
1049 "f'{{{'",
1050 "f'{{}}{'",
1051 "f'{'",
1052 ])
1053
Eric V. Smith235a6f02015-09-19 14:51:32 -04001054 # But these are just normal strings.
1055 self.assertEqual(f'{"{"}', '{')
1056 self.assertEqual(f'{"}"}', '}')
1057 self.assertEqual(f'{3:{"}"}>10}', '}}}}}}}}}3')
1058 self.assertEqual(f'{2:{"{"}>10}', '{{{{{{{{{2')
1059
1060 def test_if_conditional(self):
1061 # There's special logic in compile.c to test if the
1062 # conditional for an if (and while) are constants. Exercise
1063 # that code.
1064
1065 def test_fstring(x, expected):
1066 flag = 0
1067 if f'{x}':
1068 flag = 1
1069 else:
1070 flag = 2
1071 self.assertEqual(flag, expected)
1072
1073 def test_concat_empty(x, expected):
1074 flag = 0
1075 if '' f'{x}':
1076 flag = 1
1077 else:
1078 flag = 2
1079 self.assertEqual(flag, expected)
1080
1081 def test_concat_non_empty(x, expected):
1082 flag = 0
1083 if ' ' f'{x}':
1084 flag = 1
1085 else:
1086 flag = 2
1087 self.assertEqual(flag, expected)
1088
1089 test_fstring('', 2)
1090 test_fstring(' ', 1)
1091
1092 test_concat_empty('', 2)
1093 test_concat_empty(' ', 1)
1094
1095 test_concat_non_empty('', 1)
1096 test_concat_non_empty(' ', 1)
1097
1098 def test_empty_format_specifier(self):
1099 x = 'test'
1100 self.assertEqual(f'{x}', 'test')
1101 self.assertEqual(f'{x:}', 'test')
1102 self.assertEqual(f'{x!s:}', 'test')
1103 self.assertEqual(f'{x!r:}', "'test'")
1104
1105 def test_str_format_differences(self):
1106 d = {'a': 'string',
1107 0: 'integer',
1108 }
1109 a = 0
1110 self.assertEqual(f'{d[0]}', 'integer')
1111 self.assertEqual(f'{d["a"]}', 'string')
1112 self.assertEqual(f'{d[a]}', 'integer')
1113 self.assertEqual('{d[a]}'.format(d=d), 'string')
1114 self.assertEqual('{d[0]}'.format(d=d), 'integer')
1115
Eric V. Smith135d5f42016-02-05 18:23:08 -05001116 def test_errors(self):
1117 # see issue 26287
Serhiy Storchaka13c8f322016-10-31 08:13:00 +02001118 self.assertAllRaise(TypeError, 'unsupported',
Eric V. Smith135d5f42016-02-05 18:23:08 -05001119 [r"f'{(lambda: 0):x}'",
1120 r"f'{(0,):x}'",
1121 ])
1122 self.assertAllRaise(ValueError, 'Unknown format code',
1123 [r"f'{1000:j}'",
1124 r"f'{1000:j}'",
1125 ])
1126
Lysandros Nikolaouf7b1e462020-05-26 03:32:18 +03001127 def test_filename_in_syntaxerror(self):
1128 # see issue 38964
1129 with temp_cwd() as cwd:
1130 file_path = os.path.join(cwd, 't.py')
Inada Naoki3caea9a2021-04-04 17:01:10 +09001131 with open(file_path, 'w', encoding="utf-8") as f:
Lysandros Nikolaouf7b1e462020-05-26 03:32:18 +03001132 f.write('f"{a b}"') # This generates a SyntaxError
Serhiy Storchaka700cfa82020-06-25 17:56:31 +03001133 _, _, stderr = assert_python_failure(file_path,
1134 PYTHONIOENCODING='ascii')
1135 self.assertIn(file_path.encode('ascii', 'backslashreplace'), stderr)
Lysandros Nikolaouf7b1e462020-05-26 03:32:18 +03001136
Eric V. Smith235a6f02015-09-19 14:51:32 -04001137 def test_loop(self):
1138 for i in range(1000):
1139 self.assertEqual(f'i:{i}', 'i:' + str(i))
1140
1141 def test_dict(self):
1142 d = {'"': 'dquote',
1143 "'": 'squote',
1144 'foo': 'bar',
1145 }
Eric V. Smith235a6f02015-09-19 14:51:32 -04001146 self.assertEqual(f'''{d["'"]}''', 'squote')
1147 self.assertEqual(f"""{d['"']}""", 'dquote')
1148
1149 self.assertEqual(f'{d["foo"]}', 'bar')
1150 self.assertEqual(f"{d['foo']}", 'bar')
Eric V. Smith235a6f02015-09-19 14:51:32 -04001151
ericvsmith11e97f22017-06-16 06:19:32 -04001152 def test_backslash_char(self):
1153 # Check eval of a backslash followed by a control char.
1154 # See bpo-30682: this used to raise an assert in pydebug mode.
1155 self.assertEqual(eval('f"\\\n"'), '')
1156 self.assertEqual(eval('f"\\\r"'), '')
1157
Eric V. Smith9a4135e2019-05-08 16:28:48 -04001158 def test_debug_conversion(self):
1159 x = 'A string'
1160 self.assertEqual(f'{x=}', 'x=' + repr(x))
1161 self.assertEqual(f'{x =}', 'x =' + repr(x))
1162 self.assertEqual(f'{x=!s}', 'x=' + str(x))
1163 self.assertEqual(f'{x=!r}', 'x=' + repr(x))
1164 self.assertEqual(f'{x=!a}', 'x=' + ascii(x))
1165
1166 x = 2.71828
1167 self.assertEqual(f'{x=:.2f}', 'x=' + format(x, '.2f'))
1168 self.assertEqual(f'{x=:}', 'x=' + format(x, ''))
1169 self.assertEqual(f'{x=!r:^20}', 'x=' + format(repr(x), '^20'))
1170 self.assertEqual(f'{x=!s:^20}', 'x=' + format(str(x), '^20'))
1171 self.assertEqual(f'{x=!a:^20}', 'x=' + format(ascii(x), '^20'))
1172
1173 x = 9
1174 self.assertEqual(f'{3*x+15=}', '3*x+15=42')
1175
1176 # There is code in ast.c that deals with non-ascii expression values. So,
1177 # use a unicode identifier to trigger that.
1178 tenπ = 31.4
1179 self.assertEqual(f'{tenπ=:.2f}', 'tenπ=31.40')
1180
1181 # Also test with Unicode in non-identifiers.
1182 self.assertEqual(f'{"Σ"=}', '"Σ"=\'Σ\'')
1183
1184 # Make sure nested fstrings still work.
1185 self.assertEqual(f'{f"{3.1415=:.1f}":*^20}', '*****3.1415=3.1*****')
1186
1187 # Make sure text before and after an expression with = works
1188 # correctly.
1189 pi = 'π'
1190 self.assertEqual(f'alpha α {pi=} ω omega', "alpha α pi='π' ω omega")
1191
1192 # Check multi-line expressions.
1193 self.assertEqual(f'''{
11943
1195=}''', '\n3\n=3')
1196
1197 # Since = is handled specially, make sure all existing uses of
1198 # it still work.
1199
1200 self.assertEqual(f'{0==1}', 'False')
1201 self.assertEqual(f'{0!=1}', 'True')
1202 self.assertEqual(f'{0<=1}', 'True')
1203 self.assertEqual(f'{0>=1}', 'False')
1204 self.assertEqual(f'{(x:="5")}', '5')
1205 self.assertEqual(x, '5')
1206 self.assertEqual(f'{(x:=5)}', '5')
1207 self.assertEqual(x, 5)
1208 self.assertEqual(f'{"="}', '=')
1209
1210 x = 20
1211 # This isn't an assignment expression, it's 'x', with a format
1212 # spec of '=10'. See test_walrus: you need to use parens.
1213 self.assertEqual(f'{x:=10}', ' 20')
1214
1215 # Test named function parameters, to make sure '=' parsing works
1216 # there.
1217 def f(a):
1218 nonlocal x
1219 oldx = x
1220 x = a
1221 return oldx
1222 x = 0
1223 self.assertEqual(f'{f(a="3=")}', '0')
1224 self.assertEqual(x, '3=')
1225 self.assertEqual(f'{f(a=4)}', '3=')
1226 self.assertEqual(x, 4)
1227
1228 # Make sure __format__ is being called.
1229 class C:
1230 def __format__(self, s):
1231 return f'FORMAT-{s}'
1232 def __repr__(self):
1233 return 'REPR'
1234
1235 self.assertEqual(f'{C()=}', 'C()=REPR')
1236 self.assertEqual(f'{C()=!r}', 'C()=REPR')
1237 self.assertEqual(f'{C()=:}', 'C()=FORMAT-')
1238 self.assertEqual(f'{C()=: }', 'C()=FORMAT- ')
1239 self.assertEqual(f'{C()=:x}', 'C()=FORMAT-x')
1240 self.assertEqual(f'{C()=!r:*^20}', 'C()=********REPR********')
1241
Pablo Galindo26f55c22019-05-12 01:43:04 +01001242 self.assertRaises(SyntaxError, eval, "f'{C=]'")
1243
Eric V. Smith6f6ff8a2019-05-27 15:31:52 -04001244 # Make sure leading and following text works.
1245 x = 'foo'
1246 self.assertEqual(f'X{x=}Y', 'Xx='+repr(x)+'Y')
1247
1248 # Make sure whitespace around the = works.
1249 self.assertEqual(f'X{x =}Y', 'Xx ='+repr(x)+'Y')
1250 self.assertEqual(f'X{x= }Y', 'Xx= '+repr(x)+'Y')
1251 self.assertEqual(f'X{x = }Y', 'Xx = '+repr(x)+'Y')
1252
1253 # These next lines contains tabs. Backslash escapes don't
1254 # work in f-strings.
Min ho Kim96e12d52019-07-22 06:12:33 +10001255 # patchcheck doesn't like these tabs. So the only way to test
Eric V. Smith6f6ff8a2019-05-27 15:31:52 -04001256 # this will be to dynamically created and exec the f-strings. But
1257 # that's such a hassle I'll save it for another day. For now, convert
1258 # the tabs to spaces just to shut up patchcheck.
1259 #self.assertEqual(f'X{x =}Y', 'Xx\t='+repr(x)+'Y')
1260 #self.assertEqual(f'X{x = }Y', 'Xx\t=\t'+repr(x)+'Y')
1261
Eric V. Smith9a4135e2019-05-08 16:28:48 -04001262 def test_walrus(self):
1263 x = 20
1264 # This isn't an assignment expression, it's 'x', with a format
1265 # spec of '=10'.
1266 self.assertEqual(f'{x:=10}', ' 20')
1267
1268 # This is an assignment expression, which requires parens.
1269 self.assertEqual(f'{(x:=10)}', '10')
1270 self.assertEqual(x, 10)
1271
Lysandros Nikolaou2e0a9202020-06-26 14:24:05 +03001272 def test_invalid_syntax_error_message(self):
1273 with self.assertRaisesRegex(SyntaxError, "f-string: invalid syntax"):
1274 compile("f'{a $ b}'", "?", "exec")
1275
han-solo0d6aa7f2020-09-01 10:34:29 -04001276 def test_with_two_commas_in_format_specifier(self):
1277 error_msg = re.escape("Cannot specify ',' with ','.")
1278 with self.assertRaisesRegex(ValueError, error_msg):
1279 f'{1:,,}'
1280
1281 def test_with_two_underscore_in_format_specifier(self):
1282 error_msg = re.escape("Cannot specify '_' with '_'.")
1283 with self.assertRaisesRegex(ValueError, error_msg):
1284 f'{1:__}'
1285
1286 def test_with_a_commas_and_an_underscore_in_format_specifier(self):
1287 error_msg = re.escape("Cannot specify both ',' and '_'.")
1288 with self.assertRaisesRegex(ValueError, error_msg):
1289 f'{1:,_}'
1290
1291 def test_with_an_underscore_and_a_comma_in_format_specifier(self):
1292 error_msg = re.escape("Cannot specify both ',' and '_'.")
1293 with self.assertRaisesRegex(ValueError, error_msg):
han-solo749ed852020-09-02 04:56:37 -04001294 f'{1:_,}'
Łukasz Langae7c566c2017-09-06 17:27:58 -07001295
Pablo Galindo8efad612021-03-24 19:34:17 +00001296 def test_syntax_error_for_starred_expressions(self):
Pablo Galindob86ed8e2021-04-12 16:59:30 +01001297 error_msg = re.escape("cannot use starred expression here")
Pablo Galindo8efad612021-03-24 19:34:17 +00001298 with self.assertRaisesRegex(SyntaxError, error_msg):
1299 compile("f'{*a}'", "?", "exec")
1300
Pablo Galindob86ed8e2021-04-12 16:59:30 +01001301 error_msg = re.escape("cannot use double starred expression here")
Pablo Galindo8efad612021-03-24 19:34:17 +00001302 with self.assertRaisesRegex(SyntaxError, error_msg):
1303 compile("f'{**a}'", "?", "exec")
1304
Eric V. Smith235a6f02015-09-19 14:51:32 -04001305if __name__ == '__main__':
1306 unittest.main()