1# -*- coding: utf-8 -*- 2# There are tests here with unicode string literals and 3# identifiers. There's a code in ast.c that was added because of a 4# failure with a non-ascii-only expression. So, I have tests for 5# that. There are workarounds that would let me run tests for that 6# code without unicode identifiers and strings, but just using them 7# directly seems like the easiest and therefore safest thing to do. 8# Unicode identifiers in tests is allowed by PEP 3131. 9 10import ast 11import os 12import re 13import types 14import decimal 15import unittest 16from test.support.os_helper import temp_cwd 17from test.support.script_helper import assert_python_failure 18 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 86 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) 132 self.assertEqual(type(t.body[1].value.values[0]), ast.Constant) 133 self.assertEqual(type(t.body[1].value.values[0].value), str) 134 self.assertEqual(type(t.body[1].value.values[1]), ast.FormattedValue) 135 self.assertEqual(type(t.body[1].value.values[2]), ast.Constant) 136 self.assertEqual(type(t.body[1].value.values[2].value), str) 137 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) 201 self.assertEqual(type(binop.right.values[0]), ast.Constant) 202 self.assertEqual(type(binop.right.values[0].value), str) 203 self.assertEqual(type(binop.right.values[1]), ast.FormattedValue) 204 self.assertEqual(type(binop.right.values[2]), ast.Constant) 205 self.assertEqual(type(binop.right.values[2].value), str) 206 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) 212 self.assertEqual(call.col_offset, 11) 213 214 def test_ast_line_numbers_duplicate_expression(self): 215 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) 230 self.assertEqual(type(t.body[1].value.values[1]), ast.Constant) 231 self.assertEqual(type(t.body[1].value.values[1].value), str) 232 self.assertEqual(type(t.body[1].value.values[2]), ast.FormattedValue) 233 self.assertEqual(type(t.body[1].value.values[3]), ast.Constant) 234 self.assertEqual(type(t.body[1].value.values[3].value), str) 235 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) 264 self.assertEqual(binop.col_offset, 13) 265 self.assertEqual(binop.left.col_offset, 13) 266 self.assertEqual(binop.right.col_offset, 17) 267 # 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) 276 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) 302 303 def test_ast_line_numbers_multiline_fstring(self): 304 # See bpo-30465 for details. 305 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) 324 self.assertEqual(type(t.body[1].value.values[0]), ast.Constant) 325 self.assertEqual(type(t.body[1].value.values[0].value), str) 326 self.assertEqual(type(t.body[1].value.values[1]), ast.FormattedValue) 327 self.assertEqual(type(t.body[1].value.values[2]), ast.Constant) 328 self.assertEqual(type(t.body[1].value.values[2].value), str) 329 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) 339 # 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) 349 self.assertEqual(binop.col_offset, 3) 350 self.assertEqual(binop.left.col_offset, 3) 351 self.assertEqual(binop.right.col_offset, 7) 352 353 expr = """ 354a = f''' 355 {blech} 356 ''' 357""" 358 t = ast.parse(expr) 359 self.assertEqual(type(t), ast.Module) 360 self.assertEqual(len(t.body), 1) 361 # Check f'...' 362 self.assertEqual(type(t.body[0]), ast.Assign) 363 self.assertEqual(type(t.body[0].value), ast.JoinedStr) 364 self.assertEqual(len(t.body[0].value.values), 3) 365 self.assertEqual(type(t.body[0].value.values[1]), ast.FormattedValue) 366 self.assertEqual(t.body[0].lineno, 2) 367 self.assertEqual(t.body[0].value.lineno, 2) 368 self.assertEqual(t.body[0].value.values[0].lineno, 2) 369 self.assertEqual(t.body[0].value.values[1].lineno, 2) 370 self.assertEqual(t.body[0].value.values[2].lineno, 2) 371 self.assertEqual(t.body[0].col_offset, 0) 372 self.assertEqual(t.body[0].value.col_offset, 4) 373 self.assertEqual(t.body[0].value.values[0].col_offset, 4) 374 self.assertEqual(t.body[0].value.values[1].col_offset, 4) 375 self.assertEqual(t.body[0].value.values[2].col_offset, 4) 376 # Check {blech} 377 self.assertEqual(t.body[0].value.values[1].value.lineno, 3) 378 self.assertEqual(t.body[0].value.values[1].value.end_lineno, 3) 379 self.assertEqual(t.body[0].value.values[1].value.col_offset, 11) 380 self.assertEqual(t.body[0].value.values[1].value.end_col_offset, 16) 381 382 def test_ast_line_numbers_with_parentheses(self): 383 expr = """ 384x = ( 385 f" {test(t)}" 386)""" 387 t = ast.parse(expr) 388 self.assertEqual(type(t), ast.Module) 389 self.assertEqual(len(t.body), 1) 390 # check the test(t) location 391 call = t.body[0].value.values[1].value 392 self.assertEqual(type(call), ast.Call) 393 self.assertEqual(call.lineno, 3) 394 self.assertEqual(call.end_lineno, 3) 395 self.assertEqual(call.col_offset, 8) 396 self.assertEqual(call.end_col_offset, 15) 397 398 expr = """ 399x = ( 400 'PERL_MM_OPT', ( 401 f'wat' 402 f'some_string={f(x)} ' 403 f'wat' 404 ), 405) 406""" 407 t = ast.parse(expr) 408 self.assertEqual(type(t), ast.Module) 409 self.assertEqual(len(t.body), 1) 410 # check the fstring 411 fstring = t.body[0].value.elts[1] 412 self.assertEqual(type(fstring), ast.JoinedStr) 413 self.assertEqual(len(fstring.values), 3) 414 wat1, middle, wat2 = fstring.values 415 # check the first wat 416 self.assertEqual(type(wat1), ast.Constant) 417 self.assertEqual(wat1.lineno, 4) 418 self.assertEqual(wat1.end_lineno, 6) 419 self.assertEqual(wat1.col_offset, 12) 420 self.assertEqual(wat1.end_col_offset, 18) 421 # check the call 422 call = middle.value 423 self.assertEqual(type(call), ast.Call) 424 self.assertEqual(call.lineno, 5) 425 self.assertEqual(call.end_lineno, 5) 426 self.assertEqual(call.col_offset, 27) 427 self.assertEqual(call.end_col_offset, 31) 428 # check the second wat 429 self.assertEqual(type(wat2), ast.Constant) 430 self.assertEqual(wat2.lineno, 4) 431 self.assertEqual(wat2.end_lineno, 6) 432 self.assertEqual(wat2.col_offset, 12) 433 self.assertEqual(wat2.end_col_offset, 18) 434 435 def test_docstring(self): 436 def f(): 437 f'''Not a docstring''' 438 self.assertIsNone(f.__doc__) 439 def g(): 440 '''Not a docstring''' \ 441 f'' 442 self.assertIsNone(g.__doc__) 443 444 def test_literal_eval(self): 445 with self.assertRaisesRegex(ValueError, 'malformed node or string'): 446 ast.literal_eval("f'x'") 447 448 def test_ast_compile_time_concat(self): 449 x = [''] 450 451 expr = """x[0] = 'foo' f'{3}'""" 452 t = ast.parse(expr) 453 c = compile(t, '', 'exec') 454 exec(c) 455 self.assertEqual(x[0], 'foo3') 456 457 def test_compile_time_concat_errors(self): 458 self.assertAllRaise(SyntaxError, 459 'cannot mix bytes and nonbytes literals', 460 [r"""f'' b''""", 461 r"""b'' f''""", 462 ]) 463 464 def test_literal(self): 465 self.assertEqual(f'', '') 466 self.assertEqual(f'a', 'a') 467 self.assertEqual(f' ', ' ') 468 469 def test_unterminated_string(self): 470 self.assertAllRaise(SyntaxError, 'f-string: unterminated string', 471 [r"""f'{"x'""", 472 r"""f'{"x}'""", 473 r"""f'{("x'""", 474 r"""f'{("x}'""", 475 ]) 476 477 def test_mismatched_parens(self): 478 self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\}' " 479 r"does not match opening parenthesis '\('", 480 ["f'{((}'", 481 ]) 482 self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\)' " 483 r"does not match opening parenthesis '\['", 484 ["f'{a[4)}'", 485 ]) 486 self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\]' " 487 r"does not match opening parenthesis '\('", 488 ["f'{a(4]}'", 489 ]) 490 self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\}' " 491 r"does not match opening parenthesis '\['", 492 ["f'{a[4}'", 493 ]) 494 self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\}' " 495 r"does not match opening parenthesis '\('", 496 ["f'{a(4}'", 497 ]) 498 self.assertRaises(SyntaxError, eval, "f'{" + "("*500 + "}'") 499 500 def test_double_braces(self): 501 self.assertEqual(f'{{', '{') 502 self.assertEqual(f'a{{', 'a{') 503 self.assertEqual(f'{{b', '{b') 504 self.assertEqual(f'a{{b', 'a{b') 505 self.assertEqual(f'}}', '}') 506 self.assertEqual(f'a}}', 'a}') 507 self.assertEqual(f'}}b', '}b') 508 self.assertEqual(f'a}}b', 'a}b') 509 self.assertEqual(f'{{}}', '{}') 510 self.assertEqual(f'a{{}}', 'a{}') 511 self.assertEqual(f'{{b}}', '{b}') 512 self.assertEqual(f'{{}}c', '{}c') 513 self.assertEqual(f'a{{b}}', 'a{b}') 514 self.assertEqual(f'a{{}}c', 'a{}c') 515 self.assertEqual(f'{{b}}c', '{b}c') 516 self.assertEqual(f'a{{b}}c', 'a{b}c') 517 518 self.assertEqual(f'{{{10}', '{10') 519 self.assertEqual(f'}}{10}', '}10') 520 self.assertEqual(f'}}{{{10}', '}{10') 521 self.assertEqual(f'}}a{{{10}', '}a{10') 522 523 self.assertEqual(f'{10}{{', '10{') 524 self.assertEqual(f'{10}}}', '10}') 525 self.assertEqual(f'{10}}}{{', '10}{') 526 self.assertEqual(f'{10}}}a{{' '}', '10}a{}') 527 528 # Inside of strings, don't interpret doubled brackets. 529 self.assertEqual(f'{"{{}}"}', '{{}}') 530 531 self.assertAllRaise(TypeError, 'unhashable type', 532 ["f'{ {{}} }'", # dict in a set 533 ]) 534 535 def test_compile_time_concat(self): 536 x = 'def' 537 self.assertEqual('abc' f'## {x}ghi', 'abc## defghi') 538 self.assertEqual('abc' f'{x}' 'ghi', 'abcdefghi') 539 self.assertEqual('abc' f'{x}' 'gh' f'i{x:4}', 'abcdefghidef ') 540 self.assertEqual('{x}' f'{x}', '{x}def') 541 self.assertEqual('{x' f'{x}', '{xdef') 542 self.assertEqual('{x}' f'{x}', '{x}def') 543 self.assertEqual('{{x}}' f'{x}', '{{x}}def') 544 self.assertEqual('{{x' f'{x}', '{{xdef') 545 self.assertEqual('x}}' f'{x}', 'x}}def') 546 self.assertEqual(f'{x}' 'x}}', 'defx}}') 547 self.assertEqual(f'{x}' '', 'def') 548 self.assertEqual('' f'{x}' '', 'def') 549 self.assertEqual('' f'{x}', 'def') 550 self.assertEqual(f'{x}' '2', 'def2') 551 self.assertEqual('1' f'{x}' '2', '1def2') 552 self.assertEqual('1' f'{x}', '1def') 553 self.assertEqual(f'{x}' f'-{x}', 'def-def') 554 self.assertEqual('' f'', '') 555 self.assertEqual('' f'' '', '') 556 self.assertEqual('' f'' '' f'', '') 557 self.assertEqual(f'', '') 558 self.assertEqual(f'' '', '') 559 self.assertEqual(f'' '' f'', '') 560 self.assertEqual(f'' '' f'' '', '') 561 562 self.assertAllRaise(SyntaxError, "f-string: expecting '}'", 563 ["f'{3' f'}'", # can't concat to get a valid f-string 564 ]) 565 566 def test_comments(self): 567 # These aren't comments, since they're in strings. 568 d = {'#': 'hash'} 569 self.assertEqual(f'{"#"}', '#') 570 self.assertEqual(f'{d["#"]}', 'hash') 571 572 self.assertAllRaise(SyntaxError, "f-string expression part cannot include '#'", 573 ["f'{1#}'", # error because the expression becomes "(1#)" 574 "f'{3(#)}'", 575 "f'{#}'", 576 ]) 577 self.assertAllRaise(SyntaxError, r"f-string: unmatched '\)'", 578 ["f'{)#}'", # When wrapped in parens, this becomes 579 # '()#)'. Make sure that doesn't compile. 580 ]) 581 582 def test_many_expressions(self): 583 # Create a string with many expressions in it. Note that 584 # because we have a space in here as a literal, we're actually 585 # going to use twice as many ast nodes: one for each literal 586 # plus one for each expression. 587 def build_fstr(n, extra=''): 588 return "f'" + ('{x} ' * n) + extra + "'" 589 590 x = 'X' 591 width = 1 592 593 # Test around 256. 594 for i in range(250, 260): 595 self.assertEqual(eval(build_fstr(i)), (x+' ')*i) 596 597 # Test concatenating 2 largs fstrings. 598 self.assertEqual(eval(build_fstr(255)*256), (x+' ')*(255*256)) 599 600 s = build_fstr(253, '{x:{width}} ') 601 self.assertEqual(eval(s), (x+' ')*254) 602 603 # Test lots of expressions and constants, concatenated. 604 s = "f'{1}' 'x' 'y'" * 1024 605 self.assertEqual(eval(s), '1xy' * 1024) 606 607 def test_format_specifier_expressions(self): 608 width = 10 609 precision = 4 610 value = decimal.Decimal('12.34567') 611 self.assertEqual(f'result: {value:{width}.{precision}}', 'result: 12.35') 612 self.assertEqual(f'result: {value:{width!r}.{precision}}', 'result: 12.35') 613 self.assertEqual(f'result: {value:{width:0}.{precision:1}}', 'result: 12.35') 614 self.assertEqual(f'result: {value:{1}{0:0}.{precision:1}}', 'result: 12.35') 615 self.assertEqual(f'result: {value:{ 1}{ 0:0}.{ precision:1}}', 'result: 12.35') 616 self.assertEqual(f'{10:#{1}0x}', ' 0xa') 617 self.assertEqual(f'{10:{"#"}1{0}{"x"}}', ' 0xa') 618 self.assertEqual(f'{-10:-{"#"}1{0}x}', ' -0xa') 619 self.assertEqual(f'{-10:{"-"}#{1}0{"x"}}', ' -0xa') 620 self.assertEqual(f'{10:#{3 != {4:5} and width}x}', ' 0xa') 621 622 self.assertAllRaise(SyntaxError, "f-string: expecting '}'", 623 ["""f'{"s"!r{":10"}}'""", 624 625 # This looks like a nested format spec. 626 ]) 627 628 self.assertAllRaise(SyntaxError, "f-string: invalid syntax", 629 [# Invalid syntax inside a nested spec. 630 "f'{4:{/5}}'", 631 ]) 632 633 self.assertAllRaise(SyntaxError, "f-string: expressions nested too deeply", 634 [# Can't nest format specifiers. 635 "f'result: {value:{width:{0}}.{precision:1}}'", 636 ]) 637 638 self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character', 639 [# No expansion inside conversion or for 640 # the : or ! itself. 641 """f'{"s"!{"r"}}'""", 642 ]) 643 644 def test_side_effect_order(self): 645 class X: 646 def __init__(self): 647 self.i = 0 648 def __format__(self, spec): 649 self.i += 1 650 return str(self.i) 651 652 x = X() 653 self.assertEqual(f'{x} {x}', '1 2') 654 655 def test_missing_expression(self): 656 self.assertAllRaise(SyntaxError, 'f-string: empty expression not allowed', 657 ["f'{}'", 658 "f'{ }'" 659 "f' {} '", 660 "f'{10:{ }}'", 661 "f' { } '", 662 663 # The Python parser ignores also the following 664 # whitespace characters in additional to a space. 665 "f'''{\t\f\r\n}'''", 666 ]) 667 668 # Different error messeges are raised when a specfier ('!', ':' or '=') is used after an empty expression 669 self.assertAllRaise(SyntaxError, "f-string: expression required before '!'", 670 ["f'{!r}'", 671 "f'{ !r}'", 672 "f'{!}'", 673 "f'''{\t\f\r\n!a}'''", 674 675 # Catch empty expression before the 676 # missing closing brace. 677 "f'{!'", 678 "f'{!s:'", 679 680 # Catch empty expression before the 681 # invalid conversion. 682 "f'{!x}'", 683 "f'{ !xr}'", 684 "f'{!x:}'", 685 "f'{!x:a}'", 686 "f'{ !xr:}'", 687 "f'{ !xr:a}'", 688 ]) 689 690 self.assertAllRaise(SyntaxError, "f-string: expression required before ':'", 691 ["f'{:}'", 692 "f'{ :!}'", 693 "f'{:2}'", 694 "f'''{\t\f\r\n:a}'''", 695 "f'{:'", 696 ]) 697 698 self.assertAllRaise(SyntaxError, "f-string: expression required before '='", 699 ["f'{=}'", 700 "f'{ =}'", 701 "f'{ =:}'", 702 "f'{ =!}'", 703 "f'''{\t\f\r\n=}'''", 704 "f'{='", 705 ]) 706 707 # Different error message is raised for other whitespace characters. 708 self.assertAllRaise(SyntaxError, r"invalid non-printable character U\+00A0", 709 ["f'''{\xa0}'''", 710 "\xa0", 711 ]) 712 713 def test_parens_in_expressions(self): 714 self.assertEqual(f'{3,}', '(3,)') 715 716 # Add these because when an expression is evaluated, parens 717 # are added around it. But we shouldn't go from an invalid 718 # expression to a valid one. The added parens are just 719 # supposed to allow whitespace (including newlines). 720 self.assertAllRaise(SyntaxError, 'f-string: invalid syntax', 721 ["f'{,}'", 722 "f'{,}'", # this is (,), which is an error 723 ]) 724 725 self.assertAllRaise(SyntaxError, r"f-string: unmatched '\)'", 726 ["f'{3)+(4}'", 727 ]) 728 729 self.assertAllRaise(SyntaxError, 'unterminated string literal', 730 ["f'{\n}'", 731 ]) 732 def test_newlines_before_syntax_error(self): 733 self.assertAllRaise(SyntaxError, "invalid syntax", 734 ["f'{.}'", "\nf'{.}'", "\n\nf'{.}'"]) 735 736 def test_backslashes_in_string_part(self): 737 self.assertEqual(f'\t', '\t') 738 self.assertEqual(r'\t', '\\t') 739 self.assertEqual(rf'\t', '\\t') 740 self.assertEqual(f'{2}\t', '2\t') 741 self.assertEqual(f'{2}\t{3}', '2\t3') 742 self.assertEqual(f'\t{3}', '\t3') 743 744 self.assertEqual(f'\u0394', '\u0394') 745 self.assertEqual(r'\u0394', '\\u0394') 746 self.assertEqual(rf'\u0394', '\\u0394') 747 self.assertEqual(f'{2}\u0394', '2\u0394') 748 self.assertEqual(f'{2}\u0394{3}', '2\u03943') 749 self.assertEqual(f'\u0394{3}', '\u03943') 750 751 self.assertEqual(f'\U00000394', '\u0394') 752 self.assertEqual(r'\U00000394', '\\U00000394') 753 self.assertEqual(rf'\U00000394', '\\U00000394') 754 self.assertEqual(f'{2}\U00000394', '2\u0394') 755 self.assertEqual(f'{2}\U00000394{3}', '2\u03943') 756 self.assertEqual(f'\U00000394{3}', '\u03943') 757 758 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}', '\u0394') 759 self.assertEqual(f'{2}\N{GREEK CAPITAL LETTER DELTA}', '2\u0394') 760 self.assertEqual(f'{2}\N{GREEK CAPITAL LETTER DELTA}{3}', '2\u03943') 761 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}{3}', '\u03943') 762 self.assertEqual(f'2\N{GREEK CAPITAL LETTER DELTA}', '2\u0394') 763 self.assertEqual(f'2\N{GREEK CAPITAL LETTER DELTA}3', '2\u03943') 764 self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}3', '\u03943') 765 766 self.assertEqual(f'\x20', ' ') 767 self.assertEqual(r'\x20', '\\x20') 768 self.assertEqual(rf'\x20', '\\x20') 769 self.assertEqual(f'{2}\x20', '2 ') 770 self.assertEqual(f'{2}\x20{3}', '2 3') 771 self.assertEqual(f'\x20{3}', ' 3') 772 773 self.assertEqual(f'2\x20', '2 ') 774 self.assertEqual(f'2\x203', '2 3') 775 self.assertEqual(f'\x203', ' 3') 776 777 with self.assertWarns(DeprecationWarning): # invalid escape sequence 778 value = eval(r"f'\{6*7}'") 779 self.assertEqual(value, '\\42') 780 self.assertEqual(f'\\{6*7}', '\\42') 781 self.assertEqual(fr'\{6*7}', '\\42') 782 783 AMPERSAND = 'spam' 784 # Get the right unicode character (&), or pick up local variable 785 # depending on the number of backslashes. 786 self.assertEqual(f'\N{AMPERSAND}', '&') 787 self.assertEqual(f'\\N{AMPERSAND}', '\\Nspam') 788 self.assertEqual(fr'\N{AMPERSAND}', '\\Nspam') 789 self.assertEqual(f'\\\N{AMPERSAND}', '\\&') 790 791 def test_misformed_unicode_character_name(self): 792 # These test are needed because unicode names are parsed 793 # differently inside f-strings. 794 self.assertAllRaise(SyntaxError, r"\(unicode error\) 'unicodeescape' codec can't decode bytes in position .*: malformed \\N character escape", 795 [r"f'\N'", 796 r"f'\N '", 797 r"f'\N '", # See bpo-46503. 798 r"f'\N{'", 799 r"f'\N{GREEK CAPITAL LETTER DELTA'", 800 801 # Here are the non-f-string versions, 802 # which should give the same errors. 803 r"'\N'", 804 r"'\N '", 805 r"'\N '", 806 r"'\N{'", 807 r"'\N{GREEK CAPITAL LETTER DELTA'", 808 ]) 809 810 def test_no_backslashes_in_expression_part(self): 811 self.assertAllRaise(SyntaxError, 'f-string expression part cannot include a backslash', 812 [r"f'{\'a\'}'", 813 r"f'{\t3}'", 814 r"f'{\}'", 815 r"rf'{\'a\'}'", 816 r"rf'{\t3}'", 817 r"rf'{\}'", 818 r"""rf'{"\N{LEFT CURLY BRACKET}"}'""", 819 r"f'{\n}'", 820 ]) 821 822 def test_no_escapes_for_braces(self): 823 """ 824 Only literal curly braces begin an expression. 825 """ 826 # \x7b is '{'. 827 self.assertEqual(f'\x7b1+1}}', '{1+1}') 828 self.assertEqual(f'\x7b1+1', '{1+1') 829 self.assertEqual(f'\u007b1+1', '{1+1') 830 self.assertEqual(f'\N{LEFT CURLY BRACKET}1+1\N{RIGHT CURLY BRACKET}', '{1+1}') 831 832 def test_newlines_in_expressions(self): 833 self.assertEqual(f'{0}', '0') 834 self.assertEqual(rf'''{3+ 8354}''', '7') 836 837 def test_lambda(self): 838 x = 5 839 self.assertEqual(f'{(lambda y:x*y)("8")!r}', "'88888'") 840 self.assertEqual(f'{(lambda y:x*y)("8")!r:10}', "'88888' ") 841 self.assertEqual(f'{(lambda y:x*y)("8"):10}', "88888 ") 842 843 # lambda doesn't work without parens, because the colon 844 # makes the parser think it's a format_spec 845 self.assertAllRaise(SyntaxError, 'f-string: invalid syntax', 846 ["f'{lambda x:x}'", 847 ]) 848 849 def test_yield(self): 850 # Not terribly useful, but make sure the yield turns 851 # a function into a generator 852 def fn(y): 853 f'y:{yield y*2}' 854 f'{yield}' 855 856 g = fn(4) 857 self.assertEqual(next(g), 8) 858 self.assertEqual(next(g), None) 859 860 def test_yield_send(self): 861 def fn(x): 862 yield f'x:{yield (lambda i: x * i)}' 863 864 g = fn(10) 865 the_lambda = next(g) 866 self.assertEqual(the_lambda(4), 40) 867 self.assertEqual(g.send('string'), 'x:string') 868 869 def test_expressions_with_triple_quoted_strings(self): 870 self.assertEqual(f"{'''x'''}", 'x') 871 self.assertEqual(f"{'''eric's'''}", "eric's") 872 873 # Test concatenation within an expression 874 self.assertEqual(f'{"x" """eric"s""" "y"}', 'xeric"sy') 875 self.assertEqual(f'{"x" """eric"s"""}', 'xeric"s') 876 self.assertEqual(f'{"""eric"s""" "y"}', 'eric"sy') 877 self.assertEqual(f'{"""x""" """eric"s""" "y"}', 'xeric"sy') 878 self.assertEqual(f'{"""x""" """eric"s""" """y"""}', 'xeric"sy') 879 self.assertEqual(f'{r"""x""" """eric"s""" """y"""}', 'xeric"sy') 880 881 def test_multiple_vars(self): 882 x = 98 883 y = 'abc' 884 self.assertEqual(f'{x}{y}', '98abc') 885 886 self.assertEqual(f'X{x}{y}', 'X98abc') 887 self.assertEqual(f'{x}X{y}', '98Xabc') 888 self.assertEqual(f'{x}{y}X', '98abcX') 889 890 self.assertEqual(f'X{x}Y{y}', 'X98Yabc') 891 self.assertEqual(f'X{x}{y}Y', 'X98abcY') 892 self.assertEqual(f'{x}X{y}Y', '98XabcY') 893 894 self.assertEqual(f'X{x}Y{y}Z', 'X98YabcZ') 895 896 def test_closure(self): 897 def outer(x): 898 def inner(): 899 return f'x:{x}' 900 return inner 901 902 self.assertEqual(outer('987')(), 'x:987') 903 self.assertEqual(outer(7)(), 'x:7') 904 905 def test_arguments(self): 906 y = 2 907 def f(x, width): 908 return f'x={x*y:{width}}' 909 910 self.assertEqual(f('foo', 10), 'x=foofoo ') 911 x = 'bar' 912 self.assertEqual(f(10, 10), 'x= 20') 913 914 def test_locals(self): 915 value = 123 916 self.assertEqual(f'v:{value}', 'v:123') 917 918 def test_missing_variable(self): 919 with self.assertRaises(NameError): 920 f'v:{value}' 921 922 def test_missing_format_spec(self): 923 class O: 924 def __format__(self, spec): 925 if not spec: 926 return '*' 927 return spec 928 929 self.assertEqual(f'{O():x}', 'x') 930 self.assertEqual(f'{O()}', '*') 931 self.assertEqual(f'{O():}', '*') 932 933 self.assertEqual(f'{3:}', '3') 934 self.assertEqual(f'{3!s:}', '3') 935 936 def test_global(self): 937 self.assertEqual(f'g:{a_global}', 'g:global variable') 938 self.assertEqual(f'g:{a_global!r}', "g:'global variable'") 939 940 a_local = 'local variable' 941 self.assertEqual(f'g:{a_global} l:{a_local}', 942 'g:global variable l:local variable') 943 self.assertEqual(f'g:{a_global!r}', 944 "g:'global variable'") 945 self.assertEqual(f'g:{a_global} l:{a_local!r}', 946 "g:global variable l:'local variable'") 947 948 self.assertIn("module 'unittest' from", f'{unittest}') 949 950 def test_shadowed_global(self): 951 a_global = 'really a local' 952 self.assertEqual(f'g:{a_global}', 'g:really a local') 953 self.assertEqual(f'g:{a_global!r}', "g:'really a local'") 954 955 a_local = 'local variable' 956 self.assertEqual(f'g:{a_global} l:{a_local}', 957 'g:really a local l:local variable') 958 self.assertEqual(f'g:{a_global!r}', 959 "g:'really a local'") 960 self.assertEqual(f'g:{a_global} l:{a_local!r}', 961 "g:really a local l:'local variable'") 962 963 def test_call(self): 964 def foo(x): 965 return 'x=' + str(x) 966 967 self.assertEqual(f'{foo(10)}', 'x=10') 968 969 def test_nested_fstrings(self): 970 y = 5 971 self.assertEqual(f'{f"{0}"*3}', '000') 972 self.assertEqual(f'{f"{y}"*3}', '555') 973 974 def test_invalid_string_prefixes(self): 975 single_quote_cases = ["fu''", 976 "uf''", 977 "Fu''", 978 "fU''", 979 "Uf''", 980 "uF''", 981 "ufr''", 982 "urf''", 983 "fur''", 984 "fru''", 985 "rfu''", 986 "ruf''", 987 "FUR''", 988 "Fur''", 989 "fb''", 990 "fB''", 991 "Fb''", 992 "FB''", 993 "bf''", 994 "bF''", 995 "Bf''", 996 "BF''",] 997 double_quote_cases = [case.replace("'", '"') for case in single_quote_cases] 998 self.assertAllRaise(SyntaxError, 'invalid syntax', 999 single_quote_cases + double_quote_cases) 1000 1001 def test_leading_trailing_spaces(self): 1002 self.assertEqual(f'{ 3}', '3') 1003 self.assertEqual(f'{ 3}', '3') 1004 self.assertEqual(f'{3 }', '3') 1005 self.assertEqual(f'{3 }', '3') 1006 1007 self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]}}', 1008 'expr={1: 2}') 1009 self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]} }', 1010 'expr={1: 2}') 1011 1012 def test_not_equal(self): 1013 # There's a special test for this because there's a special 1014 # case in the f-string parser to look for != as not ending an 1015 # expression. Normally it would, while looking for !s or !r. 1016 1017 self.assertEqual(f'{3!=4}', 'True') 1018 self.assertEqual(f'{3!=4:}', 'True') 1019 self.assertEqual(f'{3!=4!s}', 'True') 1020 self.assertEqual(f'{3!=4!s:.3}', 'Tru') 1021 1022 def test_equal_equal(self): 1023 # Because an expression ending in = has special meaning, 1024 # there's a special test for ==. Make sure it works. 1025 1026 self.assertEqual(f'{0==1}', 'False') 1027 1028 def test_conversions(self): 1029 self.assertEqual(f'{3.14:10.10}', ' 3.14') 1030 self.assertEqual(f'{3.14!s:10.10}', '3.14 ') 1031 self.assertEqual(f'{3.14!r:10.10}', '3.14 ') 1032 self.assertEqual(f'{3.14!a:10.10}', '3.14 ') 1033 1034 self.assertEqual(f'{"a"}', 'a') 1035 self.assertEqual(f'{"a"!r}', "'a'") 1036 self.assertEqual(f'{"a"!a}', "'a'") 1037 1038 # Not a conversion. 1039 self.assertEqual(f'{"a!r"}', "a!r") 1040 1041 # Not a conversion, but show that ! is allowed in a format spec. 1042 self.assertEqual(f'{3.14:!<10.10}', '3.14!!!!!!') 1043 1044 self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character', 1045 ["f'{3!g}'", 1046 "f'{3!A}'", 1047 "f'{3!3}'", 1048 "f'{3!G}'", 1049 "f'{3!!}'", 1050 "f'{3!:}'", 1051 "f'{3! s}'", # no space before conversion char 1052 ]) 1053 1054 self.assertAllRaise(SyntaxError, "f-string: expecting '}'", 1055 ["f'{x!s{y}}'", 1056 "f'{3!ss}'", 1057 "f'{3!ss:}'", 1058 "f'{3!ss:s}'", 1059 ]) 1060 1061 def test_assignment(self): 1062 self.assertAllRaise(SyntaxError, r'invalid syntax', 1063 ["f'' = 3", 1064 "f'{0}' = x", 1065 "f'{x}' = x", 1066 ]) 1067 1068 def test_del(self): 1069 self.assertAllRaise(SyntaxError, 'invalid syntax', 1070 ["del f''", 1071 "del '' f''", 1072 ]) 1073 1074 def test_mismatched_braces(self): 1075 self.assertAllRaise(SyntaxError, "f-string: single '}' is not allowed", 1076 ["f'{{}'", 1077 "f'{{}}}'", 1078 "f'}'", 1079 "f'x}'", 1080 "f'x}x'", 1081 r"f'\u007b}'", 1082 1083 # Can't have { or } in a format spec. 1084 "f'{3:}>10}'", 1085 "f'{3:}}>10}'", 1086 ]) 1087 1088 self.assertAllRaise(SyntaxError, "f-string: expecting '}'", 1089 ["f'{3:{{>10}'", 1090 "f'{3'", 1091 "f'{3!'", 1092 "f'{3:'", 1093 "f'{3!s'", 1094 "f'{3!s:'", 1095 "f'{3!s:3'", 1096 "f'x{'", 1097 "f'x{x'", 1098 "f'{x'", 1099 "f'{3:s'", 1100 "f'{{{'", 1101 "f'{{}}{'", 1102 "f'{'", 1103 "f'x{<'", # See bpo-46762. 1104 "f'x{>'", 1105 "f'{i='", # See gh-93418. 1106 ]) 1107 1108 # But these are just normal strings. 1109 self.assertEqual(f'{"{"}', '{') 1110 self.assertEqual(f'{"}"}', '}') 1111 self.assertEqual(f'{3:{"}"}>10}', '}}}}}}}}}3') 1112 self.assertEqual(f'{2:{"{"}>10}', '{{{{{{{{{2') 1113 1114 def test_if_conditional(self): 1115 # There's special logic in compile.c to test if the 1116 # conditional for an if (and while) are constants. Exercise 1117 # that code. 1118 1119 def test_fstring(x, expected): 1120 flag = 0 1121 if f'{x}': 1122 flag = 1 1123 else: 1124 flag = 2 1125 self.assertEqual(flag, expected) 1126 1127 def test_concat_empty(x, expected): 1128 flag = 0 1129 if '' f'{x}': 1130 flag = 1 1131 else: 1132 flag = 2 1133 self.assertEqual(flag, expected) 1134 1135 def test_concat_non_empty(x, expected): 1136 flag = 0 1137 if ' ' f'{x}': 1138 flag = 1 1139 else: 1140 flag = 2 1141 self.assertEqual(flag, expected) 1142 1143 test_fstring('', 2) 1144 test_fstring(' ', 1) 1145 1146 test_concat_empty('', 2) 1147 test_concat_empty(' ', 1) 1148 1149 test_concat_non_empty('', 1) 1150 test_concat_non_empty(' ', 1) 1151 1152 def test_empty_format_specifier(self): 1153 x = 'test' 1154 self.assertEqual(f'{x}', 'test') 1155 self.assertEqual(f'{x:}', 'test') 1156 self.assertEqual(f'{x!s:}', 'test') 1157 self.assertEqual(f'{x!r:}', "'test'") 1158 1159 def test_str_format_differences(self): 1160 d = {'a': 'string', 1161 0: 'integer', 1162 } 1163 a = 0 1164 self.assertEqual(f'{d[0]}', 'integer') 1165 self.assertEqual(f'{d["a"]}', 'string') 1166 self.assertEqual(f'{d[a]}', 'integer') 1167 self.assertEqual('{d[a]}'.format(d=d), 'string') 1168 self.assertEqual('{d[0]}'.format(d=d), 'integer') 1169 1170 def test_errors(self): 1171 # see issue 26287 1172 self.assertAllRaise(TypeError, 'unsupported', 1173 [r"f'{(lambda: 0):x}'", 1174 r"f'{(0,):x}'", 1175 ]) 1176 self.assertAllRaise(ValueError, 'Unknown format code', 1177 [r"f'{1000:j}'", 1178 r"f'{1000:j}'", 1179 ]) 1180 1181 def test_filename_in_syntaxerror(self): 1182 # see issue 38964 1183 with temp_cwd() as cwd: 1184 file_path = os.path.join(cwd, 't.py') 1185 with open(file_path, 'w', encoding="utf-8") as f: 1186 f.write('f"{a b}"') # This generates a SyntaxError 1187 _, _, stderr = assert_python_failure(file_path, 1188 PYTHONIOENCODING='ascii') 1189 self.assertIn(file_path.encode('ascii', 'backslashreplace'), stderr) 1190 1191 def test_loop(self): 1192 for i in range(1000): 1193 self.assertEqual(f'i:{i}', 'i:' + str(i)) 1194 1195 def test_dict(self): 1196 d = {'"': 'dquote', 1197 "'": 'squote', 1198 'foo': 'bar', 1199 } 1200 self.assertEqual(f'''{d["'"]}''', 'squote') 1201 self.assertEqual(f"""{d['"']}""", 'dquote') 1202 1203 self.assertEqual(f'{d["foo"]}', 'bar') 1204 self.assertEqual(f"{d['foo']}", 'bar') 1205 1206 def test_backslash_char(self): 1207 # Check eval of a backslash followed by a control char. 1208 # See bpo-30682: this used to raise an assert in pydebug mode. 1209 self.assertEqual(eval('f"\\\n"'), '') 1210 self.assertEqual(eval('f"\\\r"'), '') 1211 1212 def test_debug_conversion(self): 1213 x = 'A string' 1214 self.assertEqual(f'{x=}', 'x=' + repr(x)) 1215 self.assertEqual(f'{x =}', 'x =' + repr(x)) 1216 self.assertEqual(f'{x=!s}', 'x=' + str(x)) 1217 self.assertEqual(f'{x=!r}', 'x=' + repr(x)) 1218 self.assertEqual(f'{x=!a}', 'x=' + ascii(x)) 1219 1220 x = 2.71828 1221 self.assertEqual(f'{x=:.2f}', 'x=' + format(x, '.2f')) 1222 self.assertEqual(f'{x=:}', 'x=' + format(x, '')) 1223 self.assertEqual(f'{x=!r:^20}', 'x=' + format(repr(x), '^20')) 1224 self.assertEqual(f'{x=!s:^20}', 'x=' + format(str(x), '^20')) 1225 self.assertEqual(f'{x=!a:^20}', 'x=' + format(ascii(x), '^20')) 1226 1227 x = 9 1228 self.assertEqual(f'{3*x+15=}', '3*x+15=42') 1229 1230 # There is code in ast.c that deals with non-ascii expression values. So, 1231 # use a unicode identifier to trigger that. 1232 tenπ = 31.4 1233 self.assertEqual(f'{tenπ=:.2f}', 'tenπ=31.40') 1234 1235 # Also test with Unicode in non-identifiers. 1236 self.assertEqual(f'{"Σ"=}', '"Σ"=\'Σ\'') 1237 1238 # Make sure nested fstrings still work. 1239 self.assertEqual(f'{f"{3.1415=:.1f}":*^20}', '*****3.1415=3.1*****') 1240 1241 # Make sure text before and after an expression with = works 1242 # correctly. 1243 pi = 'π' 1244 self.assertEqual(f'alpha α {pi=} ω omega', "alpha α pi='π' ω omega") 1245 1246 # Check multi-line expressions. 1247 self.assertEqual(f'''{ 12483 1249=}''', '\n3\n=3') 1250 1251 # Since = is handled specially, make sure all existing uses of 1252 # it still work. 1253 1254 self.assertEqual(f'{0==1}', 'False') 1255 self.assertEqual(f'{0!=1}', 'True') 1256 self.assertEqual(f'{0<=1}', 'True') 1257 self.assertEqual(f'{0>=1}', 'False') 1258 self.assertEqual(f'{(x:="5")}', '5') 1259 self.assertEqual(x, '5') 1260 self.assertEqual(f'{(x:=5)}', '5') 1261 self.assertEqual(x, 5) 1262 self.assertEqual(f'{"="}', '=') 1263 1264 x = 20 1265 # This isn't an assignment expression, it's 'x', with a format 1266 # spec of '=10'. See test_walrus: you need to use parens. 1267 self.assertEqual(f'{x:=10}', ' 20') 1268 1269 # Test named function parameters, to make sure '=' parsing works 1270 # there. 1271 def f(a): 1272 nonlocal x 1273 oldx = x 1274 x = a 1275 return oldx 1276 x = 0 1277 self.assertEqual(f'{f(a="3=")}', '0') 1278 self.assertEqual(x, '3=') 1279 self.assertEqual(f'{f(a=4)}', '3=') 1280 self.assertEqual(x, 4) 1281 1282 # Make sure __format__ is being called. 1283 class C: 1284 def __format__(self, s): 1285 return f'FORMAT-{s}' 1286 def __repr__(self): 1287 return 'REPR' 1288 1289 self.assertEqual(f'{C()=}', 'C()=REPR') 1290 self.assertEqual(f'{C()=!r}', 'C()=REPR') 1291 self.assertEqual(f'{C()=:}', 'C()=FORMAT-') 1292 self.assertEqual(f'{C()=: }', 'C()=FORMAT- ') 1293 self.assertEqual(f'{C()=:x}', 'C()=FORMAT-x') 1294 self.assertEqual(f'{C()=!r:*^20}', 'C()=********REPR********') 1295 1296 self.assertRaises(SyntaxError, eval, "f'{C=]'") 1297 1298 # Make sure leading and following text works. 1299 x = 'foo' 1300 self.assertEqual(f'X{x=}Y', 'Xx='+repr(x)+'Y') 1301 1302 # Make sure whitespace around the = works. 1303 self.assertEqual(f'X{x =}Y', 'Xx ='+repr(x)+'Y') 1304 self.assertEqual(f'X{x= }Y', 'Xx= '+repr(x)+'Y') 1305 self.assertEqual(f'X{x = }Y', 'Xx = '+repr(x)+'Y') 1306 1307 # These next lines contains tabs. Backslash escapes don't 1308 # work in f-strings. 1309 # patchcheck doesn't like these tabs. So the only way to test 1310 # this will be to dynamically created and exec the f-strings. But 1311 # that's such a hassle I'll save it for another day. For now, convert 1312 # the tabs to spaces just to shut up patchcheck. 1313 #self.assertEqual(f'X{x =}Y', 'Xx\t='+repr(x)+'Y') 1314 #self.assertEqual(f'X{x = }Y', 'Xx\t=\t'+repr(x)+'Y') 1315 1316 def test_walrus(self): 1317 x = 20 1318 # This isn't an assignment expression, it's 'x', with a format 1319 # spec of '=10'. 1320 self.assertEqual(f'{x:=10}', ' 20') 1321 1322 # This is an assignment expression, which requires parens. 1323 self.assertEqual(f'{(x:=10)}', '10') 1324 self.assertEqual(x, 10) 1325 1326 def test_invalid_syntax_error_message(self): 1327 with self.assertRaisesRegex(SyntaxError, "f-string: invalid syntax"): 1328 compile("f'{a $ b}'", "?", "exec") 1329 1330 def test_with_two_commas_in_format_specifier(self): 1331 error_msg = re.escape("Cannot specify ',' with ','.") 1332 with self.assertRaisesRegex(ValueError, error_msg): 1333 f'{1:,,}' 1334 1335 def test_with_two_underscore_in_format_specifier(self): 1336 error_msg = re.escape("Cannot specify '_' with '_'.") 1337 with self.assertRaisesRegex(ValueError, error_msg): 1338 f'{1:__}' 1339 1340 def test_with_a_commas_and_an_underscore_in_format_specifier(self): 1341 error_msg = re.escape("Cannot specify both ',' and '_'.") 1342 with self.assertRaisesRegex(ValueError, error_msg): 1343 f'{1:,_}' 1344 1345 def test_with_an_underscore_and_a_comma_in_format_specifier(self): 1346 error_msg = re.escape("Cannot specify both ',' and '_'.") 1347 with self.assertRaisesRegex(ValueError, error_msg): 1348 f'{1:_,}' 1349 1350 def test_syntax_error_for_starred_expressions(self): 1351 error_msg = re.escape("cannot use starred expression here") 1352 with self.assertRaisesRegex(SyntaxError, error_msg): 1353 compile("f'{*a}'", "?", "exec") 1354 1355 error_msg = re.escape("cannot use double starred expression here") 1356 with self.assertRaisesRegex(SyntaxError, error_msg): 1357 compile("f'{**a}'", "?", "exec") 1358 1359if __name__ == '__main__': 1360 unittest.main() 1361