17db96d56Sopenharmony_ci# -*- coding: utf-8 -*- 27db96d56Sopenharmony_ci# There are tests here with unicode string literals and 37db96d56Sopenharmony_ci# identifiers. There's a code in ast.c that was added because of a 47db96d56Sopenharmony_ci# failure with a non-ascii-only expression. So, I have tests for 57db96d56Sopenharmony_ci# that. There are workarounds that would let me run tests for that 67db96d56Sopenharmony_ci# code without unicode identifiers and strings, but just using them 77db96d56Sopenharmony_ci# directly seems like the easiest and therefore safest thing to do. 87db96d56Sopenharmony_ci# Unicode identifiers in tests is allowed by PEP 3131. 97db96d56Sopenharmony_ci 107db96d56Sopenharmony_ciimport ast 117db96d56Sopenharmony_ciimport os 127db96d56Sopenharmony_ciimport re 137db96d56Sopenharmony_ciimport types 147db96d56Sopenharmony_ciimport decimal 157db96d56Sopenharmony_ciimport unittest 167db96d56Sopenharmony_cifrom test.support.os_helper import temp_cwd 177db96d56Sopenharmony_cifrom test.support.script_helper import assert_python_failure 187db96d56Sopenharmony_ci 197db96d56Sopenharmony_cia_global = 'global variable' 207db96d56Sopenharmony_ci 217db96d56Sopenharmony_ci# You could argue that I'm too strict in looking for specific error 227db96d56Sopenharmony_ci# values with assertRaisesRegex, but without it it's way too easy to 237db96d56Sopenharmony_ci# make a syntax error in the test strings. Especially with all of the 247db96d56Sopenharmony_ci# triple quotes, raw strings, backslashes, etc. I think it's a 257db96d56Sopenharmony_ci# worthwhile tradeoff. When I switched to this method, I found many 267db96d56Sopenharmony_ci# examples where I wasn't testing what I thought I was. 277db96d56Sopenharmony_ci 287db96d56Sopenharmony_ciclass TestCase(unittest.TestCase): 297db96d56Sopenharmony_ci def assertAllRaise(self, exception_type, regex, error_strings): 307db96d56Sopenharmony_ci for str in error_strings: 317db96d56Sopenharmony_ci with self.subTest(str=str): 327db96d56Sopenharmony_ci with self.assertRaisesRegex(exception_type, regex): 337db96d56Sopenharmony_ci eval(str) 347db96d56Sopenharmony_ci 357db96d56Sopenharmony_ci def test__format__lookup(self): 367db96d56Sopenharmony_ci # Make sure __format__ is looked up on the type, not the instance. 377db96d56Sopenharmony_ci class X: 387db96d56Sopenharmony_ci def __format__(self, spec): 397db96d56Sopenharmony_ci return 'class' 407db96d56Sopenharmony_ci 417db96d56Sopenharmony_ci x = X() 427db96d56Sopenharmony_ci 437db96d56Sopenharmony_ci # Add a bound __format__ method to the 'y' instance, but not 447db96d56Sopenharmony_ci # the 'x' instance. 457db96d56Sopenharmony_ci y = X() 467db96d56Sopenharmony_ci y.__format__ = types.MethodType(lambda self, spec: 'instance', y) 477db96d56Sopenharmony_ci 487db96d56Sopenharmony_ci self.assertEqual(f'{y}', format(y)) 497db96d56Sopenharmony_ci self.assertEqual(f'{y}', 'class') 507db96d56Sopenharmony_ci self.assertEqual(format(x), format(y)) 517db96d56Sopenharmony_ci 527db96d56Sopenharmony_ci # __format__ is not called this way, but still make sure it 537db96d56Sopenharmony_ci # returns what we expect (so we can make sure we're bypassing 547db96d56Sopenharmony_ci # it). 557db96d56Sopenharmony_ci self.assertEqual(x.__format__(''), 'class') 567db96d56Sopenharmony_ci self.assertEqual(y.__format__(''), 'instance') 577db96d56Sopenharmony_ci 587db96d56Sopenharmony_ci # This is how __format__ is actually called. 597db96d56Sopenharmony_ci self.assertEqual(type(x).__format__(x, ''), 'class') 607db96d56Sopenharmony_ci self.assertEqual(type(y).__format__(y, ''), 'class') 617db96d56Sopenharmony_ci 627db96d56Sopenharmony_ci def test_ast(self): 637db96d56Sopenharmony_ci # Inspired by http://bugs.python.org/issue24975 647db96d56Sopenharmony_ci class X: 657db96d56Sopenharmony_ci def __init__(self): 667db96d56Sopenharmony_ci self.called = False 677db96d56Sopenharmony_ci def __call__(self): 687db96d56Sopenharmony_ci self.called = True 697db96d56Sopenharmony_ci return 4 707db96d56Sopenharmony_ci x = X() 717db96d56Sopenharmony_ci expr = """ 727db96d56Sopenharmony_cia = 10 737db96d56Sopenharmony_cif'{a * x()}'""" 747db96d56Sopenharmony_ci t = ast.parse(expr) 757db96d56Sopenharmony_ci c = compile(t, '', 'exec') 767db96d56Sopenharmony_ci 777db96d56Sopenharmony_ci # Make sure x was not called. 787db96d56Sopenharmony_ci self.assertFalse(x.called) 797db96d56Sopenharmony_ci 807db96d56Sopenharmony_ci # Actually run the code. 817db96d56Sopenharmony_ci exec(c) 827db96d56Sopenharmony_ci 837db96d56Sopenharmony_ci # Make sure x was called. 847db96d56Sopenharmony_ci self.assertTrue(x.called) 857db96d56Sopenharmony_ci 867db96d56Sopenharmony_ci def test_ast_line_numbers(self): 877db96d56Sopenharmony_ci expr = """ 887db96d56Sopenharmony_cia = 10 897db96d56Sopenharmony_cif'{a * x()}'""" 907db96d56Sopenharmony_ci t = ast.parse(expr) 917db96d56Sopenharmony_ci self.assertEqual(type(t), ast.Module) 927db96d56Sopenharmony_ci self.assertEqual(len(t.body), 2) 937db96d56Sopenharmony_ci # check `a = 10` 947db96d56Sopenharmony_ci self.assertEqual(type(t.body[0]), ast.Assign) 957db96d56Sopenharmony_ci self.assertEqual(t.body[0].lineno, 2) 967db96d56Sopenharmony_ci # check `f'...'` 977db96d56Sopenharmony_ci self.assertEqual(type(t.body[1]), ast.Expr) 987db96d56Sopenharmony_ci self.assertEqual(type(t.body[1].value), ast.JoinedStr) 997db96d56Sopenharmony_ci self.assertEqual(len(t.body[1].value.values), 1) 1007db96d56Sopenharmony_ci self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue) 1017db96d56Sopenharmony_ci self.assertEqual(t.body[1].lineno, 3) 1027db96d56Sopenharmony_ci self.assertEqual(t.body[1].value.lineno, 3) 1037db96d56Sopenharmony_ci self.assertEqual(t.body[1].value.values[0].lineno, 3) 1047db96d56Sopenharmony_ci # check the binop location 1057db96d56Sopenharmony_ci binop = t.body[1].value.values[0].value 1067db96d56Sopenharmony_ci self.assertEqual(type(binop), ast.BinOp) 1077db96d56Sopenharmony_ci self.assertEqual(type(binop.left), ast.Name) 1087db96d56Sopenharmony_ci self.assertEqual(type(binop.op), ast.Mult) 1097db96d56Sopenharmony_ci self.assertEqual(type(binop.right), ast.Call) 1107db96d56Sopenharmony_ci self.assertEqual(binop.lineno, 3) 1117db96d56Sopenharmony_ci self.assertEqual(binop.left.lineno, 3) 1127db96d56Sopenharmony_ci self.assertEqual(binop.right.lineno, 3) 1137db96d56Sopenharmony_ci self.assertEqual(binop.col_offset, 3) 1147db96d56Sopenharmony_ci self.assertEqual(binop.left.col_offset, 3) 1157db96d56Sopenharmony_ci self.assertEqual(binop.right.col_offset, 7) 1167db96d56Sopenharmony_ci 1177db96d56Sopenharmony_ci def test_ast_line_numbers_multiple_formattedvalues(self): 1187db96d56Sopenharmony_ci expr = """ 1197db96d56Sopenharmony_cif'no formatted values' 1207db96d56Sopenharmony_cif'eggs {a * x()} spam {b + y()}'""" 1217db96d56Sopenharmony_ci t = ast.parse(expr) 1227db96d56Sopenharmony_ci self.assertEqual(type(t), ast.Module) 1237db96d56Sopenharmony_ci self.assertEqual(len(t.body), 2) 1247db96d56Sopenharmony_ci # check `f'no formatted value'` 1257db96d56Sopenharmony_ci self.assertEqual(type(t.body[0]), ast.Expr) 1267db96d56Sopenharmony_ci self.assertEqual(type(t.body[0].value), ast.JoinedStr) 1277db96d56Sopenharmony_ci self.assertEqual(t.body[0].lineno, 2) 1287db96d56Sopenharmony_ci # check `f'...'` 1297db96d56Sopenharmony_ci self.assertEqual(type(t.body[1]), ast.Expr) 1307db96d56Sopenharmony_ci self.assertEqual(type(t.body[1].value), ast.JoinedStr) 1317db96d56Sopenharmony_ci self.assertEqual(len(t.body[1].value.values), 4) 1327db96d56Sopenharmony_ci self.assertEqual(type(t.body[1].value.values[0]), ast.Constant) 1337db96d56Sopenharmony_ci self.assertEqual(type(t.body[1].value.values[0].value), str) 1347db96d56Sopenharmony_ci self.assertEqual(type(t.body[1].value.values[1]), ast.FormattedValue) 1357db96d56Sopenharmony_ci self.assertEqual(type(t.body[1].value.values[2]), ast.Constant) 1367db96d56Sopenharmony_ci self.assertEqual(type(t.body[1].value.values[2].value), str) 1377db96d56Sopenharmony_ci self.assertEqual(type(t.body[1].value.values[3]), ast.FormattedValue) 1387db96d56Sopenharmony_ci self.assertEqual(t.body[1].lineno, 3) 1397db96d56Sopenharmony_ci self.assertEqual(t.body[1].value.lineno, 3) 1407db96d56Sopenharmony_ci self.assertEqual(t.body[1].value.values[0].lineno, 3) 1417db96d56Sopenharmony_ci self.assertEqual(t.body[1].value.values[1].lineno, 3) 1427db96d56Sopenharmony_ci self.assertEqual(t.body[1].value.values[2].lineno, 3) 1437db96d56Sopenharmony_ci self.assertEqual(t.body[1].value.values[3].lineno, 3) 1447db96d56Sopenharmony_ci # check the first binop location 1457db96d56Sopenharmony_ci binop1 = t.body[1].value.values[1].value 1467db96d56Sopenharmony_ci self.assertEqual(type(binop1), ast.BinOp) 1477db96d56Sopenharmony_ci self.assertEqual(type(binop1.left), ast.Name) 1487db96d56Sopenharmony_ci self.assertEqual(type(binop1.op), ast.Mult) 1497db96d56Sopenharmony_ci self.assertEqual(type(binop1.right), ast.Call) 1507db96d56Sopenharmony_ci self.assertEqual(binop1.lineno, 3) 1517db96d56Sopenharmony_ci self.assertEqual(binop1.left.lineno, 3) 1527db96d56Sopenharmony_ci self.assertEqual(binop1.right.lineno, 3) 1537db96d56Sopenharmony_ci self.assertEqual(binop1.col_offset, 8) 1547db96d56Sopenharmony_ci self.assertEqual(binop1.left.col_offset, 8) 1557db96d56Sopenharmony_ci self.assertEqual(binop1.right.col_offset, 12) 1567db96d56Sopenharmony_ci # check the second binop location 1577db96d56Sopenharmony_ci binop2 = t.body[1].value.values[3].value 1587db96d56Sopenharmony_ci self.assertEqual(type(binop2), ast.BinOp) 1597db96d56Sopenharmony_ci self.assertEqual(type(binop2.left), ast.Name) 1607db96d56Sopenharmony_ci self.assertEqual(type(binop2.op), ast.Add) 1617db96d56Sopenharmony_ci self.assertEqual(type(binop2.right), ast.Call) 1627db96d56Sopenharmony_ci self.assertEqual(binop2.lineno, 3) 1637db96d56Sopenharmony_ci self.assertEqual(binop2.left.lineno, 3) 1647db96d56Sopenharmony_ci self.assertEqual(binop2.right.lineno, 3) 1657db96d56Sopenharmony_ci self.assertEqual(binop2.col_offset, 23) 1667db96d56Sopenharmony_ci self.assertEqual(binop2.left.col_offset, 23) 1677db96d56Sopenharmony_ci self.assertEqual(binop2.right.col_offset, 27) 1687db96d56Sopenharmony_ci 1697db96d56Sopenharmony_ci def test_ast_line_numbers_nested(self): 1707db96d56Sopenharmony_ci expr = """ 1717db96d56Sopenharmony_cia = 10 1727db96d56Sopenharmony_cif'{a * f"-{x()}-"}'""" 1737db96d56Sopenharmony_ci t = ast.parse(expr) 1747db96d56Sopenharmony_ci self.assertEqual(type(t), ast.Module) 1757db96d56Sopenharmony_ci self.assertEqual(len(t.body), 2) 1767db96d56Sopenharmony_ci # check `a = 10` 1777db96d56Sopenharmony_ci self.assertEqual(type(t.body[0]), ast.Assign) 1787db96d56Sopenharmony_ci self.assertEqual(t.body[0].lineno, 2) 1797db96d56Sopenharmony_ci # check `f'...'` 1807db96d56Sopenharmony_ci self.assertEqual(type(t.body[1]), ast.Expr) 1817db96d56Sopenharmony_ci self.assertEqual(type(t.body[1].value), ast.JoinedStr) 1827db96d56Sopenharmony_ci self.assertEqual(len(t.body[1].value.values), 1) 1837db96d56Sopenharmony_ci self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue) 1847db96d56Sopenharmony_ci self.assertEqual(t.body[1].lineno, 3) 1857db96d56Sopenharmony_ci self.assertEqual(t.body[1].value.lineno, 3) 1867db96d56Sopenharmony_ci self.assertEqual(t.body[1].value.values[0].lineno, 3) 1877db96d56Sopenharmony_ci # check the binop location 1887db96d56Sopenharmony_ci binop = t.body[1].value.values[0].value 1897db96d56Sopenharmony_ci self.assertEqual(type(binop), ast.BinOp) 1907db96d56Sopenharmony_ci self.assertEqual(type(binop.left), ast.Name) 1917db96d56Sopenharmony_ci self.assertEqual(type(binop.op), ast.Mult) 1927db96d56Sopenharmony_ci self.assertEqual(type(binop.right), ast.JoinedStr) 1937db96d56Sopenharmony_ci self.assertEqual(binop.lineno, 3) 1947db96d56Sopenharmony_ci self.assertEqual(binop.left.lineno, 3) 1957db96d56Sopenharmony_ci self.assertEqual(binop.right.lineno, 3) 1967db96d56Sopenharmony_ci self.assertEqual(binop.col_offset, 3) 1977db96d56Sopenharmony_ci self.assertEqual(binop.left.col_offset, 3) 1987db96d56Sopenharmony_ci self.assertEqual(binop.right.col_offset, 7) 1997db96d56Sopenharmony_ci # check the nested call location 2007db96d56Sopenharmony_ci self.assertEqual(len(binop.right.values), 3) 2017db96d56Sopenharmony_ci self.assertEqual(type(binop.right.values[0]), ast.Constant) 2027db96d56Sopenharmony_ci self.assertEqual(type(binop.right.values[0].value), str) 2037db96d56Sopenharmony_ci self.assertEqual(type(binop.right.values[1]), ast.FormattedValue) 2047db96d56Sopenharmony_ci self.assertEqual(type(binop.right.values[2]), ast.Constant) 2057db96d56Sopenharmony_ci self.assertEqual(type(binop.right.values[2].value), str) 2067db96d56Sopenharmony_ci self.assertEqual(binop.right.values[0].lineno, 3) 2077db96d56Sopenharmony_ci self.assertEqual(binop.right.values[1].lineno, 3) 2087db96d56Sopenharmony_ci self.assertEqual(binop.right.values[2].lineno, 3) 2097db96d56Sopenharmony_ci call = binop.right.values[1].value 2107db96d56Sopenharmony_ci self.assertEqual(type(call), ast.Call) 2117db96d56Sopenharmony_ci self.assertEqual(call.lineno, 3) 2127db96d56Sopenharmony_ci self.assertEqual(call.col_offset, 11) 2137db96d56Sopenharmony_ci 2147db96d56Sopenharmony_ci def test_ast_line_numbers_duplicate_expression(self): 2157db96d56Sopenharmony_ci expr = """ 2167db96d56Sopenharmony_cia = 10 2177db96d56Sopenharmony_cif'{a * x()} {a * x()} {a * x()}' 2187db96d56Sopenharmony_ci""" 2197db96d56Sopenharmony_ci t = ast.parse(expr) 2207db96d56Sopenharmony_ci self.assertEqual(type(t), ast.Module) 2217db96d56Sopenharmony_ci self.assertEqual(len(t.body), 2) 2227db96d56Sopenharmony_ci # check `a = 10` 2237db96d56Sopenharmony_ci self.assertEqual(type(t.body[0]), ast.Assign) 2247db96d56Sopenharmony_ci self.assertEqual(t.body[0].lineno, 2) 2257db96d56Sopenharmony_ci # check `f'...'` 2267db96d56Sopenharmony_ci self.assertEqual(type(t.body[1]), ast.Expr) 2277db96d56Sopenharmony_ci self.assertEqual(type(t.body[1].value), ast.JoinedStr) 2287db96d56Sopenharmony_ci self.assertEqual(len(t.body[1].value.values), 5) 2297db96d56Sopenharmony_ci self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue) 2307db96d56Sopenharmony_ci self.assertEqual(type(t.body[1].value.values[1]), ast.Constant) 2317db96d56Sopenharmony_ci self.assertEqual(type(t.body[1].value.values[1].value), str) 2327db96d56Sopenharmony_ci self.assertEqual(type(t.body[1].value.values[2]), ast.FormattedValue) 2337db96d56Sopenharmony_ci self.assertEqual(type(t.body[1].value.values[3]), ast.Constant) 2347db96d56Sopenharmony_ci self.assertEqual(type(t.body[1].value.values[3].value), str) 2357db96d56Sopenharmony_ci self.assertEqual(type(t.body[1].value.values[4]), ast.FormattedValue) 2367db96d56Sopenharmony_ci self.assertEqual(t.body[1].lineno, 3) 2377db96d56Sopenharmony_ci self.assertEqual(t.body[1].value.lineno, 3) 2387db96d56Sopenharmony_ci self.assertEqual(t.body[1].value.values[0].lineno, 3) 2397db96d56Sopenharmony_ci self.assertEqual(t.body[1].value.values[1].lineno, 3) 2407db96d56Sopenharmony_ci self.assertEqual(t.body[1].value.values[2].lineno, 3) 2417db96d56Sopenharmony_ci self.assertEqual(t.body[1].value.values[3].lineno, 3) 2427db96d56Sopenharmony_ci self.assertEqual(t.body[1].value.values[4].lineno, 3) 2437db96d56Sopenharmony_ci # check the first binop location 2447db96d56Sopenharmony_ci binop = t.body[1].value.values[0].value 2457db96d56Sopenharmony_ci self.assertEqual(type(binop), ast.BinOp) 2467db96d56Sopenharmony_ci self.assertEqual(type(binop.left), ast.Name) 2477db96d56Sopenharmony_ci self.assertEqual(type(binop.op), ast.Mult) 2487db96d56Sopenharmony_ci self.assertEqual(type(binop.right), ast.Call) 2497db96d56Sopenharmony_ci self.assertEqual(binop.lineno, 3) 2507db96d56Sopenharmony_ci self.assertEqual(binop.left.lineno, 3) 2517db96d56Sopenharmony_ci self.assertEqual(binop.right.lineno, 3) 2527db96d56Sopenharmony_ci self.assertEqual(binop.col_offset, 3) 2537db96d56Sopenharmony_ci self.assertEqual(binop.left.col_offset, 3) 2547db96d56Sopenharmony_ci self.assertEqual(binop.right.col_offset, 7) 2557db96d56Sopenharmony_ci # check the second binop location 2567db96d56Sopenharmony_ci binop = t.body[1].value.values[2].value 2577db96d56Sopenharmony_ci self.assertEqual(type(binop), ast.BinOp) 2587db96d56Sopenharmony_ci self.assertEqual(type(binop.left), ast.Name) 2597db96d56Sopenharmony_ci self.assertEqual(type(binop.op), ast.Mult) 2607db96d56Sopenharmony_ci self.assertEqual(type(binop.right), ast.Call) 2617db96d56Sopenharmony_ci self.assertEqual(binop.lineno, 3) 2627db96d56Sopenharmony_ci self.assertEqual(binop.left.lineno, 3) 2637db96d56Sopenharmony_ci self.assertEqual(binop.right.lineno, 3) 2647db96d56Sopenharmony_ci self.assertEqual(binop.col_offset, 13) 2657db96d56Sopenharmony_ci self.assertEqual(binop.left.col_offset, 13) 2667db96d56Sopenharmony_ci self.assertEqual(binop.right.col_offset, 17) 2677db96d56Sopenharmony_ci # check the third binop location 2687db96d56Sopenharmony_ci binop = t.body[1].value.values[4].value 2697db96d56Sopenharmony_ci self.assertEqual(type(binop), ast.BinOp) 2707db96d56Sopenharmony_ci self.assertEqual(type(binop.left), ast.Name) 2717db96d56Sopenharmony_ci self.assertEqual(type(binop.op), ast.Mult) 2727db96d56Sopenharmony_ci self.assertEqual(type(binop.right), ast.Call) 2737db96d56Sopenharmony_ci self.assertEqual(binop.lineno, 3) 2747db96d56Sopenharmony_ci self.assertEqual(binop.left.lineno, 3) 2757db96d56Sopenharmony_ci self.assertEqual(binop.right.lineno, 3) 2767db96d56Sopenharmony_ci self.assertEqual(binop.col_offset, 23) 2777db96d56Sopenharmony_ci self.assertEqual(binop.left.col_offset, 23) 2787db96d56Sopenharmony_ci self.assertEqual(binop.right.col_offset, 27) 2797db96d56Sopenharmony_ci 2807db96d56Sopenharmony_ci def test_ast_numbers_fstring_with_formatting(self): 2817db96d56Sopenharmony_ci 2827db96d56Sopenharmony_ci t = ast.parse('f"Here is that pesky {xxx:.3f} again"') 2837db96d56Sopenharmony_ci self.assertEqual(len(t.body), 1) 2847db96d56Sopenharmony_ci self.assertEqual(t.body[0].lineno, 1) 2857db96d56Sopenharmony_ci 2867db96d56Sopenharmony_ci self.assertEqual(type(t.body[0]), ast.Expr) 2877db96d56Sopenharmony_ci self.assertEqual(type(t.body[0].value), ast.JoinedStr) 2887db96d56Sopenharmony_ci self.assertEqual(len(t.body[0].value.values), 3) 2897db96d56Sopenharmony_ci 2907db96d56Sopenharmony_ci self.assertEqual(type(t.body[0].value.values[0]), ast.Constant) 2917db96d56Sopenharmony_ci self.assertEqual(type(t.body[0].value.values[1]), ast.FormattedValue) 2927db96d56Sopenharmony_ci self.assertEqual(type(t.body[0].value.values[2]), ast.Constant) 2937db96d56Sopenharmony_ci 2947db96d56Sopenharmony_ci _, expr, _ = t.body[0].value.values 2957db96d56Sopenharmony_ci 2967db96d56Sopenharmony_ci name = expr.value 2977db96d56Sopenharmony_ci self.assertEqual(type(name), ast.Name) 2987db96d56Sopenharmony_ci self.assertEqual(name.lineno, 1) 2997db96d56Sopenharmony_ci self.assertEqual(name.end_lineno, 1) 3007db96d56Sopenharmony_ci self.assertEqual(name.col_offset, 22) 3017db96d56Sopenharmony_ci self.assertEqual(name.end_col_offset, 25) 3027db96d56Sopenharmony_ci 3037db96d56Sopenharmony_ci def test_ast_line_numbers_multiline_fstring(self): 3047db96d56Sopenharmony_ci # See bpo-30465 for details. 3057db96d56Sopenharmony_ci expr = """ 3067db96d56Sopenharmony_cia = 10 3077db96d56Sopenharmony_cif''' 3087db96d56Sopenharmony_ci {a 3097db96d56Sopenharmony_ci * 3107db96d56Sopenharmony_ci x()} 3117db96d56Sopenharmony_cinon-important content 3127db96d56Sopenharmony_ci''' 3137db96d56Sopenharmony_ci""" 3147db96d56Sopenharmony_ci t = ast.parse(expr) 3157db96d56Sopenharmony_ci self.assertEqual(type(t), ast.Module) 3167db96d56Sopenharmony_ci self.assertEqual(len(t.body), 2) 3177db96d56Sopenharmony_ci # check `a = 10` 3187db96d56Sopenharmony_ci self.assertEqual(type(t.body[0]), ast.Assign) 3197db96d56Sopenharmony_ci self.assertEqual(t.body[0].lineno, 2) 3207db96d56Sopenharmony_ci # check `f'...'` 3217db96d56Sopenharmony_ci self.assertEqual(type(t.body[1]), ast.Expr) 3227db96d56Sopenharmony_ci self.assertEqual(type(t.body[1].value), ast.JoinedStr) 3237db96d56Sopenharmony_ci self.assertEqual(len(t.body[1].value.values), 3) 3247db96d56Sopenharmony_ci self.assertEqual(type(t.body[1].value.values[0]), ast.Constant) 3257db96d56Sopenharmony_ci self.assertEqual(type(t.body[1].value.values[0].value), str) 3267db96d56Sopenharmony_ci self.assertEqual(type(t.body[1].value.values[1]), ast.FormattedValue) 3277db96d56Sopenharmony_ci self.assertEqual(type(t.body[1].value.values[2]), ast.Constant) 3287db96d56Sopenharmony_ci self.assertEqual(type(t.body[1].value.values[2].value), str) 3297db96d56Sopenharmony_ci self.assertEqual(t.body[1].lineno, 3) 3307db96d56Sopenharmony_ci self.assertEqual(t.body[1].value.lineno, 3) 3317db96d56Sopenharmony_ci self.assertEqual(t.body[1].value.values[0].lineno, 3) 3327db96d56Sopenharmony_ci self.assertEqual(t.body[1].value.values[1].lineno, 3) 3337db96d56Sopenharmony_ci self.assertEqual(t.body[1].value.values[2].lineno, 3) 3347db96d56Sopenharmony_ci self.assertEqual(t.body[1].col_offset, 0) 3357db96d56Sopenharmony_ci self.assertEqual(t.body[1].value.col_offset, 0) 3367db96d56Sopenharmony_ci self.assertEqual(t.body[1].value.values[0].col_offset, 0) 3377db96d56Sopenharmony_ci self.assertEqual(t.body[1].value.values[1].col_offset, 0) 3387db96d56Sopenharmony_ci self.assertEqual(t.body[1].value.values[2].col_offset, 0) 3397db96d56Sopenharmony_ci # NOTE: the following lineno information and col_offset is correct for 3407db96d56Sopenharmony_ci # expressions within FormattedValues. 3417db96d56Sopenharmony_ci binop = t.body[1].value.values[1].value 3427db96d56Sopenharmony_ci self.assertEqual(type(binop), ast.BinOp) 3437db96d56Sopenharmony_ci self.assertEqual(type(binop.left), ast.Name) 3447db96d56Sopenharmony_ci self.assertEqual(type(binop.op), ast.Mult) 3457db96d56Sopenharmony_ci self.assertEqual(type(binop.right), ast.Call) 3467db96d56Sopenharmony_ci self.assertEqual(binop.lineno, 4) 3477db96d56Sopenharmony_ci self.assertEqual(binop.left.lineno, 4) 3487db96d56Sopenharmony_ci self.assertEqual(binop.right.lineno, 6) 3497db96d56Sopenharmony_ci self.assertEqual(binop.col_offset, 3) 3507db96d56Sopenharmony_ci self.assertEqual(binop.left.col_offset, 3) 3517db96d56Sopenharmony_ci self.assertEqual(binop.right.col_offset, 7) 3527db96d56Sopenharmony_ci 3537db96d56Sopenharmony_ci expr = """ 3547db96d56Sopenharmony_cia = f''' 3557db96d56Sopenharmony_ci {blech} 3567db96d56Sopenharmony_ci ''' 3577db96d56Sopenharmony_ci""" 3587db96d56Sopenharmony_ci t = ast.parse(expr) 3597db96d56Sopenharmony_ci self.assertEqual(type(t), ast.Module) 3607db96d56Sopenharmony_ci self.assertEqual(len(t.body), 1) 3617db96d56Sopenharmony_ci # Check f'...' 3627db96d56Sopenharmony_ci self.assertEqual(type(t.body[0]), ast.Assign) 3637db96d56Sopenharmony_ci self.assertEqual(type(t.body[0].value), ast.JoinedStr) 3647db96d56Sopenharmony_ci self.assertEqual(len(t.body[0].value.values), 3) 3657db96d56Sopenharmony_ci self.assertEqual(type(t.body[0].value.values[1]), ast.FormattedValue) 3667db96d56Sopenharmony_ci self.assertEqual(t.body[0].lineno, 2) 3677db96d56Sopenharmony_ci self.assertEqual(t.body[0].value.lineno, 2) 3687db96d56Sopenharmony_ci self.assertEqual(t.body[0].value.values[0].lineno, 2) 3697db96d56Sopenharmony_ci self.assertEqual(t.body[0].value.values[1].lineno, 2) 3707db96d56Sopenharmony_ci self.assertEqual(t.body[0].value.values[2].lineno, 2) 3717db96d56Sopenharmony_ci self.assertEqual(t.body[0].col_offset, 0) 3727db96d56Sopenharmony_ci self.assertEqual(t.body[0].value.col_offset, 4) 3737db96d56Sopenharmony_ci self.assertEqual(t.body[0].value.values[0].col_offset, 4) 3747db96d56Sopenharmony_ci self.assertEqual(t.body[0].value.values[1].col_offset, 4) 3757db96d56Sopenharmony_ci self.assertEqual(t.body[0].value.values[2].col_offset, 4) 3767db96d56Sopenharmony_ci # Check {blech} 3777db96d56Sopenharmony_ci self.assertEqual(t.body[0].value.values[1].value.lineno, 3) 3787db96d56Sopenharmony_ci self.assertEqual(t.body[0].value.values[1].value.end_lineno, 3) 3797db96d56Sopenharmony_ci self.assertEqual(t.body[0].value.values[1].value.col_offset, 11) 3807db96d56Sopenharmony_ci self.assertEqual(t.body[0].value.values[1].value.end_col_offset, 16) 3817db96d56Sopenharmony_ci 3827db96d56Sopenharmony_ci def test_ast_line_numbers_with_parentheses(self): 3837db96d56Sopenharmony_ci expr = """ 3847db96d56Sopenharmony_cix = ( 3857db96d56Sopenharmony_ci f" {test(t)}" 3867db96d56Sopenharmony_ci)""" 3877db96d56Sopenharmony_ci t = ast.parse(expr) 3887db96d56Sopenharmony_ci self.assertEqual(type(t), ast.Module) 3897db96d56Sopenharmony_ci self.assertEqual(len(t.body), 1) 3907db96d56Sopenharmony_ci # check the test(t) location 3917db96d56Sopenharmony_ci call = t.body[0].value.values[1].value 3927db96d56Sopenharmony_ci self.assertEqual(type(call), ast.Call) 3937db96d56Sopenharmony_ci self.assertEqual(call.lineno, 3) 3947db96d56Sopenharmony_ci self.assertEqual(call.end_lineno, 3) 3957db96d56Sopenharmony_ci self.assertEqual(call.col_offset, 8) 3967db96d56Sopenharmony_ci self.assertEqual(call.end_col_offset, 15) 3977db96d56Sopenharmony_ci 3987db96d56Sopenharmony_ci expr = """ 3997db96d56Sopenharmony_cix = ( 4007db96d56Sopenharmony_ci 'PERL_MM_OPT', ( 4017db96d56Sopenharmony_ci f'wat' 4027db96d56Sopenharmony_ci f'some_string={f(x)} ' 4037db96d56Sopenharmony_ci f'wat' 4047db96d56Sopenharmony_ci ), 4057db96d56Sopenharmony_ci) 4067db96d56Sopenharmony_ci""" 4077db96d56Sopenharmony_ci t = ast.parse(expr) 4087db96d56Sopenharmony_ci self.assertEqual(type(t), ast.Module) 4097db96d56Sopenharmony_ci self.assertEqual(len(t.body), 1) 4107db96d56Sopenharmony_ci # check the fstring 4117db96d56Sopenharmony_ci fstring = t.body[0].value.elts[1] 4127db96d56Sopenharmony_ci self.assertEqual(type(fstring), ast.JoinedStr) 4137db96d56Sopenharmony_ci self.assertEqual(len(fstring.values), 3) 4147db96d56Sopenharmony_ci wat1, middle, wat2 = fstring.values 4157db96d56Sopenharmony_ci # check the first wat 4167db96d56Sopenharmony_ci self.assertEqual(type(wat1), ast.Constant) 4177db96d56Sopenharmony_ci self.assertEqual(wat1.lineno, 4) 4187db96d56Sopenharmony_ci self.assertEqual(wat1.end_lineno, 6) 4197db96d56Sopenharmony_ci self.assertEqual(wat1.col_offset, 12) 4207db96d56Sopenharmony_ci self.assertEqual(wat1.end_col_offset, 18) 4217db96d56Sopenharmony_ci # check the call 4227db96d56Sopenharmony_ci call = middle.value 4237db96d56Sopenharmony_ci self.assertEqual(type(call), ast.Call) 4247db96d56Sopenharmony_ci self.assertEqual(call.lineno, 5) 4257db96d56Sopenharmony_ci self.assertEqual(call.end_lineno, 5) 4267db96d56Sopenharmony_ci self.assertEqual(call.col_offset, 27) 4277db96d56Sopenharmony_ci self.assertEqual(call.end_col_offset, 31) 4287db96d56Sopenharmony_ci # check the second wat 4297db96d56Sopenharmony_ci self.assertEqual(type(wat2), ast.Constant) 4307db96d56Sopenharmony_ci self.assertEqual(wat2.lineno, 4) 4317db96d56Sopenharmony_ci self.assertEqual(wat2.end_lineno, 6) 4327db96d56Sopenharmony_ci self.assertEqual(wat2.col_offset, 12) 4337db96d56Sopenharmony_ci self.assertEqual(wat2.end_col_offset, 18) 4347db96d56Sopenharmony_ci 4357db96d56Sopenharmony_ci def test_docstring(self): 4367db96d56Sopenharmony_ci def f(): 4377db96d56Sopenharmony_ci f'''Not a docstring''' 4387db96d56Sopenharmony_ci self.assertIsNone(f.__doc__) 4397db96d56Sopenharmony_ci def g(): 4407db96d56Sopenharmony_ci '''Not a docstring''' \ 4417db96d56Sopenharmony_ci f'' 4427db96d56Sopenharmony_ci self.assertIsNone(g.__doc__) 4437db96d56Sopenharmony_ci 4447db96d56Sopenharmony_ci def test_literal_eval(self): 4457db96d56Sopenharmony_ci with self.assertRaisesRegex(ValueError, 'malformed node or string'): 4467db96d56Sopenharmony_ci ast.literal_eval("f'x'") 4477db96d56Sopenharmony_ci 4487db96d56Sopenharmony_ci def test_ast_compile_time_concat(self): 4497db96d56Sopenharmony_ci x = [''] 4507db96d56Sopenharmony_ci 4517db96d56Sopenharmony_ci expr = """x[0] = 'foo' f'{3}'""" 4527db96d56Sopenharmony_ci t = ast.parse(expr) 4537db96d56Sopenharmony_ci c = compile(t, '', 'exec') 4547db96d56Sopenharmony_ci exec(c) 4557db96d56Sopenharmony_ci self.assertEqual(x[0], 'foo3') 4567db96d56Sopenharmony_ci 4577db96d56Sopenharmony_ci def test_compile_time_concat_errors(self): 4587db96d56Sopenharmony_ci self.assertAllRaise(SyntaxError, 4597db96d56Sopenharmony_ci 'cannot mix bytes and nonbytes literals', 4607db96d56Sopenharmony_ci [r"""f'' b''""", 4617db96d56Sopenharmony_ci r"""b'' f''""", 4627db96d56Sopenharmony_ci ]) 4637db96d56Sopenharmony_ci 4647db96d56Sopenharmony_ci def test_literal(self): 4657db96d56Sopenharmony_ci self.assertEqual(f'', '') 4667db96d56Sopenharmony_ci self.assertEqual(f'a', 'a') 4677db96d56Sopenharmony_ci self.assertEqual(f' ', ' ') 4687db96d56Sopenharmony_ci 4697db96d56Sopenharmony_ci def test_unterminated_string(self): 4707db96d56Sopenharmony_ci self.assertAllRaise(SyntaxError, 'f-string: unterminated string', 4717db96d56Sopenharmony_ci [r"""f'{"x'""", 4727db96d56Sopenharmony_ci r"""f'{"x}'""", 4737db96d56Sopenharmony_ci r"""f'{("x'""", 4747db96d56Sopenharmony_ci r"""f'{("x}'""", 4757db96d56Sopenharmony_ci ]) 4767db96d56Sopenharmony_ci 4777db96d56Sopenharmony_ci def test_mismatched_parens(self): 4787db96d56Sopenharmony_ci self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\}' " 4797db96d56Sopenharmony_ci r"does not match opening parenthesis '\('", 4807db96d56Sopenharmony_ci ["f'{((}'", 4817db96d56Sopenharmony_ci ]) 4827db96d56Sopenharmony_ci self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\)' " 4837db96d56Sopenharmony_ci r"does not match opening parenthesis '\['", 4847db96d56Sopenharmony_ci ["f'{a[4)}'", 4857db96d56Sopenharmony_ci ]) 4867db96d56Sopenharmony_ci self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\]' " 4877db96d56Sopenharmony_ci r"does not match opening parenthesis '\('", 4887db96d56Sopenharmony_ci ["f'{a(4]}'", 4897db96d56Sopenharmony_ci ]) 4907db96d56Sopenharmony_ci self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\}' " 4917db96d56Sopenharmony_ci r"does not match opening parenthesis '\['", 4927db96d56Sopenharmony_ci ["f'{a[4}'", 4937db96d56Sopenharmony_ci ]) 4947db96d56Sopenharmony_ci self.assertAllRaise(SyntaxError, r"f-string: closing parenthesis '\}' " 4957db96d56Sopenharmony_ci r"does not match opening parenthesis '\('", 4967db96d56Sopenharmony_ci ["f'{a(4}'", 4977db96d56Sopenharmony_ci ]) 4987db96d56Sopenharmony_ci self.assertRaises(SyntaxError, eval, "f'{" + "("*500 + "}'") 4997db96d56Sopenharmony_ci 5007db96d56Sopenharmony_ci def test_double_braces(self): 5017db96d56Sopenharmony_ci self.assertEqual(f'{{', '{') 5027db96d56Sopenharmony_ci self.assertEqual(f'a{{', 'a{') 5037db96d56Sopenharmony_ci self.assertEqual(f'{{b', '{b') 5047db96d56Sopenharmony_ci self.assertEqual(f'a{{b', 'a{b') 5057db96d56Sopenharmony_ci self.assertEqual(f'}}', '}') 5067db96d56Sopenharmony_ci self.assertEqual(f'a}}', 'a}') 5077db96d56Sopenharmony_ci self.assertEqual(f'}}b', '}b') 5087db96d56Sopenharmony_ci self.assertEqual(f'a}}b', 'a}b') 5097db96d56Sopenharmony_ci self.assertEqual(f'{{}}', '{}') 5107db96d56Sopenharmony_ci self.assertEqual(f'a{{}}', 'a{}') 5117db96d56Sopenharmony_ci self.assertEqual(f'{{b}}', '{b}') 5127db96d56Sopenharmony_ci self.assertEqual(f'{{}}c', '{}c') 5137db96d56Sopenharmony_ci self.assertEqual(f'a{{b}}', 'a{b}') 5147db96d56Sopenharmony_ci self.assertEqual(f'a{{}}c', 'a{}c') 5157db96d56Sopenharmony_ci self.assertEqual(f'{{b}}c', '{b}c') 5167db96d56Sopenharmony_ci self.assertEqual(f'a{{b}}c', 'a{b}c') 5177db96d56Sopenharmony_ci 5187db96d56Sopenharmony_ci self.assertEqual(f'{{{10}', '{10') 5197db96d56Sopenharmony_ci self.assertEqual(f'}}{10}', '}10') 5207db96d56Sopenharmony_ci self.assertEqual(f'}}{{{10}', '}{10') 5217db96d56Sopenharmony_ci self.assertEqual(f'}}a{{{10}', '}a{10') 5227db96d56Sopenharmony_ci 5237db96d56Sopenharmony_ci self.assertEqual(f'{10}{{', '10{') 5247db96d56Sopenharmony_ci self.assertEqual(f'{10}}}', '10}') 5257db96d56Sopenharmony_ci self.assertEqual(f'{10}}}{{', '10}{') 5267db96d56Sopenharmony_ci self.assertEqual(f'{10}}}a{{' '}', '10}a{}') 5277db96d56Sopenharmony_ci 5287db96d56Sopenharmony_ci # Inside of strings, don't interpret doubled brackets. 5297db96d56Sopenharmony_ci self.assertEqual(f'{"{{}}"}', '{{}}') 5307db96d56Sopenharmony_ci 5317db96d56Sopenharmony_ci self.assertAllRaise(TypeError, 'unhashable type', 5327db96d56Sopenharmony_ci ["f'{ {{}} }'", # dict in a set 5337db96d56Sopenharmony_ci ]) 5347db96d56Sopenharmony_ci 5357db96d56Sopenharmony_ci def test_compile_time_concat(self): 5367db96d56Sopenharmony_ci x = 'def' 5377db96d56Sopenharmony_ci self.assertEqual('abc' f'## {x}ghi', 'abc## defghi') 5387db96d56Sopenharmony_ci self.assertEqual('abc' f'{x}' 'ghi', 'abcdefghi') 5397db96d56Sopenharmony_ci self.assertEqual('abc' f'{x}' 'gh' f'i{x:4}', 'abcdefghidef ') 5407db96d56Sopenharmony_ci self.assertEqual('{x}' f'{x}', '{x}def') 5417db96d56Sopenharmony_ci self.assertEqual('{x' f'{x}', '{xdef') 5427db96d56Sopenharmony_ci self.assertEqual('{x}' f'{x}', '{x}def') 5437db96d56Sopenharmony_ci self.assertEqual('{{x}}' f'{x}', '{{x}}def') 5447db96d56Sopenharmony_ci self.assertEqual('{{x' f'{x}', '{{xdef') 5457db96d56Sopenharmony_ci self.assertEqual('x}}' f'{x}', 'x}}def') 5467db96d56Sopenharmony_ci self.assertEqual(f'{x}' 'x}}', 'defx}}') 5477db96d56Sopenharmony_ci self.assertEqual(f'{x}' '', 'def') 5487db96d56Sopenharmony_ci self.assertEqual('' f'{x}' '', 'def') 5497db96d56Sopenharmony_ci self.assertEqual('' f'{x}', 'def') 5507db96d56Sopenharmony_ci self.assertEqual(f'{x}' '2', 'def2') 5517db96d56Sopenharmony_ci self.assertEqual('1' f'{x}' '2', '1def2') 5527db96d56Sopenharmony_ci self.assertEqual('1' f'{x}', '1def') 5537db96d56Sopenharmony_ci self.assertEqual(f'{x}' f'-{x}', 'def-def') 5547db96d56Sopenharmony_ci self.assertEqual('' f'', '') 5557db96d56Sopenharmony_ci self.assertEqual('' f'' '', '') 5567db96d56Sopenharmony_ci self.assertEqual('' f'' '' f'', '') 5577db96d56Sopenharmony_ci self.assertEqual(f'', '') 5587db96d56Sopenharmony_ci self.assertEqual(f'' '', '') 5597db96d56Sopenharmony_ci self.assertEqual(f'' '' f'', '') 5607db96d56Sopenharmony_ci self.assertEqual(f'' '' f'' '', '') 5617db96d56Sopenharmony_ci 5627db96d56Sopenharmony_ci self.assertAllRaise(SyntaxError, "f-string: expecting '}'", 5637db96d56Sopenharmony_ci ["f'{3' f'}'", # can't concat to get a valid f-string 5647db96d56Sopenharmony_ci ]) 5657db96d56Sopenharmony_ci 5667db96d56Sopenharmony_ci def test_comments(self): 5677db96d56Sopenharmony_ci # These aren't comments, since they're in strings. 5687db96d56Sopenharmony_ci d = {'#': 'hash'} 5697db96d56Sopenharmony_ci self.assertEqual(f'{"#"}', '#') 5707db96d56Sopenharmony_ci self.assertEqual(f'{d["#"]}', 'hash') 5717db96d56Sopenharmony_ci 5727db96d56Sopenharmony_ci self.assertAllRaise(SyntaxError, "f-string expression part cannot include '#'", 5737db96d56Sopenharmony_ci ["f'{1#}'", # error because the expression becomes "(1#)" 5747db96d56Sopenharmony_ci "f'{3(#)}'", 5757db96d56Sopenharmony_ci "f'{#}'", 5767db96d56Sopenharmony_ci ]) 5777db96d56Sopenharmony_ci self.assertAllRaise(SyntaxError, r"f-string: unmatched '\)'", 5787db96d56Sopenharmony_ci ["f'{)#}'", # When wrapped in parens, this becomes 5797db96d56Sopenharmony_ci # '()#)'. Make sure that doesn't compile. 5807db96d56Sopenharmony_ci ]) 5817db96d56Sopenharmony_ci 5827db96d56Sopenharmony_ci def test_many_expressions(self): 5837db96d56Sopenharmony_ci # Create a string with many expressions in it. Note that 5847db96d56Sopenharmony_ci # because we have a space in here as a literal, we're actually 5857db96d56Sopenharmony_ci # going to use twice as many ast nodes: one for each literal 5867db96d56Sopenharmony_ci # plus one for each expression. 5877db96d56Sopenharmony_ci def build_fstr(n, extra=''): 5887db96d56Sopenharmony_ci return "f'" + ('{x} ' * n) + extra + "'" 5897db96d56Sopenharmony_ci 5907db96d56Sopenharmony_ci x = 'X' 5917db96d56Sopenharmony_ci width = 1 5927db96d56Sopenharmony_ci 5937db96d56Sopenharmony_ci # Test around 256. 5947db96d56Sopenharmony_ci for i in range(250, 260): 5957db96d56Sopenharmony_ci self.assertEqual(eval(build_fstr(i)), (x+' ')*i) 5967db96d56Sopenharmony_ci 5977db96d56Sopenharmony_ci # Test concatenating 2 largs fstrings. 5987db96d56Sopenharmony_ci self.assertEqual(eval(build_fstr(255)*256), (x+' ')*(255*256)) 5997db96d56Sopenharmony_ci 6007db96d56Sopenharmony_ci s = build_fstr(253, '{x:{width}} ') 6017db96d56Sopenharmony_ci self.assertEqual(eval(s), (x+' ')*254) 6027db96d56Sopenharmony_ci 6037db96d56Sopenharmony_ci # Test lots of expressions and constants, concatenated. 6047db96d56Sopenharmony_ci s = "f'{1}' 'x' 'y'" * 1024 6057db96d56Sopenharmony_ci self.assertEqual(eval(s), '1xy' * 1024) 6067db96d56Sopenharmony_ci 6077db96d56Sopenharmony_ci def test_format_specifier_expressions(self): 6087db96d56Sopenharmony_ci width = 10 6097db96d56Sopenharmony_ci precision = 4 6107db96d56Sopenharmony_ci value = decimal.Decimal('12.34567') 6117db96d56Sopenharmony_ci self.assertEqual(f'result: {value:{width}.{precision}}', 'result: 12.35') 6127db96d56Sopenharmony_ci self.assertEqual(f'result: {value:{width!r}.{precision}}', 'result: 12.35') 6137db96d56Sopenharmony_ci self.assertEqual(f'result: {value:{width:0}.{precision:1}}', 'result: 12.35') 6147db96d56Sopenharmony_ci self.assertEqual(f'result: {value:{1}{0:0}.{precision:1}}', 'result: 12.35') 6157db96d56Sopenharmony_ci self.assertEqual(f'result: {value:{ 1}{ 0:0}.{ precision:1}}', 'result: 12.35') 6167db96d56Sopenharmony_ci self.assertEqual(f'{10:#{1}0x}', ' 0xa') 6177db96d56Sopenharmony_ci self.assertEqual(f'{10:{"#"}1{0}{"x"}}', ' 0xa') 6187db96d56Sopenharmony_ci self.assertEqual(f'{-10:-{"#"}1{0}x}', ' -0xa') 6197db96d56Sopenharmony_ci self.assertEqual(f'{-10:{"-"}#{1}0{"x"}}', ' -0xa') 6207db96d56Sopenharmony_ci self.assertEqual(f'{10:#{3 != {4:5} and width}x}', ' 0xa') 6217db96d56Sopenharmony_ci 6227db96d56Sopenharmony_ci self.assertAllRaise(SyntaxError, "f-string: expecting '}'", 6237db96d56Sopenharmony_ci ["""f'{"s"!r{":10"}}'""", 6247db96d56Sopenharmony_ci 6257db96d56Sopenharmony_ci # This looks like a nested format spec. 6267db96d56Sopenharmony_ci ]) 6277db96d56Sopenharmony_ci 6287db96d56Sopenharmony_ci self.assertAllRaise(SyntaxError, "f-string: invalid syntax", 6297db96d56Sopenharmony_ci [# Invalid syntax inside a nested spec. 6307db96d56Sopenharmony_ci "f'{4:{/5}}'", 6317db96d56Sopenharmony_ci ]) 6327db96d56Sopenharmony_ci 6337db96d56Sopenharmony_ci self.assertAllRaise(SyntaxError, "f-string: expressions nested too deeply", 6347db96d56Sopenharmony_ci [# Can't nest format specifiers. 6357db96d56Sopenharmony_ci "f'result: {value:{width:{0}}.{precision:1}}'", 6367db96d56Sopenharmony_ci ]) 6377db96d56Sopenharmony_ci 6387db96d56Sopenharmony_ci self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character', 6397db96d56Sopenharmony_ci [# No expansion inside conversion or for 6407db96d56Sopenharmony_ci # the : or ! itself. 6417db96d56Sopenharmony_ci """f'{"s"!{"r"}}'""", 6427db96d56Sopenharmony_ci ]) 6437db96d56Sopenharmony_ci 6447db96d56Sopenharmony_ci def test_side_effect_order(self): 6457db96d56Sopenharmony_ci class X: 6467db96d56Sopenharmony_ci def __init__(self): 6477db96d56Sopenharmony_ci self.i = 0 6487db96d56Sopenharmony_ci def __format__(self, spec): 6497db96d56Sopenharmony_ci self.i += 1 6507db96d56Sopenharmony_ci return str(self.i) 6517db96d56Sopenharmony_ci 6527db96d56Sopenharmony_ci x = X() 6537db96d56Sopenharmony_ci self.assertEqual(f'{x} {x}', '1 2') 6547db96d56Sopenharmony_ci 6557db96d56Sopenharmony_ci def test_missing_expression(self): 6567db96d56Sopenharmony_ci self.assertAllRaise(SyntaxError, 'f-string: empty expression not allowed', 6577db96d56Sopenharmony_ci ["f'{}'", 6587db96d56Sopenharmony_ci "f'{ }'" 6597db96d56Sopenharmony_ci "f' {} '", 6607db96d56Sopenharmony_ci "f'{10:{ }}'", 6617db96d56Sopenharmony_ci "f' { } '", 6627db96d56Sopenharmony_ci 6637db96d56Sopenharmony_ci # The Python parser ignores also the following 6647db96d56Sopenharmony_ci # whitespace characters in additional to a space. 6657db96d56Sopenharmony_ci "f'''{\t\f\r\n}'''", 6667db96d56Sopenharmony_ci ]) 6677db96d56Sopenharmony_ci 6687db96d56Sopenharmony_ci # Different error messeges are raised when a specfier ('!', ':' or '=') is used after an empty expression 6697db96d56Sopenharmony_ci self.assertAllRaise(SyntaxError, "f-string: expression required before '!'", 6707db96d56Sopenharmony_ci ["f'{!r}'", 6717db96d56Sopenharmony_ci "f'{ !r}'", 6727db96d56Sopenharmony_ci "f'{!}'", 6737db96d56Sopenharmony_ci "f'''{\t\f\r\n!a}'''", 6747db96d56Sopenharmony_ci 6757db96d56Sopenharmony_ci # Catch empty expression before the 6767db96d56Sopenharmony_ci # missing closing brace. 6777db96d56Sopenharmony_ci "f'{!'", 6787db96d56Sopenharmony_ci "f'{!s:'", 6797db96d56Sopenharmony_ci 6807db96d56Sopenharmony_ci # Catch empty expression before the 6817db96d56Sopenharmony_ci # invalid conversion. 6827db96d56Sopenharmony_ci "f'{!x}'", 6837db96d56Sopenharmony_ci "f'{ !xr}'", 6847db96d56Sopenharmony_ci "f'{!x:}'", 6857db96d56Sopenharmony_ci "f'{!x:a}'", 6867db96d56Sopenharmony_ci "f'{ !xr:}'", 6877db96d56Sopenharmony_ci "f'{ !xr:a}'", 6887db96d56Sopenharmony_ci ]) 6897db96d56Sopenharmony_ci 6907db96d56Sopenharmony_ci self.assertAllRaise(SyntaxError, "f-string: expression required before ':'", 6917db96d56Sopenharmony_ci ["f'{:}'", 6927db96d56Sopenharmony_ci "f'{ :!}'", 6937db96d56Sopenharmony_ci "f'{:2}'", 6947db96d56Sopenharmony_ci "f'''{\t\f\r\n:a}'''", 6957db96d56Sopenharmony_ci "f'{:'", 6967db96d56Sopenharmony_ci ]) 6977db96d56Sopenharmony_ci 6987db96d56Sopenharmony_ci self.assertAllRaise(SyntaxError, "f-string: expression required before '='", 6997db96d56Sopenharmony_ci ["f'{=}'", 7007db96d56Sopenharmony_ci "f'{ =}'", 7017db96d56Sopenharmony_ci "f'{ =:}'", 7027db96d56Sopenharmony_ci "f'{ =!}'", 7037db96d56Sopenharmony_ci "f'''{\t\f\r\n=}'''", 7047db96d56Sopenharmony_ci "f'{='", 7057db96d56Sopenharmony_ci ]) 7067db96d56Sopenharmony_ci 7077db96d56Sopenharmony_ci # Different error message is raised for other whitespace characters. 7087db96d56Sopenharmony_ci self.assertAllRaise(SyntaxError, r"invalid non-printable character U\+00A0", 7097db96d56Sopenharmony_ci ["f'''{\xa0}'''", 7107db96d56Sopenharmony_ci "\xa0", 7117db96d56Sopenharmony_ci ]) 7127db96d56Sopenharmony_ci 7137db96d56Sopenharmony_ci def test_parens_in_expressions(self): 7147db96d56Sopenharmony_ci self.assertEqual(f'{3,}', '(3,)') 7157db96d56Sopenharmony_ci 7167db96d56Sopenharmony_ci # Add these because when an expression is evaluated, parens 7177db96d56Sopenharmony_ci # are added around it. But we shouldn't go from an invalid 7187db96d56Sopenharmony_ci # expression to a valid one. The added parens are just 7197db96d56Sopenharmony_ci # supposed to allow whitespace (including newlines). 7207db96d56Sopenharmony_ci self.assertAllRaise(SyntaxError, 'f-string: invalid syntax', 7217db96d56Sopenharmony_ci ["f'{,}'", 7227db96d56Sopenharmony_ci "f'{,}'", # this is (,), which is an error 7237db96d56Sopenharmony_ci ]) 7247db96d56Sopenharmony_ci 7257db96d56Sopenharmony_ci self.assertAllRaise(SyntaxError, r"f-string: unmatched '\)'", 7267db96d56Sopenharmony_ci ["f'{3)+(4}'", 7277db96d56Sopenharmony_ci ]) 7287db96d56Sopenharmony_ci 7297db96d56Sopenharmony_ci self.assertAllRaise(SyntaxError, 'unterminated string literal', 7307db96d56Sopenharmony_ci ["f'{\n}'", 7317db96d56Sopenharmony_ci ]) 7327db96d56Sopenharmony_ci def test_newlines_before_syntax_error(self): 7337db96d56Sopenharmony_ci self.assertAllRaise(SyntaxError, "invalid syntax", 7347db96d56Sopenharmony_ci ["f'{.}'", "\nf'{.}'", "\n\nf'{.}'"]) 7357db96d56Sopenharmony_ci 7367db96d56Sopenharmony_ci def test_backslashes_in_string_part(self): 7377db96d56Sopenharmony_ci self.assertEqual(f'\t', '\t') 7387db96d56Sopenharmony_ci self.assertEqual(r'\t', '\\t') 7397db96d56Sopenharmony_ci self.assertEqual(rf'\t', '\\t') 7407db96d56Sopenharmony_ci self.assertEqual(f'{2}\t', '2\t') 7417db96d56Sopenharmony_ci self.assertEqual(f'{2}\t{3}', '2\t3') 7427db96d56Sopenharmony_ci self.assertEqual(f'\t{3}', '\t3') 7437db96d56Sopenharmony_ci 7447db96d56Sopenharmony_ci self.assertEqual(f'\u0394', '\u0394') 7457db96d56Sopenharmony_ci self.assertEqual(r'\u0394', '\\u0394') 7467db96d56Sopenharmony_ci self.assertEqual(rf'\u0394', '\\u0394') 7477db96d56Sopenharmony_ci self.assertEqual(f'{2}\u0394', '2\u0394') 7487db96d56Sopenharmony_ci self.assertEqual(f'{2}\u0394{3}', '2\u03943') 7497db96d56Sopenharmony_ci self.assertEqual(f'\u0394{3}', '\u03943') 7507db96d56Sopenharmony_ci 7517db96d56Sopenharmony_ci self.assertEqual(f'\U00000394', '\u0394') 7527db96d56Sopenharmony_ci self.assertEqual(r'\U00000394', '\\U00000394') 7537db96d56Sopenharmony_ci self.assertEqual(rf'\U00000394', '\\U00000394') 7547db96d56Sopenharmony_ci self.assertEqual(f'{2}\U00000394', '2\u0394') 7557db96d56Sopenharmony_ci self.assertEqual(f'{2}\U00000394{3}', '2\u03943') 7567db96d56Sopenharmony_ci self.assertEqual(f'\U00000394{3}', '\u03943') 7577db96d56Sopenharmony_ci 7587db96d56Sopenharmony_ci self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}', '\u0394') 7597db96d56Sopenharmony_ci self.assertEqual(f'{2}\N{GREEK CAPITAL LETTER DELTA}', '2\u0394') 7607db96d56Sopenharmony_ci self.assertEqual(f'{2}\N{GREEK CAPITAL LETTER DELTA}{3}', '2\u03943') 7617db96d56Sopenharmony_ci self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}{3}', '\u03943') 7627db96d56Sopenharmony_ci self.assertEqual(f'2\N{GREEK CAPITAL LETTER DELTA}', '2\u0394') 7637db96d56Sopenharmony_ci self.assertEqual(f'2\N{GREEK CAPITAL LETTER DELTA}3', '2\u03943') 7647db96d56Sopenharmony_ci self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}3', '\u03943') 7657db96d56Sopenharmony_ci 7667db96d56Sopenharmony_ci self.assertEqual(f'\x20', ' ') 7677db96d56Sopenharmony_ci self.assertEqual(r'\x20', '\\x20') 7687db96d56Sopenharmony_ci self.assertEqual(rf'\x20', '\\x20') 7697db96d56Sopenharmony_ci self.assertEqual(f'{2}\x20', '2 ') 7707db96d56Sopenharmony_ci self.assertEqual(f'{2}\x20{3}', '2 3') 7717db96d56Sopenharmony_ci self.assertEqual(f'\x20{3}', ' 3') 7727db96d56Sopenharmony_ci 7737db96d56Sopenharmony_ci self.assertEqual(f'2\x20', '2 ') 7747db96d56Sopenharmony_ci self.assertEqual(f'2\x203', '2 3') 7757db96d56Sopenharmony_ci self.assertEqual(f'\x203', ' 3') 7767db96d56Sopenharmony_ci 7777db96d56Sopenharmony_ci with self.assertWarns(DeprecationWarning): # invalid escape sequence 7787db96d56Sopenharmony_ci value = eval(r"f'\{6*7}'") 7797db96d56Sopenharmony_ci self.assertEqual(value, '\\42') 7807db96d56Sopenharmony_ci self.assertEqual(f'\\{6*7}', '\\42') 7817db96d56Sopenharmony_ci self.assertEqual(fr'\{6*7}', '\\42') 7827db96d56Sopenharmony_ci 7837db96d56Sopenharmony_ci AMPERSAND = 'spam' 7847db96d56Sopenharmony_ci # Get the right unicode character (&), or pick up local variable 7857db96d56Sopenharmony_ci # depending on the number of backslashes. 7867db96d56Sopenharmony_ci self.assertEqual(f'\N{AMPERSAND}', '&') 7877db96d56Sopenharmony_ci self.assertEqual(f'\\N{AMPERSAND}', '\\Nspam') 7887db96d56Sopenharmony_ci self.assertEqual(fr'\N{AMPERSAND}', '\\Nspam') 7897db96d56Sopenharmony_ci self.assertEqual(f'\\\N{AMPERSAND}', '\\&') 7907db96d56Sopenharmony_ci 7917db96d56Sopenharmony_ci def test_misformed_unicode_character_name(self): 7927db96d56Sopenharmony_ci # These test are needed because unicode names are parsed 7937db96d56Sopenharmony_ci # differently inside f-strings. 7947db96d56Sopenharmony_ci self.assertAllRaise(SyntaxError, r"\(unicode error\) 'unicodeescape' codec can't decode bytes in position .*: malformed \\N character escape", 7957db96d56Sopenharmony_ci [r"f'\N'", 7967db96d56Sopenharmony_ci r"f'\N '", 7977db96d56Sopenharmony_ci r"f'\N '", # See bpo-46503. 7987db96d56Sopenharmony_ci r"f'\N{'", 7997db96d56Sopenharmony_ci r"f'\N{GREEK CAPITAL LETTER DELTA'", 8007db96d56Sopenharmony_ci 8017db96d56Sopenharmony_ci # Here are the non-f-string versions, 8027db96d56Sopenharmony_ci # which should give the same errors. 8037db96d56Sopenharmony_ci r"'\N'", 8047db96d56Sopenharmony_ci r"'\N '", 8057db96d56Sopenharmony_ci r"'\N '", 8067db96d56Sopenharmony_ci r"'\N{'", 8077db96d56Sopenharmony_ci r"'\N{GREEK CAPITAL LETTER DELTA'", 8087db96d56Sopenharmony_ci ]) 8097db96d56Sopenharmony_ci 8107db96d56Sopenharmony_ci def test_no_backslashes_in_expression_part(self): 8117db96d56Sopenharmony_ci self.assertAllRaise(SyntaxError, 'f-string expression part cannot include a backslash', 8127db96d56Sopenharmony_ci [r"f'{\'a\'}'", 8137db96d56Sopenharmony_ci r"f'{\t3}'", 8147db96d56Sopenharmony_ci r"f'{\}'", 8157db96d56Sopenharmony_ci r"rf'{\'a\'}'", 8167db96d56Sopenharmony_ci r"rf'{\t3}'", 8177db96d56Sopenharmony_ci r"rf'{\}'", 8187db96d56Sopenharmony_ci r"""rf'{"\N{LEFT CURLY BRACKET}"}'""", 8197db96d56Sopenharmony_ci r"f'{\n}'", 8207db96d56Sopenharmony_ci ]) 8217db96d56Sopenharmony_ci 8227db96d56Sopenharmony_ci def test_no_escapes_for_braces(self): 8237db96d56Sopenharmony_ci """ 8247db96d56Sopenharmony_ci Only literal curly braces begin an expression. 8257db96d56Sopenharmony_ci """ 8267db96d56Sopenharmony_ci # \x7b is '{'. 8277db96d56Sopenharmony_ci self.assertEqual(f'\x7b1+1}}', '{1+1}') 8287db96d56Sopenharmony_ci self.assertEqual(f'\x7b1+1', '{1+1') 8297db96d56Sopenharmony_ci self.assertEqual(f'\u007b1+1', '{1+1') 8307db96d56Sopenharmony_ci self.assertEqual(f'\N{LEFT CURLY BRACKET}1+1\N{RIGHT CURLY BRACKET}', '{1+1}') 8317db96d56Sopenharmony_ci 8327db96d56Sopenharmony_ci def test_newlines_in_expressions(self): 8337db96d56Sopenharmony_ci self.assertEqual(f'{0}', '0') 8347db96d56Sopenharmony_ci self.assertEqual(rf'''{3+ 8357db96d56Sopenharmony_ci4}''', '7') 8367db96d56Sopenharmony_ci 8377db96d56Sopenharmony_ci def test_lambda(self): 8387db96d56Sopenharmony_ci x = 5 8397db96d56Sopenharmony_ci self.assertEqual(f'{(lambda y:x*y)("8")!r}', "'88888'") 8407db96d56Sopenharmony_ci self.assertEqual(f'{(lambda y:x*y)("8")!r:10}', "'88888' ") 8417db96d56Sopenharmony_ci self.assertEqual(f'{(lambda y:x*y)("8"):10}', "88888 ") 8427db96d56Sopenharmony_ci 8437db96d56Sopenharmony_ci # lambda doesn't work without parens, because the colon 8447db96d56Sopenharmony_ci # makes the parser think it's a format_spec 8457db96d56Sopenharmony_ci self.assertAllRaise(SyntaxError, 'f-string: invalid syntax', 8467db96d56Sopenharmony_ci ["f'{lambda x:x}'", 8477db96d56Sopenharmony_ci ]) 8487db96d56Sopenharmony_ci 8497db96d56Sopenharmony_ci def test_yield(self): 8507db96d56Sopenharmony_ci # Not terribly useful, but make sure the yield turns 8517db96d56Sopenharmony_ci # a function into a generator 8527db96d56Sopenharmony_ci def fn(y): 8537db96d56Sopenharmony_ci f'y:{yield y*2}' 8547db96d56Sopenharmony_ci f'{yield}' 8557db96d56Sopenharmony_ci 8567db96d56Sopenharmony_ci g = fn(4) 8577db96d56Sopenharmony_ci self.assertEqual(next(g), 8) 8587db96d56Sopenharmony_ci self.assertEqual(next(g), None) 8597db96d56Sopenharmony_ci 8607db96d56Sopenharmony_ci def test_yield_send(self): 8617db96d56Sopenharmony_ci def fn(x): 8627db96d56Sopenharmony_ci yield f'x:{yield (lambda i: x * i)}' 8637db96d56Sopenharmony_ci 8647db96d56Sopenharmony_ci g = fn(10) 8657db96d56Sopenharmony_ci the_lambda = next(g) 8667db96d56Sopenharmony_ci self.assertEqual(the_lambda(4), 40) 8677db96d56Sopenharmony_ci self.assertEqual(g.send('string'), 'x:string') 8687db96d56Sopenharmony_ci 8697db96d56Sopenharmony_ci def test_expressions_with_triple_quoted_strings(self): 8707db96d56Sopenharmony_ci self.assertEqual(f"{'''x'''}", 'x') 8717db96d56Sopenharmony_ci self.assertEqual(f"{'''eric's'''}", "eric's") 8727db96d56Sopenharmony_ci 8737db96d56Sopenharmony_ci # Test concatenation within an expression 8747db96d56Sopenharmony_ci self.assertEqual(f'{"x" """eric"s""" "y"}', 'xeric"sy') 8757db96d56Sopenharmony_ci self.assertEqual(f'{"x" """eric"s"""}', 'xeric"s') 8767db96d56Sopenharmony_ci self.assertEqual(f'{"""eric"s""" "y"}', 'eric"sy') 8777db96d56Sopenharmony_ci self.assertEqual(f'{"""x""" """eric"s""" "y"}', 'xeric"sy') 8787db96d56Sopenharmony_ci self.assertEqual(f'{"""x""" """eric"s""" """y"""}', 'xeric"sy') 8797db96d56Sopenharmony_ci self.assertEqual(f'{r"""x""" """eric"s""" """y"""}', 'xeric"sy') 8807db96d56Sopenharmony_ci 8817db96d56Sopenharmony_ci def test_multiple_vars(self): 8827db96d56Sopenharmony_ci x = 98 8837db96d56Sopenharmony_ci y = 'abc' 8847db96d56Sopenharmony_ci self.assertEqual(f'{x}{y}', '98abc') 8857db96d56Sopenharmony_ci 8867db96d56Sopenharmony_ci self.assertEqual(f'X{x}{y}', 'X98abc') 8877db96d56Sopenharmony_ci self.assertEqual(f'{x}X{y}', '98Xabc') 8887db96d56Sopenharmony_ci self.assertEqual(f'{x}{y}X', '98abcX') 8897db96d56Sopenharmony_ci 8907db96d56Sopenharmony_ci self.assertEqual(f'X{x}Y{y}', 'X98Yabc') 8917db96d56Sopenharmony_ci self.assertEqual(f'X{x}{y}Y', 'X98abcY') 8927db96d56Sopenharmony_ci self.assertEqual(f'{x}X{y}Y', '98XabcY') 8937db96d56Sopenharmony_ci 8947db96d56Sopenharmony_ci self.assertEqual(f'X{x}Y{y}Z', 'X98YabcZ') 8957db96d56Sopenharmony_ci 8967db96d56Sopenharmony_ci def test_closure(self): 8977db96d56Sopenharmony_ci def outer(x): 8987db96d56Sopenharmony_ci def inner(): 8997db96d56Sopenharmony_ci return f'x:{x}' 9007db96d56Sopenharmony_ci return inner 9017db96d56Sopenharmony_ci 9027db96d56Sopenharmony_ci self.assertEqual(outer('987')(), 'x:987') 9037db96d56Sopenharmony_ci self.assertEqual(outer(7)(), 'x:7') 9047db96d56Sopenharmony_ci 9057db96d56Sopenharmony_ci def test_arguments(self): 9067db96d56Sopenharmony_ci y = 2 9077db96d56Sopenharmony_ci def f(x, width): 9087db96d56Sopenharmony_ci return f'x={x*y:{width}}' 9097db96d56Sopenharmony_ci 9107db96d56Sopenharmony_ci self.assertEqual(f('foo', 10), 'x=foofoo ') 9117db96d56Sopenharmony_ci x = 'bar' 9127db96d56Sopenharmony_ci self.assertEqual(f(10, 10), 'x= 20') 9137db96d56Sopenharmony_ci 9147db96d56Sopenharmony_ci def test_locals(self): 9157db96d56Sopenharmony_ci value = 123 9167db96d56Sopenharmony_ci self.assertEqual(f'v:{value}', 'v:123') 9177db96d56Sopenharmony_ci 9187db96d56Sopenharmony_ci def test_missing_variable(self): 9197db96d56Sopenharmony_ci with self.assertRaises(NameError): 9207db96d56Sopenharmony_ci f'v:{value}' 9217db96d56Sopenharmony_ci 9227db96d56Sopenharmony_ci def test_missing_format_spec(self): 9237db96d56Sopenharmony_ci class O: 9247db96d56Sopenharmony_ci def __format__(self, spec): 9257db96d56Sopenharmony_ci if not spec: 9267db96d56Sopenharmony_ci return '*' 9277db96d56Sopenharmony_ci return spec 9287db96d56Sopenharmony_ci 9297db96d56Sopenharmony_ci self.assertEqual(f'{O():x}', 'x') 9307db96d56Sopenharmony_ci self.assertEqual(f'{O()}', '*') 9317db96d56Sopenharmony_ci self.assertEqual(f'{O():}', '*') 9327db96d56Sopenharmony_ci 9337db96d56Sopenharmony_ci self.assertEqual(f'{3:}', '3') 9347db96d56Sopenharmony_ci self.assertEqual(f'{3!s:}', '3') 9357db96d56Sopenharmony_ci 9367db96d56Sopenharmony_ci def test_global(self): 9377db96d56Sopenharmony_ci self.assertEqual(f'g:{a_global}', 'g:global variable') 9387db96d56Sopenharmony_ci self.assertEqual(f'g:{a_global!r}', "g:'global variable'") 9397db96d56Sopenharmony_ci 9407db96d56Sopenharmony_ci a_local = 'local variable' 9417db96d56Sopenharmony_ci self.assertEqual(f'g:{a_global} l:{a_local}', 9427db96d56Sopenharmony_ci 'g:global variable l:local variable') 9437db96d56Sopenharmony_ci self.assertEqual(f'g:{a_global!r}', 9447db96d56Sopenharmony_ci "g:'global variable'") 9457db96d56Sopenharmony_ci self.assertEqual(f'g:{a_global} l:{a_local!r}', 9467db96d56Sopenharmony_ci "g:global variable l:'local variable'") 9477db96d56Sopenharmony_ci 9487db96d56Sopenharmony_ci self.assertIn("module 'unittest' from", f'{unittest}') 9497db96d56Sopenharmony_ci 9507db96d56Sopenharmony_ci def test_shadowed_global(self): 9517db96d56Sopenharmony_ci a_global = 'really a local' 9527db96d56Sopenharmony_ci self.assertEqual(f'g:{a_global}', 'g:really a local') 9537db96d56Sopenharmony_ci self.assertEqual(f'g:{a_global!r}', "g:'really a local'") 9547db96d56Sopenharmony_ci 9557db96d56Sopenharmony_ci a_local = 'local variable' 9567db96d56Sopenharmony_ci self.assertEqual(f'g:{a_global} l:{a_local}', 9577db96d56Sopenharmony_ci 'g:really a local l:local variable') 9587db96d56Sopenharmony_ci self.assertEqual(f'g:{a_global!r}', 9597db96d56Sopenharmony_ci "g:'really a local'") 9607db96d56Sopenharmony_ci self.assertEqual(f'g:{a_global} l:{a_local!r}', 9617db96d56Sopenharmony_ci "g:really a local l:'local variable'") 9627db96d56Sopenharmony_ci 9637db96d56Sopenharmony_ci def test_call(self): 9647db96d56Sopenharmony_ci def foo(x): 9657db96d56Sopenharmony_ci return 'x=' + str(x) 9667db96d56Sopenharmony_ci 9677db96d56Sopenharmony_ci self.assertEqual(f'{foo(10)}', 'x=10') 9687db96d56Sopenharmony_ci 9697db96d56Sopenharmony_ci def test_nested_fstrings(self): 9707db96d56Sopenharmony_ci y = 5 9717db96d56Sopenharmony_ci self.assertEqual(f'{f"{0}"*3}', '000') 9727db96d56Sopenharmony_ci self.assertEqual(f'{f"{y}"*3}', '555') 9737db96d56Sopenharmony_ci 9747db96d56Sopenharmony_ci def test_invalid_string_prefixes(self): 9757db96d56Sopenharmony_ci single_quote_cases = ["fu''", 9767db96d56Sopenharmony_ci "uf''", 9777db96d56Sopenharmony_ci "Fu''", 9787db96d56Sopenharmony_ci "fU''", 9797db96d56Sopenharmony_ci "Uf''", 9807db96d56Sopenharmony_ci "uF''", 9817db96d56Sopenharmony_ci "ufr''", 9827db96d56Sopenharmony_ci "urf''", 9837db96d56Sopenharmony_ci "fur''", 9847db96d56Sopenharmony_ci "fru''", 9857db96d56Sopenharmony_ci "rfu''", 9867db96d56Sopenharmony_ci "ruf''", 9877db96d56Sopenharmony_ci "FUR''", 9887db96d56Sopenharmony_ci "Fur''", 9897db96d56Sopenharmony_ci "fb''", 9907db96d56Sopenharmony_ci "fB''", 9917db96d56Sopenharmony_ci "Fb''", 9927db96d56Sopenharmony_ci "FB''", 9937db96d56Sopenharmony_ci "bf''", 9947db96d56Sopenharmony_ci "bF''", 9957db96d56Sopenharmony_ci "Bf''", 9967db96d56Sopenharmony_ci "BF''",] 9977db96d56Sopenharmony_ci double_quote_cases = [case.replace("'", '"') for case in single_quote_cases] 9987db96d56Sopenharmony_ci self.assertAllRaise(SyntaxError, 'invalid syntax', 9997db96d56Sopenharmony_ci single_quote_cases + double_quote_cases) 10007db96d56Sopenharmony_ci 10017db96d56Sopenharmony_ci def test_leading_trailing_spaces(self): 10027db96d56Sopenharmony_ci self.assertEqual(f'{ 3}', '3') 10037db96d56Sopenharmony_ci self.assertEqual(f'{ 3}', '3') 10047db96d56Sopenharmony_ci self.assertEqual(f'{3 }', '3') 10057db96d56Sopenharmony_ci self.assertEqual(f'{3 }', '3') 10067db96d56Sopenharmony_ci 10077db96d56Sopenharmony_ci self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]}}', 10087db96d56Sopenharmony_ci 'expr={1: 2}') 10097db96d56Sopenharmony_ci self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]} }', 10107db96d56Sopenharmony_ci 'expr={1: 2}') 10117db96d56Sopenharmony_ci 10127db96d56Sopenharmony_ci def test_not_equal(self): 10137db96d56Sopenharmony_ci # There's a special test for this because there's a special 10147db96d56Sopenharmony_ci # case in the f-string parser to look for != as not ending an 10157db96d56Sopenharmony_ci # expression. Normally it would, while looking for !s or !r. 10167db96d56Sopenharmony_ci 10177db96d56Sopenharmony_ci self.assertEqual(f'{3!=4}', 'True') 10187db96d56Sopenharmony_ci self.assertEqual(f'{3!=4:}', 'True') 10197db96d56Sopenharmony_ci self.assertEqual(f'{3!=4!s}', 'True') 10207db96d56Sopenharmony_ci self.assertEqual(f'{3!=4!s:.3}', 'Tru') 10217db96d56Sopenharmony_ci 10227db96d56Sopenharmony_ci def test_equal_equal(self): 10237db96d56Sopenharmony_ci # Because an expression ending in = has special meaning, 10247db96d56Sopenharmony_ci # there's a special test for ==. Make sure it works. 10257db96d56Sopenharmony_ci 10267db96d56Sopenharmony_ci self.assertEqual(f'{0==1}', 'False') 10277db96d56Sopenharmony_ci 10287db96d56Sopenharmony_ci def test_conversions(self): 10297db96d56Sopenharmony_ci self.assertEqual(f'{3.14:10.10}', ' 3.14') 10307db96d56Sopenharmony_ci self.assertEqual(f'{3.14!s:10.10}', '3.14 ') 10317db96d56Sopenharmony_ci self.assertEqual(f'{3.14!r:10.10}', '3.14 ') 10327db96d56Sopenharmony_ci self.assertEqual(f'{3.14!a:10.10}', '3.14 ') 10337db96d56Sopenharmony_ci 10347db96d56Sopenharmony_ci self.assertEqual(f'{"a"}', 'a') 10357db96d56Sopenharmony_ci self.assertEqual(f'{"a"!r}', "'a'") 10367db96d56Sopenharmony_ci self.assertEqual(f'{"a"!a}', "'a'") 10377db96d56Sopenharmony_ci 10387db96d56Sopenharmony_ci # Not a conversion. 10397db96d56Sopenharmony_ci self.assertEqual(f'{"a!r"}', "a!r") 10407db96d56Sopenharmony_ci 10417db96d56Sopenharmony_ci # Not a conversion, but show that ! is allowed in a format spec. 10427db96d56Sopenharmony_ci self.assertEqual(f'{3.14:!<10.10}', '3.14!!!!!!') 10437db96d56Sopenharmony_ci 10447db96d56Sopenharmony_ci self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character', 10457db96d56Sopenharmony_ci ["f'{3!g}'", 10467db96d56Sopenharmony_ci "f'{3!A}'", 10477db96d56Sopenharmony_ci "f'{3!3}'", 10487db96d56Sopenharmony_ci "f'{3!G}'", 10497db96d56Sopenharmony_ci "f'{3!!}'", 10507db96d56Sopenharmony_ci "f'{3!:}'", 10517db96d56Sopenharmony_ci "f'{3! s}'", # no space before conversion char 10527db96d56Sopenharmony_ci ]) 10537db96d56Sopenharmony_ci 10547db96d56Sopenharmony_ci self.assertAllRaise(SyntaxError, "f-string: expecting '}'", 10557db96d56Sopenharmony_ci ["f'{x!s{y}}'", 10567db96d56Sopenharmony_ci "f'{3!ss}'", 10577db96d56Sopenharmony_ci "f'{3!ss:}'", 10587db96d56Sopenharmony_ci "f'{3!ss:s}'", 10597db96d56Sopenharmony_ci ]) 10607db96d56Sopenharmony_ci 10617db96d56Sopenharmony_ci def test_assignment(self): 10627db96d56Sopenharmony_ci self.assertAllRaise(SyntaxError, r'invalid syntax', 10637db96d56Sopenharmony_ci ["f'' = 3", 10647db96d56Sopenharmony_ci "f'{0}' = x", 10657db96d56Sopenharmony_ci "f'{x}' = x", 10667db96d56Sopenharmony_ci ]) 10677db96d56Sopenharmony_ci 10687db96d56Sopenharmony_ci def test_del(self): 10697db96d56Sopenharmony_ci self.assertAllRaise(SyntaxError, 'invalid syntax', 10707db96d56Sopenharmony_ci ["del f''", 10717db96d56Sopenharmony_ci "del '' f''", 10727db96d56Sopenharmony_ci ]) 10737db96d56Sopenharmony_ci 10747db96d56Sopenharmony_ci def test_mismatched_braces(self): 10757db96d56Sopenharmony_ci self.assertAllRaise(SyntaxError, "f-string: single '}' is not allowed", 10767db96d56Sopenharmony_ci ["f'{{}'", 10777db96d56Sopenharmony_ci "f'{{}}}'", 10787db96d56Sopenharmony_ci "f'}'", 10797db96d56Sopenharmony_ci "f'x}'", 10807db96d56Sopenharmony_ci "f'x}x'", 10817db96d56Sopenharmony_ci r"f'\u007b}'", 10827db96d56Sopenharmony_ci 10837db96d56Sopenharmony_ci # Can't have { or } in a format spec. 10847db96d56Sopenharmony_ci "f'{3:}>10}'", 10857db96d56Sopenharmony_ci "f'{3:}}>10}'", 10867db96d56Sopenharmony_ci ]) 10877db96d56Sopenharmony_ci 10887db96d56Sopenharmony_ci self.assertAllRaise(SyntaxError, "f-string: expecting '}'", 10897db96d56Sopenharmony_ci ["f'{3:{{>10}'", 10907db96d56Sopenharmony_ci "f'{3'", 10917db96d56Sopenharmony_ci "f'{3!'", 10927db96d56Sopenharmony_ci "f'{3:'", 10937db96d56Sopenharmony_ci "f'{3!s'", 10947db96d56Sopenharmony_ci "f'{3!s:'", 10957db96d56Sopenharmony_ci "f'{3!s:3'", 10967db96d56Sopenharmony_ci "f'x{'", 10977db96d56Sopenharmony_ci "f'x{x'", 10987db96d56Sopenharmony_ci "f'{x'", 10997db96d56Sopenharmony_ci "f'{3:s'", 11007db96d56Sopenharmony_ci "f'{{{'", 11017db96d56Sopenharmony_ci "f'{{}}{'", 11027db96d56Sopenharmony_ci "f'{'", 11037db96d56Sopenharmony_ci "f'x{<'", # See bpo-46762. 11047db96d56Sopenharmony_ci "f'x{>'", 11057db96d56Sopenharmony_ci "f'{i='", # See gh-93418. 11067db96d56Sopenharmony_ci ]) 11077db96d56Sopenharmony_ci 11087db96d56Sopenharmony_ci # But these are just normal strings. 11097db96d56Sopenharmony_ci self.assertEqual(f'{"{"}', '{') 11107db96d56Sopenharmony_ci self.assertEqual(f'{"}"}', '}') 11117db96d56Sopenharmony_ci self.assertEqual(f'{3:{"}"}>10}', '}}}}}}}}}3') 11127db96d56Sopenharmony_ci self.assertEqual(f'{2:{"{"}>10}', '{{{{{{{{{2') 11137db96d56Sopenharmony_ci 11147db96d56Sopenharmony_ci def test_if_conditional(self): 11157db96d56Sopenharmony_ci # There's special logic in compile.c to test if the 11167db96d56Sopenharmony_ci # conditional for an if (and while) are constants. Exercise 11177db96d56Sopenharmony_ci # that code. 11187db96d56Sopenharmony_ci 11197db96d56Sopenharmony_ci def test_fstring(x, expected): 11207db96d56Sopenharmony_ci flag = 0 11217db96d56Sopenharmony_ci if f'{x}': 11227db96d56Sopenharmony_ci flag = 1 11237db96d56Sopenharmony_ci else: 11247db96d56Sopenharmony_ci flag = 2 11257db96d56Sopenharmony_ci self.assertEqual(flag, expected) 11267db96d56Sopenharmony_ci 11277db96d56Sopenharmony_ci def test_concat_empty(x, expected): 11287db96d56Sopenharmony_ci flag = 0 11297db96d56Sopenharmony_ci if '' f'{x}': 11307db96d56Sopenharmony_ci flag = 1 11317db96d56Sopenharmony_ci else: 11327db96d56Sopenharmony_ci flag = 2 11337db96d56Sopenharmony_ci self.assertEqual(flag, expected) 11347db96d56Sopenharmony_ci 11357db96d56Sopenharmony_ci def test_concat_non_empty(x, expected): 11367db96d56Sopenharmony_ci flag = 0 11377db96d56Sopenharmony_ci if ' ' f'{x}': 11387db96d56Sopenharmony_ci flag = 1 11397db96d56Sopenharmony_ci else: 11407db96d56Sopenharmony_ci flag = 2 11417db96d56Sopenharmony_ci self.assertEqual(flag, expected) 11427db96d56Sopenharmony_ci 11437db96d56Sopenharmony_ci test_fstring('', 2) 11447db96d56Sopenharmony_ci test_fstring(' ', 1) 11457db96d56Sopenharmony_ci 11467db96d56Sopenharmony_ci test_concat_empty('', 2) 11477db96d56Sopenharmony_ci test_concat_empty(' ', 1) 11487db96d56Sopenharmony_ci 11497db96d56Sopenharmony_ci test_concat_non_empty('', 1) 11507db96d56Sopenharmony_ci test_concat_non_empty(' ', 1) 11517db96d56Sopenharmony_ci 11527db96d56Sopenharmony_ci def test_empty_format_specifier(self): 11537db96d56Sopenharmony_ci x = 'test' 11547db96d56Sopenharmony_ci self.assertEqual(f'{x}', 'test') 11557db96d56Sopenharmony_ci self.assertEqual(f'{x:}', 'test') 11567db96d56Sopenharmony_ci self.assertEqual(f'{x!s:}', 'test') 11577db96d56Sopenharmony_ci self.assertEqual(f'{x!r:}', "'test'") 11587db96d56Sopenharmony_ci 11597db96d56Sopenharmony_ci def test_str_format_differences(self): 11607db96d56Sopenharmony_ci d = {'a': 'string', 11617db96d56Sopenharmony_ci 0: 'integer', 11627db96d56Sopenharmony_ci } 11637db96d56Sopenharmony_ci a = 0 11647db96d56Sopenharmony_ci self.assertEqual(f'{d[0]}', 'integer') 11657db96d56Sopenharmony_ci self.assertEqual(f'{d["a"]}', 'string') 11667db96d56Sopenharmony_ci self.assertEqual(f'{d[a]}', 'integer') 11677db96d56Sopenharmony_ci self.assertEqual('{d[a]}'.format(d=d), 'string') 11687db96d56Sopenharmony_ci self.assertEqual('{d[0]}'.format(d=d), 'integer') 11697db96d56Sopenharmony_ci 11707db96d56Sopenharmony_ci def test_errors(self): 11717db96d56Sopenharmony_ci # see issue 26287 11727db96d56Sopenharmony_ci self.assertAllRaise(TypeError, 'unsupported', 11737db96d56Sopenharmony_ci [r"f'{(lambda: 0):x}'", 11747db96d56Sopenharmony_ci r"f'{(0,):x}'", 11757db96d56Sopenharmony_ci ]) 11767db96d56Sopenharmony_ci self.assertAllRaise(ValueError, 'Unknown format code', 11777db96d56Sopenharmony_ci [r"f'{1000:j}'", 11787db96d56Sopenharmony_ci r"f'{1000:j}'", 11797db96d56Sopenharmony_ci ]) 11807db96d56Sopenharmony_ci 11817db96d56Sopenharmony_ci def test_filename_in_syntaxerror(self): 11827db96d56Sopenharmony_ci # see issue 38964 11837db96d56Sopenharmony_ci with temp_cwd() as cwd: 11847db96d56Sopenharmony_ci file_path = os.path.join(cwd, 't.py') 11857db96d56Sopenharmony_ci with open(file_path, 'w', encoding="utf-8") as f: 11867db96d56Sopenharmony_ci f.write('f"{a b}"') # This generates a SyntaxError 11877db96d56Sopenharmony_ci _, _, stderr = assert_python_failure(file_path, 11887db96d56Sopenharmony_ci PYTHONIOENCODING='ascii') 11897db96d56Sopenharmony_ci self.assertIn(file_path.encode('ascii', 'backslashreplace'), stderr) 11907db96d56Sopenharmony_ci 11917db96d56Sopenharmony_ci def test_loop(self): 11927db96d56Sopenharmony_ci for i in range(1000): 11937db96d56Sopenharmony_ci self.assertEqual(f'i:{i}', 'i:' + str(i)) 11947db96d56Sopenharmony_ci 11957db96d56Sopenharmony_ci def test_dict(self): 11967db96d56Sopenharmony_ci d = {'"': 'dquote', 11977db96d56Sopenharmony_ci "'": 'squote', 11987db96d56Sopenharmony_ci 'foo': 'bar', 11997db96d56Sopenharmony_ci } 12007db96d56Sopenharmony_ci self.assertEqual(f'''{d["'"]}''', 'squote') 12017db96d56Sopenharmony_ci self.assertEqual(f"""{d['"']}""", 'dquote') 12027db96d56Sopenharmony_ci 12037db96d56Sopenharmony_ci self.assertEqual(f'{d["foo"]}', 'bar') 12047db96d56Sopenharmony_ci self.assertEqual(f"{d['foo']}", 'bar') 12057db96d56Sopenharmony_ci 12067db96d56Sopenharmony_ci def test_backslash_char(self): 12077db96d56Sopenharmony_ci # Check eval of a backslash followed by a control char. 12087db96d56Sopenharmony_ci # See bpo-30682: this used to raise an assert in pydebug mode. 12097db96d56Sopenharmony_ci self.assertEqual(eval('f"\\\n"'), '') 12107db96d56Sopenharmony_ci self.assertEqual(eval('f"\\\r"'), '') 12117db96d56Sopenharmony_ci 12127db96d56Sopenharmony_ci def test_debug_conversion(self): 12137db96d56Sopenharmony_ci x = 'A string' 12147db96d56Sopenharmony_ci self.assertEqual(f'{x=}', 'x=' + repr(x)) 12157db96d56Sopenharmony_ci self.assertEqual(f'{x =}', 'x =' + repr(x)) 12167db96d56Sopenharmony_ci self.assertEqual(f'{x=!s}', 'x=' + str(x)) 12177db96d56Sopenharmony_ci self.assertEqual(f'{x=!r}', 'x=' + repr(x)) 12187db96d56Sopenharmony_ci self.assertEqual(f'{x=!a}', 'x=' + ascii(x)) 12197db96d56Sopenharmony_ci 12207db96d56Sopenharmony_ci x = 2.71828 12217db96d56Sopenharmony_ci self.assertEqual(f'{x=:.2f}', 'x=' + format(x, '.2f')) 12227db96d56Sopenharmony_ci self.assertEqual(f'{x=:}', 'x=' + format(x, '')) 12237db96d56Sopenharmony_ci self.assertEqual(f'{x=!r:^20}', 'x=' + format(repr(x), '^20')) 12247db96d56Sopenharmony_ci self.assertEqual(f'{x=!s:^20}', 'x=' + format(str(x), '^20')) 12257db96d56Sopenharmony_ci self.assertEqual(f'{x=!a:^20}', 'x=' + format(ascii(x), '^20')) 12267db96d56Sopenharmony_ci 12277db96d56Sopenharmony_ci x = 9 12287db96d56Sopenharmony_ci self.assertEqual(f'{3*x+15=}', '3*x+15=42') 12297db96d56Sopenharmony_ci 12307db96d56Sopenharmony_ci # There is code in ast.c that deals with non-ascii expression values. So, 12317db96d56Sopenharmony_ci # use a unicode identifier to trigger that. 12327db96d56Sopenharmony_ci tenπ = 31.4 12337db96d56Sopenharmony_ci self.assertEqual(f'{tenπ=:.2f}', 'tenπ=31.40') 12347db96d56Sopenharmony_ci 12357db96d56Sopenharmony_ci # Also test with Unicode in non-identifiers. 12367db96d56Sopenharmony_ci self.assertEqual(f'{"Σ"=}', '"Σ"=\'Σ\'') 12377db96d56Sopenharmony_ci 12387db96d56Sopenharmony_ci # Make sure nested fstrings still work. 12397db96d56Sopenharmony_ci self.assertEqual(f'{f"{3.1415=:.1f}":*^20}', '*****3.1415=3.1*****') 12407db96d56Sopenharmony_ci 12417db96d56Sopenharmony_ci # Make sure text before and after an expression with = works 12427db96d56Sopenharmony_ci # correctly. 12437db96d56Sopenharmony_ci pi = 'π' 12447db96d56Sopenharmony_ci self.assertEqual(f'alpha α {pi=} ω omega', "alpha α pi='π' ω omega") 12457db96d56Sopenharmony_ci 12467db96d56Sopenharmony_ci # Check multi-line expressions. 12477db96d56Sopenharmony_ci self.assertEqual(f'''{ 12487db96d56Sopenharmony_ci3 12497db96d56Sopenharmony_ci=}''', '\n3\n=3') 12507db96d56Sopenharmony_ci 12517db96d56Sopenharmony_ci # Since = is handled specially, make sure all existing uses of 12527db96d56Sopenharmony_ci # it still work. 12537db96d56Sopenharmony_ci 12547db96d56Sopenharmony_ci self.assertEqual(f'{0==1}', 'False') 12557db96d56Sopenharmony_ci self.assertEqual(f'{0!=1}', 'True') 12567db96d56Sopenharmony_ci self.assertEqual(f'{0<=1}', 'True') 12577db96d56Sopenharmony_ci self.assertEqual(f'{0>=1}', 'False') 12587db96d56Sopenharmony_ci self.assertEqual(f'{(x:="5")}', '5') 12597db96d56Sopenharmony_ci self.assertEqual(x, '5') 12607db96d56Sopenharmony_ci self.assertEqual(f'{(x:=5)}', '5') 12617db96d56Sopenharmony_ci self.assertEqual(x, 5) 12627db96d56Sopenharmony_ci self.assertEqual(f'{"="}', '=') 12637db96d56Sopenharmony_ci 12647db96d56Sopenharmony_ci x = 20 12657db96d56Sopenharmony_ci # This isn't an assignment expression, it's 'x', with a format 12667db96d56Sopenharmony_ci # spec of '=10'. See test_walrus: you need to use parens. 12677db96d56Sopenharmony_ci self.assertEqual(f'{x:=10}', ' 20') 12687db96d56Sopenharmony_ci 12697db96d56Sopenharmony_ci # Test named function parameters, to make sure '=' parsing works 12707db96d56Sopenharmony_ci # there. 12717db96d56Sopenharmony_ci def f(a): 12727db96d56Sopenharmony_ci nonlocal x 12737db96d56Sopenharmony_ci oldx = x 12747db96d56Sopenharmony_ci x = a 12757db96d56Sopenharmony_ci return oldx 12767db96d56Sopenharmony_ci x = 0 12777db96d56Sopenharmony_ci self.assertEqual(f'{f(a="3=")}', '0') 12787db96d56Sopenharmony_ci self.assertEqual(x, '3=') 12797db96d56Sopenharmony_ci self.assertEqual(f'{f(a=4)}', '3=') 12807db96d56Sopenharmony_ci self.assertEqual(x, 4) 12817db96d56Sopenharmony_ci 12827db96d56Sopenharmony_ci # Make sure __format__ is being called. 12837db96d56Sopenharmony_ci class C: 12847db96d56Sopenharmony_ci def __format__(self, s): 12857db96d56Sopenharmony_ci return f'FORMAT-{s}' 12867db96d56Sopenharmony_ci def __repr__(self): 12877db96d56Sopenharmony_ci return 'REPR' 12887db96d56Sopenharmony_ci 12897db96d56Sopenharmony_ci self.assertEqual(f'{C()=}', 'C()=REPR') 12907db96d56Sopenharmony_ci self.assertEqual(f'{C()=!r}', 'C()=REPR') 12917db96d56Sopenharmony_ci self.assertEqual(f'{C()=:}', 'C()=FORMAT-') 12927db96d56Sopenharmony_ci self.assertEqual(f'{C()=: }', 'C()=FORMAT- ') 12937db96d56Sopenharmony_ci self.assertEqual(f'{C()=:x}', 'C()=FORMAT-x') 12947db96d56Sopenharmony_ci self.assertEqual(f'{C()=!r:*^20}', 'C()=********REPR********') 12957db96d56Sopenharmony_ci 12967db96d56Sopenharmony_ci self.assertRaises(SyntaxError, eval, "f'{C=]'") 12977db96d56Sopenharmony_ci 12987db96d56Sopenharmony_ci # Make sure leading and following text works. 12997db96d56Sopenharmony_ci x = 'foo' 13007db96d56Sopenharmony_ci self.assertEqual(f'X{x=}Y', 'Xx='+repr(x)+'Y') 13017db96d56Sopenharmony_ci 13027db96d56Sopenharmony_ci # Make sure whitespace around the = works. 13037db96d56Sopenharmony_ci self.assertEqual(f'X{x =}Y', 'Xx ='+repr(x)+'Y') 13047db96d56Sopenharmony_ci self.assertEqual(f'X{x= }Y', 'Xx= '+repr(x)+'Y') 13057db96d56Sopenharmony_ci self.assertEqual(f'X{x = }Y', 'Xx = '+repr(x)+'Y') 13067db96d56Sopenharmony_ci 13077db96d56Sopenharmony_ci # These next lines contains tabs. Backslash escapes don't 13087db96d56Sopenharmony_ci # work in f-strings. 13097db96d56Sopenharmony_ci # patchcheck doesn't like these tabs. So the only way to test 13107db96d56Sopenharmony_ci # this will be to dynamically created and exec the f-strings. But 13117db96d56Sopenharmony_ci # that's such a hassle I'll save it for another day. For now, convert 13127db96d56Sopenharmony_ci # the tabs to spaces just to shut up patchcheck. 13137db96d56Sopenharmony_ci #self.assertEqual(f'X{x =}Y', 'Xx\t='+repr(x)+'Y') 13147db96d56Sopenharmony_ci #self.assertEqual(f'X{x = }Y', 'Xx\t=\t'+repr(x)+'Y') 13157db96d56Sopenharmony_ci 13167db96d56Sopenharmony_ci def test_walrus(self): 13177db96d56Sopenharmony_ci x = 20 13187db96d56Sopenharmony_ci # This isn't an assignment expression, it's 'x', with a format 13197db96d56Sopenharmony_ci # spec of '=10'. 13207db96d56Sopenharmony_ci self.assertEqual(f'{x:=10}', ' 20') 13217db96d56Sopenharmony_ci 13227db96d56Sopenharmony_ci # This is an assignment expression, which requires parens. 13237db96d56Sopenharmony_ci self.assertEqual(f'{(x:=10)}', '10') 13247db96d56Sopenharmony_ci self.assertEqual(x, 10) 13257db96d56Sopenharmony_ci 13267db96d56Sopenharmony_ci def test_invalid_syntax_error_message(self): 13277db96d56Sopenharmony_ci with self.assertRaisesRegex(SyntaxError, "f-string: invalid syntax"): 13287db96d56Sopenharmony_ci compile("f'{a $ b}'", "?", "exec") 13297db96d56Sopenharmony_ci 13307db96d56Sopenharmony_ci def test_with_two_commas_in_format_specifier(self): 13317db96d56Sopenharmony_ci error_msg = re.escape("Cannot specify ',' with ','.") 13327db96d56Sopenharmony_ci with self.assertRaisesRegex(ValueError, error_msg): 13337db96d56Sopenharmony_ci f'{1:,,}' 13347db96d56Sopenharmony_ci 13357db96d56Sopenharmony_ci def test_with_two_underscore_in_format_specifier(self): 13367db96d56Sopenharmony_ci error_msg = re.escape("Cannot specify '_' with '_'.") 13377db96d56Sopenharmony_ci with self.assertRaisesRegex(ValueError, error_msg): 13387db96d56Sopenharmony_ci f'{1:__}' 13397db96d56Sopenharmony_ci 13407db96d56Sopenharmony_ci def test_with_a_commas_and_an_underscore_in_format_specifier(self): 13417db96d56Sopenharmony_ci error_msg = re.escape("Cannot specify both ',' and '_'.") 13427db96d56Sopenharmony_ci with self.assertRaisesRegex(ValueError, error_msg): 13437db96d56Sopenharmony_ci f'{1:,_}' 13447db96d56Sopenharmony_ci 13457db96d56Sopenharmony_ci def test_with_an_underscore_and_a_comma_in_format_specifier(self): 13467db96d56Sopenharmony_ci error_msg = re.escape("Cannot specify both ',' and '_'.") 13477db96d56Sopenharmony_ci with self.assertRaisesRegex(ValueError, error_msg): 13487db96d56Sopenharmony_ci f'{1:_,}' 13497db96d56Sopenharmony_ci 13507db96d56Sopenharmony_ci def test_syntax_error_for_starred_expressions(self): 13517db96d56Sopenharmony_ci error_msg = re.escape("cannot use starred expression here") 13527db96d56Sopenharmony_ci with self.assertRaisesRegex(SyntaxError, error_msg): 13537db96d56Sopenharmony_ci compile("f'{*a}'", "?", "exec") 13547db96d56Sopenharmony_ci 13557db96d56Sopenharmony_ci error_msg = re.escape("cannot use double starred expression here") 13567db96d56Sopenharmony_ci with self.assertRaisesRegex(SyntaxError, error_msg): 13577db96d56Sopenharmony_ci compile("f'{**a}'", "?", "exec") 13587db96d56Sopenharmony_ci 13597db96d56Sopenharmony_ciif __name__ == '__main__': 13607db96d56Sopenharmony_ci unittest.main() 1361