lib-python/3/test/test_unparse.py::unmodified
== PyPy 3.12.13 (a3c6b428e7a8, Jul 09 2026, 07:59:28) [PyPy 8.0.0-alpha0 with GCC Apple LLVM 16.0.0 (clang-1600.0.26.6)]
== macOS-15.7.7-x86_64-i386-64bit little-endian
== Python build: release with_assert
== cwd: /private/tmp/buildbot-x86_64/pytest/test_python_worker_65130æ
== CPU count: 8
== encodings: locale=UTF-8 FS=utf-8
== resources (3): curses,gui,network
Using random seed: 4170502206
Raised RLIMIT_NOFILE: 256 -> 1024
0:00:00 load avg: 1.41 Run 1 test sequentially in a single process
0:00:00 load avg: 1.41 [1/1] test_unparse
test_backslash_in_format_spec (test.test_unparse.CosmeticTestCase.test_backslash_in_format_spec) ... ok
test_class_bases_and_keywords (test.test_unparse.CosmeticTestCase.test_class_bases_and_keywords) ... ok
test_docstrings (test.test_unparse.CosmeticTestCase.test_docstrings) ... ok
test_docstrings_negative_cases (test.test_unparse.CosmeticTestCase.test_docstrings_negative_cases) ... ok
test_fstrings (test.test_unparse.CosmeticTestCase.test_fstrings) ... ok
test_lambda_parameters (test.test_unparse.CosmeticTestCase.test_lambda_parameters) ... ok
test_multiquote_joined_string (test.test_unparse.CosmeticTestCase.test_multiquote_joined_string) ...
test_multiquote_joined_string (test.test_unparse.CosmeticTestCase.test_multiquote_joined_string) (code1='f\'"""{1}\'\'\' ', ast_parse_kwargs={}) ... FAIL
test_multiquote_joined_string (test.test_unparse.CosmeticTestCase.test_multiquote_joined_string) (code1='f\'"""{"\\n"}\'\'\' ', ast_parse_kwargs={}) ... FAIL
test_quote_in_format_spec (test.test_unparse.CosmeticTestCase.test_quote_in_format_spec) ... ok
test_simple_expressions_parens (test.test_unparse.CosmeticTestCase.test_simple_expressions_parens) ... ok
test_slices (test.test_unparse.CosmeticTestCase.test_slices) ... ok
test_star_expr_assign_target (test.test_unparse.CosmeticTestCase.test_star_expr_assign_target) ... ok
test_star_expr_assign_target_multiple (test.test_unparse.CosmeticTestCase.test_star_expr_assign_target_multiple) ... ok
test_unary_op_factor (test.test_unparse.CosmeticTestCase.test_unary_op_factor) ... ok
test_files (test.test_unparse.DirectoryTestCase.test_files) ... Testing /Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/test_dbm.py
Testing /Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/pathlib.py
Testing /Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/mp_fork_bomb.py
Testing /Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/test_dbm_dumb.py
Testing /Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/test_urllib2net.py
Testing /Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/cgi.py
Testing /Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/test_unpack_ex.py
Testing /Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/test_isinstance.py
Testing /Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/ssl_servers.py
Testing /Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/test_pyclbr.py
Testing /Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/test_fstring.py
test_files (test.test_unparse.DirectoryTestCase.test_files) (code1='# -*- coding: utf-8 -*-\n# There are tests here with unicode string literals and\n# identifiers. There\'s a code in ast.c that was added because of a\n# failure with a non-ascii-only expression. So, I have tests for\n# that. There are workarounds that would let me run tests for that\n# code without unicode identifiers and strings, but just using them\n# directly seems like the easiest and therefore safest thing to do.\n# Unicode identifiers in tests is allowed by PEP 3131.\n\nimport ast\nimport datetime\nimport os\nimport re\nimport types\nimport decimal\nimport unittest\nimport warnings\nfrom test import support\nfrom test.support.os_helper import temp_cwd\nfrom test.support.script_helper import assert_python_failure, assert_python_ok\n\na_global = "global variable"\n\n# You could argue that I\'m too strict in looking for specific error\n# values with assertRaisesRegex, but without it it\'s way too easy to\n# make a syntax error in the test strings. Especially with all of the\n# triple quotes, raw strings, backslashes, etc. I think it\'s a\n# worthwhile tradeoff. When I switched to this method, I found many\n# examples where I wasn\'t testing what I thought I was.\n\n\nclass TestCase(unittest.TestCase):\n def assertAllRaise(self, exception_type, regex, error_strings):\n for str in error_strings:\n with self.subTest(str=str):\n with self.assertRaisesRegex(exception_type, regex):\n eval(str)\n\n def test__format__lookup(self):\n # Make sure __format__ is looked up on the type, not the instance.\n class X:\n def __format__(self, spec):\n return "class"\n\n x = X()\n\n # Add a bound __format__ method to the \'y\' instance, but not\n # the \'x\' instance.\n y = X()\n y.__format__ = types.MethodType(lambda self, spec: "instance", y)\n\n self.assertEqual(f"{y}", format(y))\n self.assertEqual(f"{y}", "class")\n self.assertEqual(format(x), format(y))\n\n # __format__ is not called this way, but still make sure it\n # returns what we expect (so we can make sure we\'re bypassing\n # it).\n self.assertEqual(x.__format__(""), "class")\n self.assertEqual(y.__format__(""), "instance")\n\n # This is how __format__ is actually called.\n self.assertEqual(type(x).__format__(x, ""), "class")\n self.assertEqual(type(y).__format__(y, ""), "class")\n\n def test_ast(self):\n # Inspired by http://bugs.python.org/issue24975\n class X:\n def __init__(self):\n self.called = False\n\n def __call__(self):\n self.called = True\n return 4\n\n x = X()\n expr = """\na = 10\nf\'{a * x()}\'"""\n t = ast.parse(expr)\n c = compile(t, "", "exec")\n\n # Make sure x was not called.\n self.assertFalse(x.called)\n\n # Actually run the code.\n exec(c)\n\n # Make sure x was called.\n self.assertTrue(x.called)\n\n def test_ast_line_numbers(self):\n expr = """\na = 10\nf\'{a * x()}\'"""\n t = ast.parse(expr)\n self.assertEqual(type(t), ast.Module)\n self.assertEqual(len(t.body), 2)\n # check `a = 10`\n self.assertEqual(type(t.body[0]), ast.Assign)\n self.assertEqual(t.body[0].lineno, 2)\n # check `f\'...\'`\n self.assertEqual(type(t.body[1]), ast.Expr)\n self.assertEqual(type(t.body[1].value), ast.JoinedStr)\n self.assertEqual(len(t.body[1].value.values), 1)\n self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)\n self.assertEqual(t.body[1].lineno, 3)\n self.assertEqual(t.body[1].value.lineno, 3)\n self.assertEqual(t.body[1].value.values[0].lineno, 3)\n # check the binop location\n binop = t.body[1].value.values[0].value\n self.assertEqual(type(binop), ast.BinOp)\n self.assertEqual(type(binop.left), ast.Name)\n self.assertEqual(type(binop.op), ast.Mult)\n self.assertEqual(type(binop.right), ast.Call)\n self.assertEqual(binop.lineno, 3)\n self.assertEqual(binop.left.lineno, 3)\n self.assertEqual(binop.right.lineno, 3)\n self.assertEqual(binop.col_offset, 3)\n self.assertEqual(binop.left.col_offset, 3)\n self.assertEqual(binop.right.col_offset, 7)\n\n def test_ast_line_numbers_multiple_formattedvalues(self):\n expr = """\nf\'no formatted values\'\nf\'eggs {a * x()} spam {b + y()}\'"""\n t = ast.parse(expr)\n self.assertEqual(type(t), ast.Module)\n self.assertEqual(len(t.body), 2)\n # check `f\'no formatted value\'`\n self.assertEqual(type(t.body[0]), ast.Expr)\n self.assertEqual(type(t.body[0].value), ast.JoinedStr)\n self.assertEqual(t.body[0].lineno, 2)\n # check `f\'...\'`\n self.assertEqual(type(t.body[1]), ast.Expr)\n self.assertEqual(type(t.body[1].value), ast.JoinedStr)\n self.assertEqual(len(t.body[1].value.values), 4)\n self.assertEqual(type(t.body[1].value.values[0]), ast.Constant)\n self.assertEqual(type(t.body[1].value.values[0].value), str)\n self.assertEqual(type(t.body[1].value.values[1]), ast.FormattedValue)\n self.assertEqual(type(t.body[1].value.values[2]), ast.Constant)\n self.assertEqual(type(t.body[1].value.values[2].value), str)\n self.assertEqual(type(t.body[1].value.values[3]), ast.FormattedValue)\n self.assertEqual(t.body[1].lineno, 3)\n self.assertEqual(t.body[1].value.lineno, 3)\n self.assertEqual(t.body[1].value.values[0].lineno, 3)\n self.assertEqual(t.body[1].value.values[1].lineno, 3)\n self.assertEqual(t.body[1].value.values[2].lineno, 3)\n self.assertEqual(t.body[1].value.values[3].lineno, 3)\n # check the first binop location\n binop1 = t.body[1].value.values[1].value\n self.assertEqual(type(binop1), ast.BinOp)\n self.assertEqual(type(binop1.left), ast.Name)\n self.assertEqual(type(binop1.op), ast.Mult)\n self.assertEqual(type(binop1.right), ast.Call)\n self.assertEqual(binop1.lineno, 3)\n self.assertEqual(binop1.left.lineno, 3)\n self.assertEqual(binop1.right.lineno, 3)\n self.assertEqual(binop1.col_offset, 8)\n self.assertEqual(binop1.left.col_offset, 8)\n self.assertEqual(binop1.right.col_offset, 12)\n # check the second binop location\n binop2 = t.body[1].value.values[3].value\n self.assertEqual(type(binop2), ast.BinOp)\n self.assertEqual(type(binop2.left), ast.Name)\n self.assertEqual(type(binop2.op), ast.Add)\n self.assertEqual(type(binop2.right), ast.Call)\n self.assertEqual(binop2.lineno, 3)\n self.assertEqual(binop2.left.lineno, 3)\n self.assertEqual(binop2.right.lineno, 3)\n self.assertEqual(binop2.col_offset, 23)\n self.assertEqual(binop2.left.col_offset, 23)\n self.assertEqual(binop2.right.col_offset, 27)\n\n def test_ast_line_numbers_nested(self):\n expr = """\na = 10\nf\'{a * f"-{x()}-"}\'"""\n t = ast.parse(expr)\n self.assertEqual(type(t), ast.Module)\n self.assertEqual(len(t.body), 2)\n # check `a = 10`\n self.assertEqual(type(t.body[0]), ast.Assign)\n self.assertEqual(t.body[0].lineno, 2)\n # check `f\'...\'`\n self.assertEqual(type(t.body[1]), ast.Expr)\n self.assertEqual(type(t.body[1].value), ast.JoinedStr)\n self.assertEqual(len(t.body[1].value.values), 1)\n self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)\n self.assertEqual(t.body[1].lineno, 3)\n self.assertEqual(t.body[1].value.lineno, 3)\n self.assertEqual(t.body[1].value.values[0].lineno, 3)\n # check the binop location\n binop = t.body[1].value.values[0].value\n self.assertEqual(type(binop), ast.BinOp)\n self.assertEqual(type(binop.left), ast.Name)\n self.assertEqual(type(binop.op), ast.Mult)\n self.assertEqual(type(binop.right), ast.JoinedStr)\n self.assertEqual(binop.lineno, 3)\n self.assertEqual(binop.left.lineno, 3)\n self.assertEqual(binop.right.lineno, 3)\n self.assertEqual(binop.col_offset, 3)\n self.assertEqual(binop.left.col_offset, 3)\n self.assertEqual(binop.right.col_offset, 7)\n # check the nested call location\n self.assertEqual(len(binop.right.values), 3)\n self.assertEqual(type(binop.right.values[0]), ast.Constant)\n self.assertEqual(type(binop.right.values[0].value), str)\n self.assertEqual(type(binop.right.values[1]), ast.FormattedValue)\n self.assertEqual(type(binop.right.values[2]), ast.Constant)\n self.assertEqual(type(binop.right.values[2].value), str)\n self.assertEqual(binop.right.values[0].lineno, 3)\n self.assertEqual(binop.right.values[1].lineno, 3)\n self.assertEqual(binop.right.values[2].lineno, 3)\n call = binop.right.values[1].value\n self.assertEqual(type(call), ast.Call)\n self.assertEqual(call.lineno, 3)\n self.assertEqual(call.col_offset, 11)\n\n def test_ast_line_numbers_duplicate_expression(self):\n expr = """\na = 10\nf\'{a * x()} {a * x()} {a * x()}\'\n"""\n t = ast.parse(expr)\n self.assertEqual(type(t), ast.Module)\n self.assertEqual(len(t.body), 2)\n # check `a = 10`\n self.assertEqual(type(t.body[0]), ast.Assign)\n self.assertEqual(t.body[0].lineno, 2)\n # check `f\'...\'`\n self.assertEqual(type(t.body[1]), ast.Expr)\n self.assertEqual(type(t.body[1].value), ast.JoinedStr)\n self.assertEqual(len(t.body[1].value.values), 5)\n self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)\n self.assertEqual(type(t.body[1].value.values[1]), ast.Constant)\n self.assertEqual(type(t.body[1].value.values[1].value), str)\n self.assertEqual(type(t.body[1].value.values[2]), ast.FormattedValue)\n self.assertEqual(type(t.body[1].value.values[3]), ast.Constant)\n self.assertEqual(type(t.body[1].value.values[3].value), str)\n self.assertEqual(type(t.body[1].value.values[4]), ast.FormattedValue)\n self.assertEqual(t.body[1].lineno, 3)\n self.assertEqual(t.body[1].value.lineno, 3)\n self.assertEqual(t.body[1].value.values[0].lineno, 3)\n self.assertEqual(t.body[1].value.values[1].lineno, 3)\n self.assertEqual(t.body[1].value.values[2].lineno, 3)\n self.assertEqual(t.body[1].value.values[3].lineno, 3)\n self.assertEqual(t.body[1].value.values[4].lineno, 3)\n # check the first binop location\n binop = t.body[1].value.values[0].value\n self.assertEqual(type(binop), ast.BinOp)\n self.assertEqual(type(binop.left), ast.Name)\n self.assertEqual(type(binop.op), ast.Mult)\n self.assertEqual(type(binop.right), ast.Call)\n self.assertEqual(binop.lineno, 3)\n self.assertEqual(binop.left.lineno, 3)\n self.assertEqual(binop.right.lineno, 3)\n self.assertEqual(binop.col_offset, 3)\n self.assertEqual(binop.left.col_offset, 3)\n self.assertEqual(binop.right.col_offset, 7)\n # check the second binop location\n binop = t.body[1].value.values[2].value\n self.assertEqual(type(binop), ast.BinOp)\n self.assertEqual(type(binop.left), ast.Name)\n self.assertEqual(type(binop.op), ast.Mult)\n self.assertEqual(type(binop.right), ast.Call)\n self.assertEqual(binop.lineno, 3)\n self.assertEqual(binop.left.lineno, 3)\n self.assertEqual(binop.right.lineno, 3)\n self.assertEqual(binop.col_offset, 13)\n self.assertEqual(binop.left.col_offset, 13)\n self.assertEqual(binop.right.col_offset, 17)\n # check the third binop location\n binop = t.body[1].value.values[4].value\n self.assertEqual(type(binop), ast.BinOp)\n self.assertEqual(type(binop.left), ast.Name)\n self.assertEqual(type(binop.op), ast.Mult)\n self.assertEqual(type(binop.right), ast.Call)\n self.assertEqual(binop.lineno, 3)\n self.assertEqual(binop.left.lineno, 3)\n self.assertEqual(binop.right.lineno, 3)\n self.assertEqual(binop.col_offset, 23)\n self.assertEqual(binop.left.col_offset, 23)\n self.assertEqual(binop.right.col_offset, 27)\n\n def test_ast_numbers_fstring_with_formatting(self):\n t = ast.parse(\'f"Here is that pesky {xxx:.3f} again"\')\n self.assertEqual(len(t.body), 1)\n self.assertEqual(t.body[0].lineno, 1)\n\n self.assertEqual(type(t.body[0]), ast.Expr)\n self.assertEqual(type(t.body[0].value), ast.JoinedStr)\n self.assertEqual(len(t.body[0].value.values), 3)\n\n self.assertEqual(type(t.body[0].value.values[0]), ast.Constant)\n self.assertEqual(type(t.body[0].value.values[1]), ast.FormattedValue)\n self.assertEqual(type(t.body[0].value.values[2]), ast.Constant)\n\n _, expr, _ = t.body[0].value.values\n\n name = expr.value\n self.assertEqual(type(name), ast.Name)\n self.assertEqual(name.lineno, 1)\n self.assertEqual(name.end_lineno, 1)\n self.assertEqual(name.col_offset, 22)\n self.assertEqual(name.end_col_offset, 25)\n\n def test_ast_line_numbers_multiline_fstring(self):\n # See bpo-30465 for details.\n expr = """\na = 10\nf\'\'\'\n {a\n *\n x()}\nnon-important content\n\'\'\'\n"""\n t = ast.parse(expr)\n self.assertEqual(type(t), ast.Module)\n self.assertEqual(len(t.body), 2)\n # check `a = 10`\n self.assertEqual(type(t.body[0]), ast.Assign)\n self.assertEqual(t.body[0].lineno, 2)\n # check `f\'...\'`\n self.assertEqual(type(t.body[1]), ast.Expr)\n self.assertEqual(type(t.body[1].value), ast.JoinedStr)\n self.assertEqual(len(t.body[1].value.values), 3)\n self.assertEqual(type(t.body[1].value.values[0]), ast.Constant)\n self.assertEqual(type(t.body[1].value.values[0].value), str)\n self.assertEqual(type(t.body[1].value.values[1]), ast.FormattedValue)\n self.assertEqual(type(t.body[1].value.values[2]), ast.Constant)\n self.assertEqual(type(t.body[1].value.values[2].value), str)\n self.assertEqual(t.body[1].lineno, 3)\n self.assertEqual(t.body[1].value.lineno, 3)\n self.assertEqual(t.body[1].value.values[0].lineno, 3)\n self.assertEqual(t.body[1].value.values[1].lineno, 4)\n self.assertEqual(t.body[1].value.values[2].lineno, 6)\n self.assertEqual(t.body[1].col_offset, 0)\n self.assertEqual(t.body[1].value.col_offset, 0)\n self.assertEqual(t.body[1].value.values[0].col_offset, 4)\n self.assertEqual(t.body[1].value.values[1].col_offset, 2)\n self.assertEqual(t.body[1].value.values[2].col_offset, 11)\n # NOTE: the following lineno information and col_offset is correct for\n # expressions within FormattedValues.\n binop = t.body[1].value.values[1].value\n self.assertEqual(type(binop), ast.BinOp)\n self.assertEqual(type(binop.left), ast.Name)\n self.assertEqual(type(binop.op), ast.Mult)\n self.assertEqual(type(binop.right), ast.Call)\n self.assertEqual(binop.lineno, 4)\n self.assertEqual(binop.left.lineno, 4)\n self.assertEqual(binop.right.lineno, 6)\n self.assertEqual(binop.col_offset, 3)\n self.assertEqual(binop.left.col_offset, 3)\n self.assertEqual(binop.right.col_offset, 7)\n\n expr = """\na = f\'\'\'\n {blech}\n \'\'\'\n"""\n t = ast.parse(expr)\n self.assertEqual(type(t), ast.Module)\n self.assertEqual(len(t.body), 1)\n # Check f\'...\'\n self.assertEqual(type(t.body[0]), ast.Assign)\n self.assertEqual(type(t.body[0].value), ast.JoinedStr)\n self.assertEqual(len(t.body[0].value.values), 3)\n self.assertEqual(type(t.body[0].value.values[1]), ast.FormattedValue)\n self.assertEqual(t.body[0].lineno, 2)\n self.assertEqual(t.body[0].value.lineno, 2)\n self.assertEqual(t.body[0].value.values[0].lineno, 2)\n self.assertEqual(t.body[0].value.values[1].lineno, 3)\n self.assertEqual(t.body[0].value.values[2].lineno, 3)\n self.assertEqual(t.body[0].col_offset, 0)\n self.assertEqual(t.body[0].value.col_offset, 4)\n self.assertEqual(t.body[0].value.values[0].col_offset, 8)\n self.assertEqual(t.body[0].value.values[1].col_offset, 10)\n self.assertEqual(t.body[0].value.values[2].col_offset, 17)\n # Check {blech}\n self.assertEqual(t.body[0].value.values[1].value.lineno, 3)\n self.assertEqual(t.body[0].value.values[1].value.end_lineno, 3)\n self.assertEqual(t.body[0].value.values[1].value.col_offset, 11)\n self.assertEqual(t.body[0].value.values[1].value.end_col_offset, 16)\n\n def test_ast_line_numbers_with_parentheses(self):\n expr = """\nx = (\n f" {test(t)}"\n)"""\n t = ast.parse(expr)\n self.assertEqual(type(t), ast.Module)\n self.assertEqual(len(t.body), 1)\n # check the joinedstr location\n joinedstr = t.body[0].value\n self.assertEqual(type(joinedstr), ast.JoinedStr)\n self.assertEqual(joinedstr.lineno, 3)\n self.assertEqual(joinedstr.end_lineno, 3)\n self.assertEqual(joinedstr.col_offset, 4)\n self.assertEqual(joinedstr.end_col_offset, 17)\n # check the formatted value location\n fv = t.body[0].value.values[1]\n self.assertEqual(type(fv), ast.FormattedValue)\n self.assertEqual(fv.lineno, 3)\n self.assertEqual(fv.end_lineno, 3)\n self.assertEqual(fv.col_offset, 7)\n self.assertEqual(fv.end_col_offset, 16)\n # check the test(t) location\n call = t.body[0].value.values[1].value\n self.assertEqual(type(call), ast.Call)\n self.assertEqual(call.lineno, 3)\n self.assertEqual(call.end_lineno, 3)\n self.assertEqual(call.col_offset, 8)\n self.assertEqual(call.end_col_offset, 15)\n\n expr = """\nx = (\n u\'wat\',\n u"wat",\n b\'wat\',\n b"wat",\n f\'wat\',\n f"wat",\n)\n\ny = (\n u\'\'\'wat\'\'\',\n u\\"\\"\\"wat\\"\\"\\",\n b\'\'\'wat\'\'\',\n b\\"\\"\\"wat\\"\\"\\",\n f\'\'\'wat\'\'\',\n f\\"\\"\\"wat\\"\\"\\",\n)\n """\n t = ast.parse(expr)\n self.assertEqual(type(t), ast.Module)\n self.assertEqual(len(t.body), 2)\n x, y = t.body\n\n # Check the single quoted string offsets first.\n offsets = [(elt.col_offset, elt.end_col_offset) for elt in x.value.elts]\n self.assertTrue(all(offset == (4, 10) for offset in offsets))\n\n # Check the triple quoted string offsets.\n offsets = [(elt.col_offset, elt.end_col_offset) for elt in y.value.elts]\n self.assertTrue(all(offset == (4, 14) for offset in offsets))\n\n expr = """\nx = (\n \'PERL_MM_OPT\', (\n f\'wat\'\n f\'some_string={f(x)} \'\n f\'wat\'\n ),\n)\n"""\n t = ast.parse(expr)\n self.assertEqual(type(t), ast.Module)\n self.assertEqual(len(t.body), 1)\n # check the fstring\n fstring = t.body[0].value.elts[1]\n self.assertEqual(type(fstring), ast.JoinedStr)\n self.assertEqual(len(fstring.values), 3)\n wat1, middle, wat2 = fstring.values\n # check the first wat\n self.assertEqual(type(wat1), ast.Constant)\n self.assertEqual(wat1.lineno, 4)\n self.assertEqual(wat1.end_lineno, 5)\n self.assertEqual(wat1.col_offset, 14)\n self.assertEqual(wat1.end_col_offset, 26)\n # check the call\n call = middle.value\n self.assertEqual(type(call), ast.Call)\n self.assertEqual(call.lineno, 5)\n self.assertEqual(call.end_lineno, 5)\n self.assertEqual(call.col_offset, 27)\n self.assertEqual(call.end_col_offset, 31)\n # check the second wat\n self.assertEqual(type(wat2), ast.Constant)\n self.assertEqual(wat2.lineno, 5)\n self.assertEqual(wat2.end_lineno, 6)\n self.assertEqual(wat2.col_offset, 32)\n # wat ends at the offset 17, but the whole f-string\n # ends at the offset 18 (since the quote is part of the\n # f-string but not the wat string)\n self.assertEqual(wat2.end_col_offset, 17)\n self.assertEqual(fstring.end_col_offset, 18)\n\n def test_ast_fstring_empty_format_spec(self):\n expr = "f\'{expr:}\'"\n\n mod = ast.parse(expr)\n self.assertEqual(type(mod), ast.Module)\n self.assertEqual(len(mod.body), 1)\n\n fstring = mod.body[0].value\n self.assertEqual(type(fstring), ast.JoinedStr)\n self.assertEqual(len(fstring.values), 1)\n\n fv = fstring.values[0]\n self.assertEqual(type(fv), ast.FormattedValue)\n\n format_spec = fv.format_spec\n self.assertEqual(type(format_spec), ast.JoinedStr)\n self.assertEqual(len(format_spec.values), 0)\n\n def test_docstring(self):\n def f():\n f"""Not a docstring"""\n\n self.assertIsNone(f.__doc__)\n\n def g():\n """Not a docstring""" f""\n\n self.assertIsNone(g.__doc__)\n\n def test_literal_eval(self):\n with self.assertRaisesRegex(ValueError, "malformed node or string"):\n ast.literal_eval("f\'x\'")\n\n def test_ast_compile_time_concat(self):\n x = [""]\n\n expr = """x[0] = \'foo\' f\'{3}\'"""\n t = ast.parse(expr)\n c = compile(t, "", "exec")\n exec(c)\n self.assertEqual(x[0], "foo3")\n\n def test_compile_time_concat_errors(self):\n self.assertAllRaise(\n SyntaxError,\n "cannot mix bytes and nonbytes literals",\n [\n r"""f\'\' b\'\'""",\n r"""b\'\' f\'\'""",\n ],\n )\n\n def test_literal(self):\n self.assertEqual(f"", "")\n self.assertEqual(f"a", "a")\n self.assertEqual(f" ", " ")\n\n def test_unterminated_string(self):\n self.assertAllRaise(\n SyntaxError,\n "unterminated string",\n [\n r"""f\'{"x\'""",\n r"""f\'{"x}\'""",\n r"""f\'{("x\'""",\n r"""f\'{("x}\'""",\n ],\n )\n\n @unittest.skipIf(support.is_wasi, "exhausts limited stack on WASI")\n def test_mismatched_parens(self):\n self.assertAllRaise(\n SyntaxError,\n r"closing parenthesis \'\\}\' " r"does not match opening parenthesis \'\\(\'",\n [\n "f\'{((}\'",\n ],\n )\n self.assertAllRaise(\n SyntaxError,\n r"closing parenthesis \'\\)\' " r"does not match opening parenthesis \'\\[\'",\n [\n "f\'{a[4)}\'",\n ],\n )\n self.assertAllRaise(\n SyntaxError,\n r"closing parenthesis \'\\]\' " r"does not match opening parenthesis \'\\(\'",\n [\n "f\'{a(4]}\'",\n ],\n )\n self.assertAllRaise(\n SyntaxError,\n r"closing parenthesis \'\\}\' " r"does not match opening parenthesis \'\\[\'",\n [\n "f\'{a[4}\'",\n ],\n )\n self.assertAllRaise(\n SyntaxError,\n r"closing parenthesis \'\\}\' " r"does not match opening parenthesis \'\\(\'",\n [\n "f\'{a(4}\'",\n ],\n )\n self.assertRaises(SyntaxError, eval, "f\'{" + "(" * 500 + "}\'")\n\n @unittest.skipIf(support.is_wasi, "exhausts limited stack on WASI")\n def test_fstring_nested_too_deeply(self):\n self.assertAllRaise(\n SyntaxError,\n "f-string: expressions nested too deeply",\n [\'f"{1+2:{1+2:{1+1:{1}}}}"\'],\n )\n\n def create_nested_fstring(n):\n if n == 0:\n return "1+1"\n prev = create_nested_fstring(n - 1)\n return f\'f"{{{prev}}}"\'\n\n self.assertAllRaise(\n SyntaxError, "too many nested f-strings", [create_nested_fstring(160)]\n )\n\n def test_syntax_error_in_nested_fstring(self):\n # See gh-104016 for more information on this crash\n self.assertAllRaise(\n SyntaxError, "invalid syntax", [\'f"{1 1:\' + (\'{f"1:\' * 199)]\n )\n\n def test_double_braces(self):\n self.assertEqual(f"{{", "{")\n self.assertEqual(f"a{{", "a{")\n self.assertEqual(f"{{b", "{b")\n self.assertEqual(f"a{{b", "a{b")\n self.assertEqual(f"}}", "}")\n self.assertEqual(f"a}}", "a}")\n self.assertEqual(f"}}b", "}b")\n self.assertEqual(f"a}}b", "a}b")\n self.assertEqual(f"{{}}", "{}")\n self.assertEqual(f"a{{}}", "a{}")\n self.assertEqual(f"{{b}}", "{b}")\n self.assertEqual(f"{{}}c", "{}c")\n self.assertEqual(f"a{{b}}", "a{b}")\n self.assertEqual(f"a{{}}c", "a{}c")\n self.assertEqual(f"{{b}}c", "{b}c")\n self.assertEqual(f"a{{b}}c", "a{b}c")\n\n self.assertEqual(f"{{{10}", "{10")\n self.assertEqual(f"}}{10}", "}10")\n self.assertEqual(f"}}{{{10}", "}{10")\n self.assertEqual(f"}}a{{{10}", "}a{10")\n\n self.assertEqual(f"{10}{{", "10{")\n self.assertEqual(f"{10}}}", "10}")\n self.assertEqual(f"{10}}}{{", "10}{")\n self.assertEqual(f"{10}}}a{{" "}", "10}a{}")\n\n # Inside of strings, don\'t interpret doubled brackets.\n self.assertEqual(f\'{"{{}}"}\', "{{}}")\n\n self.assertAllRaise(\n TypeError,\n "unhashable type",\n [\n "f\'{ {{}} }\'", # dict in a set\n ],\n )\n\n def test_compile_time_concat(self):\n x = "def"\n self.assertEqual("abc" f"## {x}ghi", "abc## defghi")\n self.assertEqual("abc" f"{x}" "ghi", "abcdefghi")\n self.assertEqual("abc" f"{x}" "gh" f"i{x:4}", "abcdefghidef ")\n self.assertEqual("{x}" f"{x}", "{x}def")\n self.assertEqual("{x" f"{x}", "{xdef")\n self.assertEqual("{x}" f"{x}", "{x}def")\n self.assertEqual("{{x}}" f"{x}", "{{x}}def")\n self.assertEqual("{{x" f"{x}", "{{xdef")\n self.assertEqual("x}}" f"{x}", "x}}def")\n self.assertEqual(f"{x}" "x}}", "defx}}")\n self.assertEqual(f"{x}" "", "def")\n self.assertEqual("" f"{x}" "", "def")\n self.assertEqual("" f"{x}", "def")\n self.assertEqual(f"{x}" "2", "def2")\n self.assertEqual("1" f"{x}" "2", "1def2")\n self.assertEqual("1" f"{x}", "1def")\n self.assertEqual(f"{x}" f"-{x}", "def-def")\n self.assertEqual("" f"", "")\n self.assertEqual("" f"" "", "")\n self.assertEqual("" f"" "" f"", "")\n self.assertEqual(f"", "")\n self.assertEqual(f"" "", "")\n self.assertEqual(f"" "" f"", "")\n self.assertEqual(f"" "" f"" "", "")\n\n # This is not really [f\'{\'] + [f\'}\'] since we treat the inside\n # of braces as a purely new context, so it is actually f\'{ and\n # then eval(\' f\') (a valid expression) and then }\' which would\n # constitute a valid f-string.\n self.assertEqual(f\'{\' f\'}\', " f")\n\n self.assertAllRaise(\n SyntaxError,\n "expecting \'}\'",\n [\n \'\'\'f\'{3\' f"}"\'\'\', # can\'t concat to get a valid f-string\n ],\n )\n\n def test_comments(self):\n # These aren\'t comments, since they\'re in strings.\n d = {"#": "hash"}\n self.assertEqual(f\'{"#"}\', "#")\n self.assertEqual(f\'{d["#"]}\', "hash")\n\n self.assertAllRaise(\n SyntaxError,\n "\'{\' was never closed",\n [\n "f\'{1#}\'", # error because everything after \'#\' is a comment\n "f\'{#}\'",\n "f\'one: {1#}\'",\n "f\'{1# one} {2 this is a comment still#}\'",\n ],\n )\n self.assertAllRaise(\n SyntaxError,\n r"f-string: unmatched \'\\)\'",\n [\n "f\'{)#}\'", # When wrapped in parens, this becomes\n # \'()#)\'. Make sure that doesn\'t compile.\n ],\n )\n self.assertEqual(\n f"""A complex trick: {\n2 # two\n}""",\n "A complex trick: 2",\n )\n self.assertEqual(\n f"""\n{\n40 # forty\n+ # plus\n2 # two\n}""",\n "\\n42",\n )\n self.assertEqual(\n f"""\n{\n40 # forty\n+ # plus\n2 # two\n}""",\n "\\n42",\n )\n\n self.assertEqual(\n f"""\n# this is not a comment\n{ # the following operation it\'s\n3 # this is a number\n* 2}""",\n "\\n# this is not a comment\\n6",\n )\n self.assertEqual(\n f"""\n{# f\'a {comment}\'\n86 # constant\n# nothing more\n}""",\n "\\n86",\n )\n\n self.assertAllRaise(\n SyntaxError,\n r"f-string: valid expression required before \'}\'",\n [\n """f\'\'\'\n{\n# only a comment\n}\'\'\'\n""", # this is equivalent to f\'{}\'\n ],\n )\n\n def test_many_expressions(self):\n # Create a string with many expressions in it. Note that\n # because we have a space in here as a literal, we\'re actually\n # going to use twice as many ast nodes: one for each literal\n # plus one for each expression.\n def build_fstr(n, extra=""):\n return "f\'" + ("{x} " * n) + extra + "\'"\n\n x = "X"\n width = 1\n\n # Test around 256.\n for i in range(250, 260):\n self.assertEqual(eval(build_fstr(i)), (x + " ") * i)\n\n # Test concatenating 2 largs fstrings.\n self.assertEqual(eval(build_fstr(255) * 256), (x + " ") * (255 * 256))\n\n s = build_fstr(253, "{x:{width}} ")\n self.assertEqual(eval(s), (x + " ") * 254)\n\n # Test lots of expressions and constants, concatenated.\n s = "f\'{1}\' \'x\' \'y\'" * 1024\n self.assertEqual(eval(s), "1xy" * 1024)\n\n def test_format_specifier_expressions(self):\n width = 10\n precision = 4\n value = decimal.Decimal("12.34567")\n self.assertEqual(f"result: {value:{width}.{precision}}", "result: 12.35")\n self.assertEqual(f"result: {value:{width!r}.{precision}}", "result: 12.35")\n self.assertEqual(\n f"result: {value:{width:0}.{precision:1}}", "result: 12.35"\n )\n self.assertEqual(\n f"result: {value:{1}{0:0}.{precision:1}}", "result: 12.35"\n )\n self.assertEqual(\n f"result: {value:{ 1}{ 0:0}.{ precision:1}}", "result: 12.35"\n )\n self.assertEqual(f"{10:#{1}0x}", " 0xa")\n self.assertEqual(f\'{10:{"#"}1{0}{"x"}}\', " 0xa")\n self.assertEqual(f\'{-10:-{"#"}1{0}x}\', " -0xa")\n self.assertEqual(f\'{-10:{"-"}#{1}0{"x"}}\', " -0xa")\n self.assertEqual(f"{10:#{3 != {4:5} and width}x}", " 0xa")\n self.assertEqual(\n f"result: {value:{width:{0}}.{precision:1}}", "result: 12.35"\n )\n\n self.assertAllRaise(\n SyntaxError,\n "f-string: expecting \':\' or \'}\'",\n [\n """f\'{"s"!r{":10"}}\'""",\n # This looks like a nested format spec.\n ],\n )\n\n self.assertAllRaise(\n SyntaxError,\n "f-string: expecting a valid expression after \'{\'",\n [ # Invalid syntax inside a nested spec.\n "f\'{4:{/5}}\'",\n ],\n )\n\n self.assertAllRaise(\n SyntaxError,\n "f-string: invalid conversion character",\n [ # No expansion inside conversion or for\n # the : or ! itself.\n """f\'{"s"!{"r"}}\'""",\n ],\n )\n\n def test_custom_format_specifier(self):\n class CustomFormat:\n def __format__(self, format_spec):\n return format_spec\n\n self.assertEqual(f"{CustomFormat():\\n}", "\\n")\n self.assertEqual(f"{CustomFormat():\\u2603}", "☃")\n with self.assertWarns(SyntaxWarning):\n exec(r\'f"{F():¯\\_(ツ)_/¯}"\', {"F": CustomFormat})\n\n def test_side_effect_order(self):\n class X:\n def __init__(self):\n self.i = 0\n\n def __format__(self, spec):\n self.i += 1\n return str(self.i)\n\n x = X()\n self.assertEqual(f"{x} {x}", "1 2")\n\n def test_missing_expression(self):\n self.assertAllRaise(\n SyntaxError,\n "f-string: valid expression required before \'}\'",\n [\n "f\'{}\'",\n "f\'{ }\'" "f\' {} \'",\n "f\'{10:{ }}\'",\n "f\' { } \'",\n # The Python parser ignores also the following\n # whitespace characters in additional to a space.\n "f\'\'\'{\\t\\f\\r\\n}\'\'\'",\n ],\n )\n\n self.assertAllRaise(\n SyntaxError,\n "f-string: valid expression required before \'!\'",\n [\n "f\'{!r}\'",\n "f\'{ !r}\'",\n "f\'{!}\'",\n "f\'\'\'{\\t\\f\\r\\n!a}\'\'\'",\n # Catch empty expression before the\n # missing closing brace.\n "f\'{!\'",\n "f\'{!s:\'",\n # Catch empty expression before the\n # invalid conversion.\n "f\'{!x}\'",\n "f\'{ !xr}\'",\n "f\'{!x:}\'",\n "f\'{!x:a}\'",\n "f\'{ !xr:}\'",\n "f\'{ !xr:a}\'",\n ],\n )\n\n self.assertAllRaise(\n SyntaxError,\n "f-string: valid expression required before \':\'",\n [\n "f\'{:}\'",\n "f\'{ :!}\'",\n "f\'{:2}\'",\n "f\'\'\'{\\t\\f\\r\\n:a}\'\'\'",\n "f\'{:\'",\n "F\'{[F\'{:\'}[F\'{:\'}]]]",\n ],\n )\n\n self.assertAllRaise(\n SyntaxError,\n "f-string: valid expression required before \'=\'",\n [\n "f\'{=}\'",\n "f\'{ =}\'",\n "f\'{ =:}\'",\n "f\'{ =!}\'",\n "f\'\'\'{\\t\\f\\r\\n=}\'\'\'",\n "f\'{=\'",\n ],\n )\n\n # Different error message is raised for other whitespace characters.\n self.assertAllRaise(\n SyntaxError,\n r"invalid non-printable character U\\+00A0",\n [\n "f\'\'\'{\\xa0}\'\'\'",\n "\\xa0",\n ],\n )\n\n def test_parens_in_expressions(self):\n self.assertEqual(f"{3,}", "(3,)")\n\n self.assertAllRaise(\n SyntaxError,\n "f-string: expecting a valid expression after \'{\'",\n [\n "f\'{,}\'",\n ],\n )\n\n self.assertAllRaise(\n SyntaxError,\n r"f-string: unmatched \'\\)\'",\n [\n "f\'{3)+(4}\'",\n ],\n )\n\n def test_newlines_before_syntax_error(self):\n self.assertAllRaise(\n SyntaxError,\n "f-string: expecting a valid expression after \'{\'",\n ["f\'{.}\'", "\\nf\'{.}\'", "\\n\\nf\'{.}\'"],\n )\n\n def test_backslashes_in_string_part(self):\n self.assertEqual(f"\\t", "\\t")\n self.assertEqual(r"\\t", "\\\\t")\n self.assertEqual(rf"\\t", "\\\\t")\n self.assertEqual(f"{2}\\t", "2\\t")\n self.assertEqual(f"{2}\\t{3}", "2\\t3")\n self.assertEqual(f"\\t{3}", "\\t3")\n\n self.assertEqual(f"\\u0394", "\\u0394")\n self.assertEqual(r"\\u0394", "\\\\u0394")\n self.assertEqual(rf"\\u0394", "\\\\u0394")\n self.assertEqual(f"{2}\\u0394", "2\\u0394")\n self.assertEqual(f"{2}\\u0394{3}", "2\\u03943")\n self.assertEqual(f"\\u0394{3}", "\\u03943")\n\n self.assertEqual(f"\\U00000394", "\\u0394")\n self.assertEqual(r"\\U00000394", "\\\\U00000394")\n self.assertEqual(rf"\\U00000394", "\\\\U00000394")\n self.assertEqual(f"{2}\\U00000394", "2\\u0394")\n self.assertEqual(f"{2}\\U00000394{3}", "2\\u03943")\n self.assertEqual(f"\\U00000394{3}", "\\u03943")\n\n self.assertEqual(f"\\N{GREEK CAPITAL LETTER DELTA}", "\\u0394")\n self.assertEqual(f"{2}\\N{GREEK CAPITAL LETTER DELTA}", "2\\u0394")\n self.assertEqual(f"{2}\\N{GREEK CAPITAL LETTER DELTA}{3}", "2\\u03943")\n self.assertEqual(f"\\N{GREEK CAPITAL LETTER DELTA}{3}", "\\u03943")\n self.assertEqual(f"2\\N{GREEK CAPITAL LETTER DELTA}", "2\\u0394")\n self.assertEqual(f"2\\N{GREEK CAPITAL LETTER DELTA}3", "2\\u03943")\n self.assertEqual(f"\\N{GREEK CAPITAL LETTER DELTA}3", "\\u03943")\n\n self.assertEqual(f"\\x20", " ")\n self.assertEqual(r"\\x20", "\\\\x20")\n self.assertEqual(rf"\\x20", "\\\\x20")\n self.assertEqual(f"{2}\\x20", "2 ")\n self.assertEqual(f"{2}\\x20{3}", "2 3")\n self.assertEqual(f"\\x20{3}", " 3")\n\n self.assertEqual(f"2\\x20", "2 ")\n self.assertEqual(f"2\\x203", "2 3")\n self.assertEqual(f"\\x203", " 3")\n\n with self.assertWarns(SyntaxWarning): # invalid escape sequence\n value = eval(r"f\'\\{6*7}\'")\n self.assertEqual(value, "\\\\42")\n with self.assertWarns(SyntaxWarning): # invalid escape sequence\n value = eval(r"f\'\\g\'")\n self.assertEqual(value, "\\\\g")\n self.assertEqual(f"\\\\{6*7}", "\\\\42")\n self.assertEqual(rf"\\{6*7}", "\\\\42")\n\n AMPERSAND = "spam"\n # Get the right unicode character (&), or pick up local variable\n # depending on the number of backslashes.\n self.assertEqual(f"\\N{AMPERSAND}", "&")\n self.assertEqual(f"\\\\N{AMPERSAND}", "\\\\Nspam")\n self.assertEqual(rf"\\N{AMPERSAND}", "\\\\Nspam")\n self.assertEqual(f"\\\\\\N{AMPERSAND}", "\\\\&")\n\n def test_misformed_unicode_character_name(self):\n # These test are needed because unicode names are parsed\n # differently inside f-strings.\n self.assertAllRaise(\n SyntaxError,\n r"\\(unicode error\\) \'unicodeescape\' codec can\'t decode bytes in position .*: malformed \\\\N character escape",\n [\n r"f\'\\N\'",\n r"f\'\\N \'",\n r"f\'\\N \'", # See bpo-46503.\n r"f\'\\N{\'",\n r"f\'\\N{GREEK CAPITAL LETTER DELTA\'",\n # Here are the non-f-string versions,\n # which should give the same errors.\n r"\'\\N\'",\n r"\'\\N \'",\n r"\'\\N \'",\n r"\'\\N{\'",\n r"\'\\N{GREEK CAPITAL LETTER DELTA\'",\n ],\n )\n\n def test_backslashes_in_expression_part(self):\n self.assertEqual(\n f"{(\n 1 +\n 2\n )}",\n "3",\n )\n\n self.assertEqual("\\N{LEFT CURLY BRACKET}", "{")\n self.assertEqual(f\'{"\\N{LEFT CURLY BRACKET}"}\', "{")\n self.assertEqual(rf\'{"\\N{LEFT CURLY BRACKET}"}\', "{")\n\n self.assertAllRaise(\n SyntaxError,\n "f-string: valid expression required before \'}\'",\n [\n "f\'{\\n}\'",\n ],\n )\n\n def test_invalid_backslashes_inside_fstring_context(self):\n # All of these variations are invalid python syntax,\n # so they are also invalid in f-strings as well.\n cases = [\n formatting.format(expr=expr)\n for formatting in [\n "{expr}",\n "f\'{{{expr}}}\'",\n "rf\'{{{expr}}}\'",\n ]\n for expr in [\n r"\\\'a\\\'",\n r"\\t3",\n r"\\\\"[0],\n ]\n ]\n self.assertAllRaise(\n SyntaxError, "unexpected character after line continuation", cases\n )\n\n def test_no_escapes_for_braces(self):\n """\n Only literal curly braces begin an expression.\n """\n # \\x7b is \'{\'.\n self.assertEqual(f"\\x7b1+1}}", "{1+1}")\n self.assertEqual(f"\\x7b1+1", "{1+1")\n self.assertEqual(f"\\u007b1+1", "{1+1")\n self.assertEqual(f"\\N{LEFT CURLY BRACKET}1+1\\N{RIGHT CURLY BRACKET}", "{1+1}")\n\n def test_newlines_in_expressions(self):\n self.assertEqual(f"{0}", "0")\n self.assertEqual(\n rf"""{3+\n4}""",\n "7",\n )\n\n def test_lambda(self):\n x = 5\n self.assertEqual(f\'{(lambda y:x*y)("8")!r}\', "\'88888\'")\n self.assertEqual(f\'{(lambda y:x*y)("8")!r:10}\', "\'88888\' ")\n self.assertEqual(f\'{(lambda y:x*y)("8"):10}\', "88888 ")\n\n # lambda doesn\'t work without parens, because the colon\n # makes the parser think it\'s a format_spec\n # emit warning if we can match a format_spec\n self.assertAllRaise(\n SyntaxError,\n "f-string: lambda expressions are not allowed " "without parentheses",\n [\n "f\'{lambda x:x}\'",\n "f\'{lambda :x}\'",\n "f\'{lambda *arg, :x}\'",\n "f\'{1, lambda:x}\'",\n "f\'{lambda x:}\'",\n "f\'{lambda :}\'",\n ],\n )\n # Ensure the detection of invalid lambdas doesn\'t trigger detection\n # for valid lambdas in the second error pass\n with self.assertRaisesRegex(SyntaxError, "invalid syntax"):\n compile("lambda name_3=f\'{name_4}\': {name_3}\\n1 $ 1", "<string>", "exec")\n\n # but don\'t emit the paren warning in general cases\n with self.assertRaisesRegex(\n SyntaxError, "f-string: expecting a valid expression after \'{\'"\n ):\n eval("f\'{+ lambda:None}\'")\n\n def test_valid_prefixes(self):\n self.assertEqual(f"{1}", "1")\n self.assertEqual(Rf"{2}", "2")\n self.assertEqual(Rf"{3}", "3")\n\n def test_roundtrip_raw_quotes(self):\n self.assertEqual(rf"\\\'", "\\\\\'")\n self.assertEqual(rf"\\"", \'\\\\"\')\n self.assertEqual(rf"\\"\\\'", "\\\\\\"\\\\\'")\n self.assertEqual(rf"\\\'\\"", "\\\\\'\\\\\\"")\n self.assertEqual(rf"\\"\\\'\\"", \'\\\\"\\\\\\\'\\\\"\')\n self.assertEqual(rf"\\\'\\"\\\'", "\\\\\'\\\\\\"\\\\\'")\n self.assertEqual(rf"\\"\\\'\\"\\\'", "\\\\\\"\\\\\'\\\\\\"\\\\\'")\n\n def test_fstring_backslash_before_double_bracket(self):\n deprecated_cases = [\n (r"f\'\\{{\\}}\'", "\\\\{\\\\}"),\n (r"f\'\\{{\'", "\\\\{"),\n (r"f\'\\{{{1+1}\'", "\\\\{2"),\n (r"f\'\\}}{1+1}\'", "\\\\}2"),\n (r"f\'{1+1}\\}}\'", "2\\\\}"),\n ]\n for case, expected_result in deprecated_cases:\n with self.subTest(case=case, expected_result=expected_result):\n with self.assertWarns(SyntaxWarning):\n result = eval(case)\n self.assertEqual(result, expected_result)\n self.assertEqual(rf"\\{{\\}}", "\\\\{\\\\}")\n self.assertEqual(rf"\\{{", "\\\\{")\n self.assertEqual(rf"\\{{{1+1}", "\\\\{2")\n self.assertEqual(rf"\\}}{1+1}", "\\\\}2")\n self.assertEqual(rf"{1+1}\\}}", "2\\\\}")\n\n def test_fstring_backslash_before_double_bracket_warns_once(self):\n with self.assertWarns(SyntaxWarning) as w:\n eval(r"f\'\\{{\'")\n self.assertEqual(len(w.warnings), 1)\n self.assertEqual(w.warnings[0].category, SyntaxWarning)\n\n def test_fstring_backslash_prefix_raw(self):\n self.assertEqual(f"\\\\", "\\\\")\n self.assertEqual(f"\\\\\\\\", "\\\\\\\\")\n self.assertEqual(rf"\\\\", r"\\\\")\n self.assertEqual(rf"\\\\\\\\", r"\\\\\\\\")\n self.assertEqual(rf"\\\\", r"\\\\")\n self.assertEqual(rf"\\\\\\\\", r"\\\\\\\\")\n self.assertEqual(Rf"\\\\", R"\\\\")\n self.assertEqual(Rf"\\\\\\\\", R"\\\\\\\\")\n self.assertEqual(Rf"\\\\", R"\\\\")\n self.assertEqual(Rf"\\\\\\\\", R"\\\\\\\\")\n self.assertEqual(Rf"\\\\", R"\\\\")\n self.assertEqual(Rf"\\\\\\\\", R"\\\\\\\\")\n\n def test_fstring_format_spec_greedy_matching(self):\n self.assertEqual(f"{1:}}}", "1}")\n self.assertEqual(f"{1:>3{5}}}}", " 1}")\n\n def test_yield(self):\n # Not terribly useful, but make sure the yield turns\n # a function into a generator\n def fn(y):\n f"y:{yield y*2}"\n f"{yield}"\n\n g = fn(4)\n self.assertEqual(next(g), 8)\n self.assertEqual(next(g), None)\n\n def test_yield_send(self):\n def fn(x):\n yield f"x:{yield (lambda i: x * i)}"\n\n g = fn(10)\n the_lambda = next(g)\n self.assertEqual(the_lambda(4), 40)\n self.assertEqual(g.send("string"), "x:string")\n\n def test_expressions_with_triple_quoted_strings(self):\n self.assertEqual(f"{\'\'\'x\'\'\'}", "x")\n self.assertEqual(f"{\'\'\'eric\'s\'\'\'}", "eric\'s")\n\n # Test concatenation within an expression\n self.assertEqual(f\'{"x" """eric"s""" "y"}\', \'xeric"sy\')\n self.assertEqual(f\'{"x" """eric"s"""}\', \'xeric"s\')\n self.assertEqual(f\'{"""eric"s""" "y"}\', \'eric"sy\')\n self.assertEqual(f\'{"""x""" """eric"s""" "y"}\', \'xeric"sy\')\n self.assertEqual(f\'{"""x""" """eric"s""" """y"""}\', \'xeric"sy\')\n self.assertEqual(f\'{r"""x""" """eric"s""" """y"""}\', \'xeric"sy\')\n\n def test_multiple_vars(self):\n x = 98\n y = "abc"\n self.assertEqual(f"{x}{y}", "98abc")\n\n self.assertEqual(f"X{x}{y}", "X98abc")\n self.assertEqual(f"{x}X{y}", "98Xabc")\n self.assertEqual(f"{x}{y}X", "98abcX")\n\n self.assertEqual(f"X{x}Y{y}", "X98Yabc")\n self.assertEqual(f"X{x}{y}Y", "X98abcY")\n self.assertEqual(f"{x}X{y}Y", "98XabcY")\n\n self.assertEqual(f"X{x}Y{y}Z", "X98YabcZ")\n\n def test_closure(self):\n def outer(x):\n def inner():\n return f"x:{x}"\n\n return inner\n\n self.assertEqual(outer("987")(), "x:987")\n self.assertEqual(outer(7)(), "x:7")\n\n def test_arguments(self):\n y = 2\n\n def f(x, width):\n return f"x={x*y:{width}}"\n\n self.assertEqual(f("foo", 10), "x=foofoo ")\n x = "bar"\n self.assertEqual(f(10, 10), "x= 20")\n\n def test_locals(self):\n value = 123\n self.assertEqual(f"v:{value}", "v:123")\n\n def test_missing_variable(self):\n with self.assertRaises(NameError):\n f"v:{value}"\n\n def test_missing_format_spec(self):\n class O:\n def __format__(self, spec):\n if not spec:\n return "*"\n return spec\n\n self.assertEqual(f"{O():x}", "x")\n self.assertEqual(f"{O()}", "*")\n self.assertEqual(f"{O():}", "*")\n\n self.assertEqual(f"{3:}", "3")\n self.assertEqual(f"{3!s:}", "3")\n\n def test_global(self):\n self.assertEqual(f"g:{a_global}", "g:global variable")\n self.assertEqual(f"g:{a_global!r}", "g:\'global variable\'")\n\n a_local = "local variable"\n self.assertEqual(\n f"g:{a_global} l:{a_local}", "g:global variable l:local variable"\n )\n self.assertEqual(f"g:{a_global!r}", "g:\'global variable\'")\n self.assertEqual(\n f"g:{a_global} l:{a_local!r}", "g:global variable l:\'local variable\'"\n )\n\n self.assertIn("module \'unittest\' from", f"{unittest}")\n\n def test_shadowed_global(self):\n a_global = "really a local"\n self.assertEqual(f"g:{a_global}", "g:really a local")\n self.assertEqual(f"g:{a_global!r}", "g:\'really a local\'")\n\n a_local = "local variable"\n self.assertEqual(\n f"g:{a_global} l:{a_local}", "g:really a local l:local variable"\n )\n self.assertEqual(f"g:{a_global!r}", "g:\'really a local\'")\n self.assertEqual(\n f"g:{a_global} l:{a_local!r}", "g:really a local l:\'local variable\'"\n )\n\n def test_call(self):\n def foo(x):\n return "x=" + str(x)\n\n self.assertEqual(f"{foo(10)}", "x=10")\n\n def test_nested_fstrings(self):\n y = 5\n self.assertEqual(f\'{f"{0}"*3}\', "000")\n self.assertEqual(f\'{f"{y}"*3}\', "555")\n\n def test_invalid_string_prefixes(self):\n single_quote_cases = [\n "fu\'\'",\n "uf\'\'",\n "Fu\'\'",\n "fU\'\'",\n "Uf\'\'",\n "uF\'\'",\n "ufr\'\'",\n "urf\'\'",\n "fur\'\'",\n "fru\'\'",\n "rfu\'\'",\n "ruf\'\'",\n "FUR\'\'",\n "Fur\'\'",\n "fb\'\'",\n "fB\'\'",\n "Fb\'\'",\n "FB\'\'",\n "bf\'\'",\n "bF\'\'",\n "Bf\'\'",\n "BF\'\'",\n ]\n double_quote_cases = [case.replace("\'", \'"\') for case in single_quote_cases]\n self.assertAllRaise(\n SyntaxError, "invalid syntax", single_quote_cases + double_quote_cases\n )\n\n def test_leading_trailing_spaces(self):\n self.assertEqual(f"{ 3}", "3")\n self.assertEqual(f"{ 3}", "3")\n self.assertEqual(f"{3 }", "3")\n self.assertEqual(f"{3 }", "3")\n\n self.assertEqual(f"expr={ {x: y for x, y in [(1, 2), ]}}", "expr={1: 2}")\n self.assertEqual(f"expr={ {x: y for x, y in [(1, 2), ]} }", "expr={1: 2}")\n\n def test_not_equal(self):\n # There\'s a special test for this because there\'s a special\n # case in the f-string parser to look for != as not ending an\n # expression. Normally it would, while looking for !s or !r.\n\n self.assertEqual(f"{3!=4}", "True")\n self.assertEqual(f"{3!=4:}", "True")\n self.assertEqual(f"{3!=4!s}", "True")\n self.assertEqual(f"{3!=4!s:.3}", "Tru")\n\n def test_equal_equal(self):\n # Because an expression ending in = has special meaning,\n # there\'s a special test for ==. Make sure it works.\n\n self.assertEqual(f"{0==1}", "False")\n\n def test_conversions(self):\n self.assertEqual(f"{3.14:10.10}", " 3.14")\n self.assertEqual(f"{3.14!s:10.10}", "3.14 ")\n self.assertEqual(f"{3.14!r:10.10}", "3.14 ")\n self.assertEqual(f"{3.14!a:10.10}", "3.14 ")\n\n self.assertEqual(f\'{"a"}\', "a")\n self.assertEqual(f\'{"a"!r}\', "\'a\'")\n self.assertEqual(f\'{"a"!a}\', "\'a\'")\n\n # Conversions can have trailing whitespace after them since it\n # does not provide any significance\n self.assertEqual(f"{3!s }", "3")\n self.assertEqual(f"{3.14!s :10.10}", "3.14 ")\n\n # Not a conversion.\n self.assertEqual(f\'{"a!r"}\', "a!r")\n\n # Not a conversion, but show that ! is allowed in a format spec.\n self.assertEqual(f"{3.14:!<10.10}", "3.14!!!!!!")\n\n self.assertAllRaise(\n SyntaxError,\n "f-string: expecting \'}\'",\n [\n "f\'{3!\'",\n "f\'{3!s\'",\n "f\'{3!g\'",\n ],\n )\n\n self.assertAllRaise(\n SyntaxError,\n "f-string: missing conversion character",\n [\n "f\'{3!}\'",\n "f\'{3!:\'",\n "f\'{3!:}\'",\n ],\n )\n\n for conv_identifier in "g", "A", "G", "ä", "ɐ":\n self.assertAllRaise(\n SyntaxError,\n "f-string: invalid conversion character %r: "\n "expected \'s\', \'r\', or \'a\'" % conv_identifier,\n ["f\'{3!" + conv_identifier + "}\'"],\n )\n\n for conv_non_identifier in "3", "!":\n self.assertAllRaise(\n SyntaxError,\n "f-string: invalid conversion character",\n ["f\'{3!" + conv_non_identifier + "}\'"],\n )\n\n for conv in " s", " s ":\n self.assertAllRaise(\n SyntaxError,\n "f-string: conversion type must come right after the"\n " exclamanation mark",\n ["f\'{3!" + conv + "}\'"],\n )\n\n self.assertAllRaise(\n SyntaxError,\n "f-string: invalid conversion character \'ss\': " "expected \'s\', \'r\', or \'a\'",\n [\n "f\'{3!ss}\'",\n "f\'{3!ss:}\'",\n "f\'{3!ss:s}\'",\n ],\n )\n\n def test_assignment(self):\n self.assertAllRaise(\n SyntaxError,\n r"invalid syntax",\n [\n "f\'\' = 3",\n "f\'{0}\' = x",\n "f\'{x}\' = x",\n ],\n )\n\n def test_del(self):\n self.assertAllRaise(\n SyntaxError,\n "invalid syntax",\n [\n "del f\'\'",\n "del \'\' f\'\'",\n ],\n )\n\n def test_mismatched_braces(self):\n self.assertAllRaise(\n SyntaxError,\n "f-string: single \'}\' is not allowed",\n [\n "f\'{{}\'",\n "f\'{{}}}\'",\n "f\'}\'",\n "f\'x}\'",\n "f\'x}x\'",\n r"f\'\\u007b}\'",\n # Can\'t have { or } in a format spec.\n "f\'{3:}>10}\'",\n "f\'{3:}}>10}\'",\n ],\n )\n\n self.assertAllRaise(\n SyntaxError,\n "f-string: expecting \'}\'",\n [\n "f\'{3\'",\n "f\'{3!\'",\n "f\'{3:\'",\n "f\'{3!s\'",\n "f\'{3!s:\'",\n "f\'{3!s:3\'",\n "f\'x{\'",\n "f\'x{x\'",\n "f\'{x\'",\n "f\'{3:s\'",\n "f\'{{{\'",\n "f\'{{}}{\'",\n "f\'{\'",\n "f\'{i=\'", # See gh-93418.\n ],\n )\n\n self.assertAllRaise(\n SyntaxError,\n "f-string: expecting a valid expression after \'{\'",\n [\n "f\'{3:{{>10}\'",\n ],\n )\n\n # But these are just normal strings.\n self.assertEqual(f\'{"{"}\', "{")\n self.assertEqual(f\'{"}"}\', "}")\n self.assertEqual(f\'{3:{"}"}>10}\', "}}}}}}}}}3")\n self.assertEqual(f\'{2:{"{"}>10}\', "{{{{{{{{{2")\n\n def test_if_conditional(self):\n # There\'s special logic in compile.c to test if the\n # conditional for an if (and while) are constants. Exercise\n # that code.\n\n def test_fstring(x, expected):\n flag = 0\n if f"{x}":\n flag = 1\n else:\n flag = 2\n self.assertEqual(flag, expected)\n\n def test_concat_empty(x, expected):\n flag = 0\n if "" f"{x}":\n flag = 1\n else:\n flag = 2\n self.assertEqual(flag, expected)\n\n def test_concat_non_empty(x, expected):\n flag = 0\n if " " f"{x}":\n flag = 1\n else:\n flag = 2\n self.assertEqual(flag, expected)\n\n test_fstring("", 2)\n test_fstring(" ", 1)\n\n test_concat_empty("", 2)\n test_concat_empty(" ", 1)\n\n test_concat_non_empty("", 1)\n test_concat_non_empty(" ", 1)\n\n def test_empty_format_specifier(self):\n x = "test"\n self.assertEqual(f"{x}", "test")\n self.assertEqual(f"{x:}", "test")\n self.assertEqual(f"{x!s:}", "test")\n self.assertEqual(f"{x!r:}", "\'test\'")\n\n def test_str_format_differences(self):\n d = {\n "a": "string",\n 0: "integer",\n }\n a = 0\n self.assertEqual(f"{d[0]}", "integer")\n self.assertEqual(f\'{d["a"]}\', "string")\n self.assertEqual(f"{d[a]}", "integer")\n self.assertEqual("{d[a]}".format(d=d), "string")\n self.assertEqual("{d[0]}".format(d=d), "integer")\n\n def test_errors(self):\n # see issue 26287\n self.assertAllRaise(\n TypeError,\n "unsupported",\n [\n r"f\'{(lambda: 0):x}\'",\n r"f\'{(0,):x}\'",\n ],\n )\n self.assertAllRaise(\n ValueError,\n "Unknown format code",\n [\n r"f\'{1000:j}\'",\n r"f\'{1000:j}\'",\n ],\n )\n\n def test_filename_in_syntaxerror(self):\n # see issue 38964\n with temp_cwd() as cwd:\n file_path = os.path.join(cwd, "t.py")\n with open(file_path, "w", encoding="utf-8") as f:\n f.write(\'f"{a b}"\') # This generates a SyntaxError\n _, _, stderr = assert_python_failure(file_path, PYTHONIOENCODING="ascii")\n self.assertIn(file_path.encode("ascii", "backslashreplace"), stderr)\n\n def test_loop(self):\n for i in range(1000):\n self.assertEqual(f"i:{i}", "i:" + str(i))\n\n def test_dict(self):\n d = {\n \'"\': "dquote",\n "\'": "squote",\n "foo": "bar",\n }\n self.assertEqual(f"""{d["\'"]}""", "squote")\n self.assertEqual(f"""{d[\'"\']}""", "dquote")\n\n self.assertEqual(f\'{d["foo"]}\', "bar")\n self.assertEqual(f"{d[\'foo\']}", "bar")\n\n def test_backslash_char(self):\n # Check eval of a backslash followed by a control char.\n # See bpo-30682: this used to raise an assert in pydebug mode.\n self.assertEqual(eval(\'f"\\\\\\n"\'), "")\n self.assertEqual(eval(\'f"\\\\\\r"\'), "")\n\n def test_debug_conversion(self):\n x = "A string"\n self.assertEqual(f"{x=}", "x=" + repr(x))\n self.assertEqual(f"{x =}", "x =" + repr(x))\n self.assertEqual(f"{x=!s}", "x=" + str(x))\n self.assertEqual(f"{x=!r}", "x=" + repr(x))\n self.assertEqual(f"{x=!a}", "x=" + ascii(x))\n\n x = 2.71828\n self.assertEqual(f"{x=:.2f}", "x=" + format(x, ".2f"))\n self.assertEqual(f"{x=:}", "x=" + format(x, ""))\n self.assertEqual(f"{x=!r:^20}", "x=" + format(repr(x), "^20"))\n self.assertEqual(f"{x=!s:^20}", "x=" + format(str(x), "^20"))\n self.assertEqual(f"{x=!a:^20}", "x=" + format(ascii(x), "^20"))\n\n x = 9\n self.assertEqual(f"{3*x+15=}", "3*x+15=42")\n\n # There is code in ast.c that deals with non-ascii expression values. So,\n # use a unicode identifier to trigger that.\n tenπ = 31.4\n self.assertEqual(f"{tenπ=:.2f}", "tenπ=31.40")\n\n # Also test with Unicode in non-identifiers.\n self.assertEqual(f\'{"Σ"=}\', "\\"Σ\\"=\'Σ\'")\n\n # Make sure nested fstrings still work.\n self.assertEqual(f\'{f"{3.1415=:.1f}":*^20}\', "*****3.1415=3.1*****")\n\n # Make sure text before and after an expression with = works\n # correctly.\n pi = "π"\n self.assertEqual(f"alpha α {pi=} ω omega", "alpha α pi=\'π\' ω omega")\n\n # Check multi-line expressions.\n self.assertEqual(\n f"""{\n3\n=}""",\n "\\n3\\n=3",\n )\n\n # Since = is handled specially, make sure all existing uses of\n # it still work.\n\n self.assertEqual(f"{0==1}", "False")\n self.assertEqual(f"{0!=1}", "True")\n self.assertEqual(f"{0<=1}", "True")\n self.assertEqual(f"{0>=1}", "False")\n self.assertEqual(f\'{(x:="5")}\', "5")\n self.assertEqual(x, "5")\n self.assertEqual(f"{(x:=5)}", "5")\n self.assertEqual(x, 5)\n self.assertEqual(f\'{"="}\', "=")\n\n x = 20\n # This isn\'t an assignment expression, it\'s \'x\', with a format\n # spec of \'=10\'. See test_walrus: you need to use parens.\n self.assertEqual(f"{x:=10}", " 20")\n\n # Test named function parameters, to make sure \'=\' parsing works\n # there.\n def f(a):\n nonlocal x\n oldx = x\n x = a\n return oldx\n\n x = 0\n self.assertEqual(f\'{f(a="3=")}\', "0")\n self.assertEqual(x, "3=")\n self.assertEqual(f"{f(a=4)}", "3=")\n self.assertEqual(x, 4)\n\n # Check debug expressions in format spec\n y = 20\n self.assertEqual(f"{2:{y=}}", "yyyyyyyyyyyyyyyyyyy2")\n self.assertEqual(\n f"{datetime.datetime.now():h1{y=}h2{y=}h3{y=}}", "h1y=20h2y=20h3y=20"\n )\n\n # Make sure __format__ is being called.\n class C:\n def __format__(self, s):\n return f"FORMAT-{s}"\n\n def __repr__(self):\n return "REPR"\n\n self.assertEqual(f"{C()=}", "C()=REPR")\n self.assertEqual(f"{C()=!r}", "C()=REPR")\n self.assertEqual(f"{C()=:}", "C()=FORMAT-")\n self.assertEqual(f"{C()=: }", "C()=FORMAT- ")\n self.assertEqual(f"{C()=:x}", "C()=FORMAT-x")\n self.assertEqual(f"{C()=!r:*^20}", "C()=********REPR********")\n self.assertEqual(f"{C():{20=}}", "FORMAT-20=20")\n\n self.assertRaises(SyntaxError, eval, "f\'{C=]\'")\n\n # Make sure leading and following text works.\n x = "foo"\n self.assertEqual(f"X{x=}Y", "Xx=" + repr(x) + "Y")\n\n # Make sure whitespace around the = works.\n self.assertEqual(f"X{x =}Y", "Xx =" + repr(x) + "Y")\n self.assertEqual(f"X{x= }Y", "Xx= " + repr(x) + "Y")\n self.assertEqual(f"X{x = }Y", "Xx = " + repr(x) + "Y")\n self.assertEqual(f"sadsd {1 + 1 = :{1 + 1:1d}f}", "sadsd 1 + 1 = 2.000000")\n\n self.assertEqual(\n f"{1+2 = # my comment\n }",\n "1+2 = \\n 3",\n )\n\n # These next lines contains tabs. Backslash escapes don\'t\n # work in f-strings.\n # patchcheck doesn\'t like these tabs. So the only way to test\n # this will be to dynamically created and exec the f-strings. But\n # that\'s such a hassle I\'ll save it for another day. For now, convert\n # the tabs to spaces just to shut up patchcheck.\n # self.assertEqual(f\'X{x =}Y\', \'Xx\\t=\'+repr(x)+\'Y\')\n # self.assertEqual(f\'X{x = }Y\', \'Xx\\t=\\t\'+repr(x)+\'Y\')\n\n def test_debug_expressions_are_raw_strings(self):\n\n self.assertEqual(f\'{b"\\N{OX}"=}\', \'b"\\\\N{OX}"=b\\\'\\\\\\\\N{OX}\\\'\')\n self.assertEqual(f\'{r"\\xff"=}\', \'r"\\\\xff"=\\\'\\\\\\\\xff\\\'\')\n self.assertEqual(f\'{r"\\n"=}\', \'r"\\\\n"=\\\'\\\\\\\\n\\\'\')\n self.assertEqual(f"{\'\\\'\'=}", "\'\\\\\'\'=\\"\'\\"")\n self.assertEqual(f\'{\'\\xc5\'=}\', r"\'\\xc5\'=\'Å\'")\n\n def test_walrus(self):\n x = 20\n # This isn\'t an assignment expression, it\'s \'x\', with a format\n # spec of \'=10\'.\n self.assertEqual(f"{x:=10}", " 20")\n\n # This is an assignment expression, which requires parens.\n self.assertEqual(f"{(x:=10)}", "10")\n self.assertEqual(x, 10)\n\n def test_invalid_syntax_error_message(self):\n with self.assertRaisesRegex(\n SyntaxError, "f-string: expecting \'=\', or \'!\', or \':\', or \'}\'"\n ):\n compile("f\'{a $ b}\'", "?", "exec")\n\n def test_with_two_commas_in_format_specifier(self):\n error_msg = re.escape("Cannot specify \',\' with \',\'.")\n with self.assertRaisesRegex(ValueError, error_msg):\n f"{1:,,}"\n\n def test_with_two_underscore_in_format_specifier(self):\n error_msg = re.escape("Cannot specify \'_\' with \'_\'.")\n with self.assertRaisesRegex(ValueError, error_msg):\n f"{1:__}"\n\n def test_with_a_commas_and_an_underscore_in_format_specifier(self):\n error_msg = re.escape("Cannot specify both \',\' and \'_\'.")\n with self.assertRaisesRegex(ValueError, error_msg):\n f"{1:,_}"\n\n def test_with_an_underscore_and_a_comma_in_format_specifier(self):\n error_msg = re.escape("Cannot specify both \',\' and \'_\'.")\n with self.assertRaisesRegex(ValueError, error_msg):\n f"{1:_,}"\n\n def test_syntax_error_for_starred_expressions(self):\n with self.assertRaisesRegex(SyntaxError, "can\'t use starred expression here"):\n compile("f\'{*a}\'", "?", "exec")\n\n with self.assertRaisesRegex(\n SyntaxError, "f-string: expecting a valid expression after \'{\'"\n ):\n compile("f\'{**a}\'", "?", "exec")\n\n def test_not_closing_quotes(self):\n self.assertAllRaise(SyntaxError, "unterminated f-string literal", [\'f"\', "f\'"])\n self.assertAllRaise(\n SyntaxError, "unterminated triple-quoted f-string literal", [\'f"""\', "f\'\'\'"]\n )\n # Ensure that the errors are reported at the correct line number.\n data = \'\'\'\\\nx = 1 + 1\ny = 2 + 2\nz = f"""\nsdfjnsdfjsdf\nsdfsdfs{1+\n2} dfigdf {3+\n4}sdufsd""\n\'\'\'\n try:\n compile(data, "?", "exec")\n except SyntaxError as e:\n self.assertEqual(e.text, \'z = f"""\')\n self.assertEqual(e.lineno, 3)\n\n def test_syntax_error_after_debug(self):\n self.assertAllRaise(\n SyntaxError,\n "f-string: expecting a valid expression after \'{\'",\n [\n "f\'{1=}{;\'",\n "f\'{1=}{+;\'",\n "f\'{1=}{2}{;\'",\n "f\'{1=}{3}{;\'",\n ],\n )\n self.assertAllRaise(\n SyntaxError,\n "f-string: expecting \'=\', or \'!\', or \':\', or \'}\'",\n [\n "f\'{1=}{1;\'",\n "f\'{1=}{1;}\'",\n ],\n )\n\n def test_debug_in_file(self):\n with temp_cwd():\n script = "script.py"\n with open("script.py", "w") as f:\n f.write(f"""\\\nprint(f\'\'\'{{\n3\n=}}\'\'\')""")\n\n _, stdout, _ = assert_python_ok(script)\n self.assertEqual(\n stdout.decode("utf-8").strip().replace("\\r\\n", "\\n").replace("\\r", "\\n"),\n "3\\n=3",\n )\n\n def test_syntax_warning_infinite_recursion_in_file(self):\n with temp_cwd():\n script = "script.py"\n with open(script, "w") as f:\n f.write(r"print(f\'\\{1}\')")\n\n _, stdout, stderr = assert_python_ok(script)\n self.assertIn(rb"\\1", stdout)\n self.assertEqual(len(stderr.strip().splitlines()), 2)\n\n def test_gh129093(self):\n self.assertEqual(f\'{1==2=}\', \'1==2=False\')\n self.assertEqual(f\'{1 == 2=}\', \'1 == 2=False\')\n self.assertEqual(f\'{1!=2=}\', \'1!=2=True\')\n self.assertEqual(f\'{1 != 2=}\', \'1 != 2=True\')\n\n self.assertEqual(f\'{(1) != 2=}\', \'(1) != 2=True\')\n self.assertEqual(f\'{(1*2) != (3)=}\', \'(1*2) != (3)=True\')\n\n self.assertEqual(f\'{1 != 2 == 3 != 4=}\', \'1 != 2 == 3 != 4=False\')\n self.assertEqual(f\'{1 == 2 != 3 == 4=}\', \'1 == 2 != 3 == 4=False\')\n\n self.assertEqual(f\'{f\'{1==2=}\'=}\', "f\'{1==2=}\'=\'1==2=False\'")\n self.assertEqual(f\'{f\'{1 == 2=}\'=}\', "f\'{1 == 2=}\'=\'1 == 2=False\'")\n self.assertEqual(f\'{f\'{1!=2=}\'=}\', "f\'{1!=2=}\'=\'1!=2=True\'")\n self.assertEqual(f\'{f\'{1 != 2=}\'=}\', "f\'{1 != 2=}\'=\'1 != 2=True\'")\n\n\nif __name__ == "__main__":\n\n unittest.main()\n', ast_parse_kwargs={}, filename=PosixPath('/Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/test_fstring.py')) ... FAIL
Testing /Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/test_syntax.py
Testing /Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/test_grammar.py
Testing /Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/test_compile.py
Testing /Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/test_asdl_parser.py
Testing /Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/test_tokenize.py
Testing /Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/test_patma.py
Testing /Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/test_type_params.py
test_async_function (test.test_unparse.ManualASTCreationTestCase.test_async_function) ... ok
test_async_function_with_type_params (test.test_unparse.ManualASTCreationTestCase.test_async_function_with_type_params) ... ok
test_class (test.test_unparse.ManualASTCreationTestCase.test_class) ... ok
test_class_with_type_params (test.test_unparse.ManualASTCreationTestCase.test_class_with_type_params) ... ok
test_function (test.test_unparse.ManualASTCreationTestCase.test_function) ... ok
test_function_with_type_params (test.test_unparse.ManualASTCreationTestCase.test_function_with_type_params) ... ok
test_function_with_type_params_and_bound (test.test_unparse.ManualASTCreationTestCase.test_function_with_type_params_and_bound) ... ok
test_annotations (test.test_unparse.UnparseTestCase.test_annotations) ... ok
test_bytes (test.test_unparse.UnparseTestCase.test_bytes) ... ok
test_chained_comparisons (test.test_unparse.UnparseTestCase.test_chained_comparisons) ... ok
test_class_decorators (test.test_unparse.UnparseTestCase.test_class_decorators) ... ok
test_class_definition (test.test_unparse.UnparseTestCase.test_class_definition) ... ok
test_constant_tuples (test.test_unparse.UnparseTestCase.test_constant_tuples) ... ok
test_del_statement (test.test_unparse.UnparseTestCase.test_del_statement) ... ok
test_dict_comprehension (test.test_unparse.UnparseTestCase.test_dict_comprehension) ... ok
test_dict_unpacking_in_dict (test.test_unparse.UnparseTestCase.test_dict_unpacking_in_dict) ... ok
test_docstrings (test.test_unparse.UnparseTestCase.test_docstrings) ... ok
test_elifs (test.test_unparse.UnparseTestCase.test_elifs) ... ok
test_empty_set (test.test_unparse.UnparseTestCase.test_empty_set) ... ok
test_for_else (test.test_unparse.UnparseTestCase.test_for_else) ... ok
test_fstring_backslash (test.test_unparse.UnparseTestCase.test_fstring_backslash) ... ok
test_fstrings (test.test_unparse.UnparseTestCase.test_fstrings) ... ok
test_fstrings_complicated (test.test_unparse.UnparseTestCase.test_fstrings_complicated) ... ok
test_fstrings_pep701 (test.test_unparse.UnparseTestCase.test_fstrings_pep701) ... ok
test_fstrings_special_chars (test.test_unparse.UnparseTestCase.test_fstrings_special_chars) ... ok
test_function_arguments (test.test_unparse.UnparseTestCase.test_function_arguments) ... ok
test_function_type (test.test_unparse.UnparseTestCase.test_function_type) ... ok
test_huge_float (test.test_unparse.UnparseTestCase.test_huge_float) ... ok
test_imaginary_literals (test.test_unparse.UnparseTestCase.test_imaginary_literals) ... ok
test_import_from_level_none (test.test_unparse.UnparseTestCase.test_import_from_level_none) ... ok
test_integer_parens (test.test_unparse.UnparseTestCase.test_integer_parens) ... ok
test_invalid_fstring_value (test.test_unparse.UnparseTestCase.test_invalid_fstring_value) ... ok
test_invalid_raise (test.test_unparse.UnparseTestCase.test_invalid_raise) ... ok
test_invalid_yield_from (test.test_unparse.UnparseTestCase.test_invalid_yield_from) ... ok
test_lambda_parentheses (test.test_unparse.UnparseTestCase.test_lambda_parentheses) ... ok
test_min_int (test.test_unparse.UnparseTestCase.test_min_int) ... ok
test_nan (test.test_unparse.UnparseTestCase.test_nan) ... ok
test_nonlocal (test.test_unparse.UnparseTestCase.test_nonlocal) ... ok
test_raise_from (test.test_unparse.UnparseTestCase.test_raise_from) ... ok
test_relative_import (test.test_unparse.UnparseTestCase.test_relative_import) ... ok
test_set_comprehension (test.test_unparse.UnparseTestCase.test_set_comprehension) ... ok
test_set_literal (test.test_unparse.UnparseTestCase.test_set_literal) ... ok
test_shifts (test.test_unparse.UnparseTestCase.test_shifts) ... ok
test_slices (test.test_unparse.UnparseTestCase.test_slices) ... ok
test_starred_assignment (test.test_unparse.UnparseTestCase.test_starred_assignment) ... ok
test_strings (test.test_unparse.UnparseTestCase.test_strings) ... ok
test_try_except_finally (test.test_unparse.UnparseTestCase.test_try_except_finally) ... ok
test_try_except_star_finally (test.test_unparse.UnparseTestCase.test_try_except_star_finally) ... ok
test_type_comments (test.test_unparse.UnparseTestCase.test_type_comments) ... ok
test_type_ignore (test.test_unparse.UnparseTestCase.test_type_ignore) ... ok
test_unary_parens (test.test_unparse.UnparseTestCase.test_unary_parens) ... ok
test_while_else (test.test_unparse.UnparseTestCase.test_while_else) ... ok
test_with_as (test.test_unparse.UnparseTestCase.test_with_as) ... ok
test_with_simple (test.test_unparse.UnparseTestCase.test_with_simple) ... ok
test_with_two_items (test.test_unparse.UnparseTestCase.test_with_two_items) ... ok
======================================================================
FAIL: test_multiquote_joined_string (test.test_unparse.CosmeticTestCase.test_multiquote_joined_string) (code1='f\'"""{1}\'\'\' ', ast_parse_kwargs={})
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/test_unparse.py", line 138, in check_ast_roundtrip
self.assertASTEqual(ast1, ast2)
^^^^^^^^^^^^^^
File "/Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/support/ast_helper.py", line 43, in assertASTEqual
traverse_compare(ast1, ast2)
File "/Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/support/ast_helper.py", line 24, in traverse_compare
traverse_compare(value1, value2)
File "/Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/support/ast_helper.py", line 28, in traverse_compare
traverse_compare(node1, node2)
File "/Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/support/ast_helper.py", line 24, in traverse_compare
traverse_compare(value1, value2)
File "/Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/support/ast_helper.py", line 24, in traverse_compare
traverse_compare(value1, value2)
File "/Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/support/ast_helper.py", line 40, in traverse_compare
raise self.failureException(msg) from None
AssertionError: First list contains 1 additional elements.
======================================================================
FAIL: test_multiquote_joined_string (test.test_unparse.CosmeticTestCase.test_multiquote_joined_string) (code1='f\'"""{"\\n"}\'\'\' ', ast_parse_kwargs={})
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/test_unparse.py", line 138, in check_ast_roundtrip
self.assertASTEqual(ast1, ast2)
^^^^^^^^^^^^^^
File "/Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/support/ast_helper.py", line 43, in assertASTEqual
traverse_compare(ast1, ast2)
File "/Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/support/ast_helper.py", line 24, in traverse_compare
traverse_compare(value1, value2)
File "/Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/support/ast_helper.py", line 28, in traverse_compare
traverse_compare(node1, node2)
File "/Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/support/ast_helper.py", line 24, in traverse_compare
traverse_compare(value1, value2)
File "/Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/support/ast_helper.py", line 24, in traverse_compare
traverse_compare(value1, value2)
File "/Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/support/ast_helper.py", line 40, in traverse_compare
raise self.failureException(msg) from None
AssertionError: First list contains 1 additional elements.
======================================================================
FAIL: test_files (test.test_unparse.DirectoryTestCase.test_files) (code1='# -*- coding: utf-8 -*-\n# There are tests here with unicode string literals and\n# identifiers. There\'s a code in ast.c that was added because of a\n# failure with a non-ascii-only expression. So, I have tests for\n# that. There are workarounds that would let me run tests for that\n# code without unicode identifiers and strings, but just using them\n# directly seems like the easiest and therefore safest thing to do.\n# Unicode identifiers in tests is allowed by PEP 3131.\n\nimport ast\nimport datetime\nimport os\nimport re\nimport types\nimport decimal\nimport unittest\nimport warnings\nfrom test import support\nfrom test.support.os_helper import temp_cwd\nfrom test.support.script_helper import assert_python_failure, assert_python_ok\n\na_global = "global variable"\n\n# You could argue that I\'m too strict in looking for specific error\n# values with assertRaisesRegex, but without it it\'s way too easy to\n# make a syntax error in the test strings. Especially with all of the\n# triple quotes, raw strings, backslashes, etc. I think it\'s a\n# worthwhile tradeoff. When I switched to this method, I found many\n# examples where I wasn\'t testing what I thought I was.\n\n\nclass TestCase(unittest.TestCase):\n def assertAllRaise(self, exception_type, regex, error_strings):\n for str in error_strings:\n with self.subTest(str=str):\n with self.assertRaisesRegex(exception_type, regex):\n eval(str)\n\n def test__format__lookup(self):\n # Make sure __format__ is looked up on the type, not the instance.\n class X:\n def __format__(self, spec):\n return "class"\n\n x = X()\n\n # Add a bound __format__ method to the \'y\' instance, but not\n # the \'x\' instance.\n y = X()\n y.__format__ = types.MethodType(lambda self, spec: "instance", y)\n\n self.assertEqual(f"{y}", format(y))\n self.assertEqual(f"{y}", "class")\n self.assertEqual(format(x), format(y))\n\n # __format__ is not called this way, but still make sure it\n # returns what we expect (so we can make sure we\'re bypassing\n # it).\n self.assertEqual(x.__format__(""), "class")\n self.assertEqual(y.__format__(""), "instance")\n\n # This is how __format__ is actually called.\n self.assertEqual(type(x).__format__(x, ""), "class")\n self.assertEqual(type(y).__format__(y, ""), "class")\n\n def test_ast(self):\n # Inspired by http://bugs.python.org/issue24975\n class X:\n def __init__(self):\n self.called = False\n\n def __call__(self):\n self.called = True\n return 4\n\n x = X()\n expr = """\na = 10\nf\'{a * x()}\'"""\n t = ast.parse(expr)\n c = compile(t, "", "exec")\n\n # Make sure x was not called.\n self.assertFalse(x.called)\n\n # Actually run the code.\n exec(c)\n\n # Make sure x was called.\n self.assertTrue(x.called)\n\n def test_ast_line_numbers(self):\n expr = """\na = 10\nf\'{a * x()}\'"""\n t = ast.parse(expr)\n self.assertEqual(type(t), ast.Module)\n self.assertEqual(len(t.body), 2)\n # check `a = 10`\n self.assertEqual(type(t.body[0]), ast.Assign)\n self.assertEqual(t.body[0].lineno, 2)\n # check `f\'...\'`\n self.assertEqual(type(t.body[1]), ast.Expr)\n self.assertEqual(type(t.body[1].value), ast.JoinedStr)\n self.assertEqual(len(t.body[1].value.values), 1)\n self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)\n self.assertEqual(t.body[1].lineno, 3)\n self.assertEqual(t.body[1].value.lineno, 3)\n self.assertEqual(t.body[1].value.values[0].lineno, 3)\n # check the binop location\n binop = t.body[1].value.values[0].value\n self.assertEqual(type(binop), ast.BinOp)\n self.assertEqual(type(binop.left), ast.Name)\n self.assertEqual(type(binop.op), ast.Mult)\n self.assertEqual(type(binop.right), ast.Call)\n self.assertEqual(binop.lineno, 3)\n self.assertEqual(binop.left.lineno, 3)\n self.assertEqual(binop.right.lineno, 3)\n self.assertEqual(binop.col_offset, 3)\n self.assertEqual(binop.left.col_offset, 3)\n self.assertEqual(binop.right.col_offset, 7)\n\n def test_ast_line_numbers_multiple_formattedvalues(self):\n expr = """\nf\'no formatted values\'\nf\'eggs {a * x()} spam {b + y()}\'"""\n t = ast.parse(expr)\n self.assertEqual(type(t), ast.Module)\n self.assertEqual(len(t.body), 2)\n # check `f\'no formatted value\'`\n self.assertEqual(type(t.body[0]), ast.Expr)\n self.assertEqual(type(t.body[0].value), ast.JoinedStr)\n self.assertEqual(t.body[0].lineno, 2)\n # check `f\'...\'`\n self.assertEqual(type(t.body[1]), ast.Expr)\n self.assertEqual(type(t.body[1].value), ast.JoinedStr)\n self.assertEqual(len(t.body[1].value.values), 4)\n self.assertEqual(type(t.body[1].value.values[0]), ast.Constant)\n self.assertEqual(type(t.body[1].value.values[0].value), str)\n self.assertEqual(type(t.body[1].value.values[1]), ast.FormattedValue)\n self.assertEqual(type(t.body[1].value.values[2]), ast.Constant)\n self.assertEqual(type(t.body[1].value.values[2].value), str)\n self.assertEqual(type(t.body[1].value.values[3]), ast.FormattedValue)\n self.assertEqual(t.body[1].lineno, 3)\n self.assertEqual(t.body[1].value.lineno, 3)\n self.assertEqual(t.body[1].value.values[0].lineno, 3)\n self.assertEqual(t.body[1].value.values[1].lineno, 3)\n self.assertEqual(t.body[1].value.values[2].lineno, 3)\n self.assertEqual(t.body[1].value.values[3].lineno, 3)\n # check the first binop location\n binop1 = t.body[1].value.values[1].value\n self.assertEqual(type(binop1), ast.BinOp)\n self.assertEqual(type(binop1.left), ast.Name)\n self.assertEqual(type(binop1.op), ast.Mult)\n self.assertEqual(type(binop1.right), ast.Call)\n self.assertEqual(binop1.lineno, 3)\n self.assertEqual(binop1.left.lineno, 3)\n self.assertEqual(binop1.right.lineno, 3)\n self.assertEqual(binop1.col_offset, 8)\n self.assertEqual(binop1.left.col_offset, 8)\n self.assertEqual(binop1.right.col_offset, 12)\n # check the second binop location\n binop2 = t.body[1].value.values[3].value\n self.assertEqual(type(binop2), ast.BinOp)\n self.assertEqual(type(binop2.left), ast.Name)\n self.assertEqual(type(binop2.op), ast.Add)\n self.assertEqual(type(binop2.right), ast.Call)\n self.assertEqual(binop2.lineno, 3)\n self.assertEqual(binop2.left.lineno, 3)\n self.assertEqual(binop2.right.lineno, 3)\n self.assertEqual(binop2.col_offset, 23)\n self.assertEqual(binop2.left.col_offset, 23)\n self.assertEqual(binop2.right.col_offset, 27)\n\n def test_ast_line_numbers_nested(self):\n expr = """\na = 10\nf\'{a * f"-{x()}-"}\'"""\n t = ast.parse(expr)\n self.assertEqual(type(t), ast.Module)\n self.assertEqual(len(t.body), 2)\n # check `a = 10`\n self.assertEqual(type(t.body[0]), ast.Assign)\n self.assertEqual(t.body[0].lineno, 2)\n # check `f\'...\'`\n self.assertEqual(type(t.body[1]), ast.Expr)\n self.assertEqual(type(t.body[1].value), ast.JoinedStr)\n self.assertEqual(len(t.body[1].value.values), 1)\n self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)\n self.assertEqual(t.body[1].lineno, 3)\n self.assertEqual(t.body[1].value.lineno, 3)\n self.assertEqual(t.body[1].value.values[0].lineno, 3)\n # check the binop location\n binop = t.body[1].value.values[0].value\n self.assertEqual(type(binop), ast.BinOp)\n self.assertEqual(type(binop.left), ast.Name)\n self.assertEqual(type(binop.op), ast.Mult)\n self.assertEqual(type(binop.right), ast.JoinedStr)\n self.assertEqual(binop.lineno, 3)\n self.assertEqual(binop.left.lineno, 3)\n self.assertEqual(binop.right.lineno, 3)\n self.assertEqual(binop.col_offset, 3)\n self.assertEqual(binop.left.col_offset, 3)\n self.assertEqual(binop.right.col_offset, 7)\n # check the nested call location\n self.assertEqual(len(binop.right.values), 3)\n self.assertEqual(type(binop.right.values[0]), ast.Constant)\n self.assertEqual(type(binop.right.values[0].value), str)\n self.assertEqual(type(binop.right.values[1]), ast.FormattedValue)\n self.assertEqual(type(binop.right.values[2]), ast.Constant)\n self.assertEqual(type(binop.right.values[2].value), str)\n self.assertEqual(binop.right.values[0].lineno, 3)\n self.assertEqual(binop.right.values[1].lineno, 3)\n self.assertEqual(binop.right.values[2].lineno, 3)\n call = binop.right.values[1].value\n self.assertEqual(type(call), ast.Call)\n self.assertEqual(call.lineno, 3)\n self.assertEqual(call.col_offset, 11)\n\n def test_ast_line_numbers_duplicate_expression(self):\n expr = """\na = 10\nf\'{a * x()} {a * x()} {a * x()}\'\n"""\n t = ast.parse(expr)\n self.assertEqual(type(t), ast.Module)\n self.assertEqual(len(t.body), 2)\n # check `a = 10`\n self.assertEqual(type(t.body[0]), ast.Assign)\n self.assertEqual(t.body[0].lineno, 2)\n # check `f\'...\'`\n self.assertEqual(type(t.body[1]), ast.Expr)\n self.assertEqual(type(t.body[1].value), ast.JoinedStr)\n self.assertEqual(len(t.body[1].value.values), 5)\n self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)\n self.assertEqual(type(t.body[1].value.values[1]), ast.Constant)\n self.assertEqual(type(t.body[1].value.values[1].value), str)\n self.assertEqual(type(t.body[1].value.values[2]), ast.FormattedValue)\n self.assertEqual(type(t.body[1].value.values[3]), ast.Constant)\n self.assertEqual(type(t.body[1].value.values[3].value), str)\n self.assertEqual(type(t.body[1].value.values[4]), ast.FormattedValue)\n self.assertEqual(t.body[1].lineno, 3)\n self.assertEqual(t.body[1].value.lineno, 3)\n self.assertEqual(t.body[1].value.values[0].lineno, 3)\n self.assertEqual(t.body[1].value.values[1].lineno, 3)\n self.assertEqual(t.body[1].value.values[2].lineno, 3)\n self.assertEqual(t.body[1].value.values[3].lineno, 3)\n self.assertEqual(t.body[1].value.values[4].lineno, 3)\n # check the first binop location\n binop = t.body[1].value.values[0].value\n self.assertEqual(type(binop), ast.BinOp)\n self.assertEqual(type(binop.left), ast.Name)\n self.assertEqual(type(binop.op), ast.Mult)\n self.assertEqual(type(binop.right), ast.Call)\n self.assertEqual(binop.lineno, 3)\n self.assertEqual(binop.left.lineno, 3)\n self.assertEqual(binop.right.lineno, 3)\n self.assertEqual(binop.col_offset, 3)\n self.assertEqual(binop.left.col_offset, 3)\n self.assertEqual(binop.right.col_offset, 7)\n # check the second binop location\n binop = t.body[1].value.values[2].value\n self.assertEqual(type(binop), ast.BinOp)\n self.assertEqual(type(binop.left), ast.Name)\n self.assertEqual(type(binop.op), ast.Mult)\n self.assertEqual(type(binop.right), ast.Call)\n self.assertEqual(binop.lineno, 3)\n self.assertEqual(binop.left.lineno, 3)\n self.assertEqual(binop.right.lineno, 3)\n self.assertEqual(binop.col_offset, 13)\n self.assertEqual(binop.left.col_offset, 13)\n self.assertEqual(binop.right.col_offset, 17)\n # check the third binop location\n binop = t.body[1].value.values[4].value\n self.assertEqual(type(binop), ast.BinOp)\n self.assertEqual(type(binop.left), ast.Name)\n self.assertEqual(type(binop.op), ast.Mult)\n self.assertEqual(type(binop.right), ast.Call)\n self.assertEqual(binop.lineno, 3)\n self.assertEqual(binop.left.lineno, 3)\n self.assertEqual(binop.right.lineno, 3)\n self.assertEqual(binop.col_offset, 23)\n self.assertEqual(binop.left.col_offset, 23)\n self.assertEqual(binop.right.col_offset, 27)\n\n def test_ast_numbers_fstring_with_formatting(self):\n t = ast.parse(\'f"Here is that pesky {xxx:.3f} again"\')\n self.assertEqual(len(t.body), 1)\n self.assertEqual(t.body[0].lineno, 1)\n\n self.assertEqual(type(t.body[0]), ast.Expr)\n self.assertEqual(type(t.body[0].value), ast.JoinedStr)\n self.assertEqual(len(t.body[0].value.values), 3)\n\n self.assertEqual(type(t.body[0].value.values[0]), ast.Constant)\n self.assertEqual(type(t.body[0].value.values[1]), ast.FormattedValue)\n self.assertEqual(type(t.body[0].value.values[2]), ast.Constant)\n\n _, expr, _ = t.body[0].value.values\n\n name = expr.value\n self.assertEqual(type(name), ast.Name)\n self.assertEqual(name.lineno, 1)\n self.assertEqual(name.end_lineno, 1)\n self.assertEqual(name.col_offset, 22)\n self.assertEqual(name.end_col_offset, 25)\n\n def test_ast_line_numbers_multiline_fstring(self):\n # See bpo-30465 for details.\n expr = """\na = 10\nf\'\'\'\n {a\n *\n x()}\nnon-important content\n\'\'\'\n"""\n t = ast.parse(expr)\n self.assertEqual(type(t), ast.Module)\n self.assertEqual(len(t.body), 2)\n # check `a = 10`\n self.assertEqual(type(t.body[0]), ast.Assign)\n self.assertEqual(t.body[0].lineno, 2)\n # check `f\'...\'`\n self.assertEqual(type(t.body[1]), ast.Expr)\n self.assertEqual(type(t.body[1].value), ast.JoinedStr)\n self.assertEqual(len(t.body[1].value.values), 3)\n self.assertEqual(type(t.body[1].value.values[0]), ast.Constant)\n self.assertEqual(type(t.body[1].value.values[0].value), str)\n self.assertEqual(type(t.body[1].value.values[1]), ast.FormattedValue)\n self.assertEqual(type(t.body[1].value.values[2]), ast.Constant)\n self.assertEqual(type(t.body[1].value.values[2].value), str)\n self.assertEqual(t.body[1].lineno, 3)\n self.assertEqual(t.body[1].value.lineno, 3)\n self.assertEqual(t.body[1].value.values[0].lineno, 3)\n self.assertEqual(t.body[1].value.values[1].lineno, 4)\n self.assertEqual(t.body[1].value.values[2].lineno, 6)\n self.assertEqual(t.body[1].col_offset, 0)\n self.assertEqual(t.body[1].value.col_offset, 0)\n self.assertEqual(t.body[1].value.values[0].col_offset, 4)\n self.assertEqual(t.body[1].value.values[1].col_offset, 2)\n self.assertEqual(t.body[1].value.values[2].col_offset, 11)\n # NOTE: the following lineno information and col_offset is correct for\n # expressions within FormattedValues.\n binop = t.body[1].value.values[1].value\n self.assertEqual(type(binop), ast.BinOp)\n self.assertEqual(type(binop.left), ast.Name)\n self.assertEqual(type(binop.op), ast.Mult)\n self.assertEqual(type(binop.right), ast.Call)\n self.assertEqual(binop.lineno, 4)\n self.assertEqual(binop.left.lineno, 4)\n self.assertEqual(binop.right.lineno, 6)\n self.assertEqual(binop.col_offset, 3)\n self.assertEqual(binop.left.col_offset, 3)\n self.assertEqual(binop.right.col_offset, 7)\n\n expr = """\na = f\'\'\'\n {blech}\n \'\'\'\n"""\n t = ast.parse(expr)\n self.assertEqual(type(t), ast.Module)\n self.assertEqual(len(t.body), 1)\n # Check f\'...\'\n self.assertEqual(type(t.body[0]), ast.Assign)\n self.assertEqual(type(t.body[0].value), ast.JoinedStr)\n self.assertEqual(len(t.body[0].value.values), 3)\n self.assertEqual(type(t.body[0].value.values[1]), ast.FormattedValue)\n self.assertEqual(t.body[0].lineno, 2)\n self.assertEqual(t.body[0].value.lineno, 2)\n self.assertEqual(t.body[0].value.values[0].lineno, 2)\n self.assertEqual(t.body[0].value.values[1].lineno, 3)\n self.assertEqual(t.body[0].value.values[2].lineno, 3)\n self.assertEqual(t.body[0].col_offset, 0)\n self.assertEqual(t.body[0].value.col_offset, 4)\n self.assertEqual(t.body[0].value.values[0].col_offset, 8)\n self.assertEqual(t.body[0].value.values[1].col_offset, 10)\n self.assertEqual(t.body[0].value.values[2].col_offset, 17)\n # Check {blech}\n self.assertEqual(t.body[0].value.values[1].value.lineno, 3)\n self.assertEqual(t.body[0].value.values[1].value.end_lineno, 3)\n self.assertEqual(t.body[0].value.values[1].value.col_offset, 11)\n self.assertEqual(t.body[0].value.values[1].value.end_col_offset, 16)\n\n def test_ast_line_numbers_with_parentheses(self):\n expr = """\nx = (\n f" {test(t)}"\n)"""\n t = ast.parse(expr)\n self.assertEqual(type(t), ast.Module)\n self.assertEqual(len(t.body), 1)\n # check the joinedstr location\n joinedstr = t.body[0].value\n self.assertEqual(type(joinedstr), ast.JoinedStr)\n self.assertEqual(joinedstr.lineno, 3)\n self.assertEqual(joinedstr.end_lineno, 3)\n self.assertEqual(joinedstr.col_offset, 4)\n self.assertEqual(joinedstr.end_col_offset, 17)\n # check the formatted value location\n fv = t.body[0].value.values[1]\n self.assertEqual(type(fv), ast.FormattedValue)\n self.assertEqual(fv.lineno, 3)\n self.assertEqual(fv.end_lineno, 3)\n self.assertEqual(fv.col_offset, 7)\n self.assertEqual(fv.end_col_offset, 16)\n # check the test(t) location\n call = t.body[0].value.values[1].value\n self.assertEqual(type(call), ast.Call)\n self.assertEqual(call.lineno, 3)\n self.assertEqual(call.end_lineno, 3)\n self.assertEqual(call.col_offset, 8)\n self.assertEqual(call.end_col_offset, 15)\n\n expr = """\nx = (\n u\'wat\',\n u"wat",\n b\'wat\',\n b"wat",\n f\'wat\',\n f"wat",\n)\n\ny = (\n u\'\'\'wat\'\'\',\n u\\"\\"\\"wat\\"\\"\\",\n b\'\'\'wat\'\'\',\n b\\"\\"\\"wat\\"\\"\\",\n f\'\'\'wat\'\'\',\n f\\"\\"\\"wat\\"\\"\\",\n)\n """\n t = ast.parse(expr)\n self.assertEqual(type(t), ast.Module)\n self.assertEqual(len(t.body), 2)\n x, y = t.body\n\n # Check the single quoted string offsets first.\n offsets = [(elt.col_offset, elt.end_col_offset) for elt in x.value.elts]\n self.assertTrue(all(offset == (4, 10) for offset in offsets))\n\n # Check the triple quoted string offsets.\n offsets = [(elt.col_offset, elt.end_col_offset) for elt in y.value.elts]\n self.assertTrue(all(offset == (4, 14) for offset in offsets))\n\n expr = """\nx = (\n \'PERL_MM_OPT\', (\n f\'wat\'\n f\'some_string={f(x)} \'\n f\'wat\'\n ),\n)\n"""\n t = ast.parse(expr)\n self.assertEqual(type(t), ast.Module)\n self.assertEqual(len(t.body), 1)\n # check the fstring\n fstring = t.body[0].value.elts[1]\n self.assertEqual(type(fstring), ast.JoinedStr)\n self.assertEqual(len(fstring.values), 3)\n wat1, middle, wat2 = fstring.values\n # check the first wat\n self.assertEqual(type(wat1), ast.Constant)\n self.assertEqual(wat1.lineno, 4)\n self.assertEqual(wat1.end_lineno, 5)\n self.assertEqual(wat1.col_offset, 14)\n self.assertEqual(wat1.end_col_offset, 26)\n # check the call\n call = middle.value\n self.assertEqual(type(call), ast.Call)\n self.assertEqual(call.lineno, 5)\n self.assertEqual(call.end_lineno, 5)\n self.assertEqual(call.col_offset, 27)\n self.assertEqual(call.end_col_offset, 31)\n # check the second wat\n self.assertEqual(type(wat2), ast.Constant)\n self.assertEqual(wat2.lineno, 5)\n self.assertEqual(wat2.end_lineno, 6)\n self.assertEqual(wat2.col_offset, 32)\n # wat ends at the offset 17, but the whole f-string\n # ends at the offset 18 (since the quote is part of the\n # f-string but not the wat string)\n self.assertEqual(wat2.end_col_offset, 17)\n self.assertEqual(fstring.end_col_offset, 18)\n\n def test_ast_fstring_empty_format_spec(self):\n expr = "f\'{expr:}\'"\n\n mod = ast.parse(expr)\n self.assertEqual(type(mod), ast.Module)\n self.assertEqual(len(mod.body), 1)\n\n fstring = mod.body[0].value\n self.assertEqual(type(fstring), ast.JoinedStr)\n self.assertEqual(len(fstring.values), 1)\n\n fv = fstring.values[0]\n self.assertEqual(type(fv), ast.FormattedValue)\n\n format_spec = fv.format_spec\n self.assertEqual(type(format_spec), ast.JoinedStr)\n self.assertEqual(len(format_spec.values), 0)\n\n def test_docstring(self):\n def f():\n f"""Not a docstring"""\n\n self.assertIsNone(f.__doc__)\n\n def g():\n """Not a docstring""" f""\n\n self.assertIsNone(g.__doc__)\n\n def test_literal_eval(self):\n with self.assertRaisesRegex(ValueError, "malformed node or string"):\n ast.literal_eval("f\'x\'")\n\n def test_ast_compile_time_concat(self):\n x = [""]\n\n expr = """x[0] = \'foo\' f\'{3}\'"""\n t = ast.parse(expr)\n c = compile(t, "", "exec")\n exec(c)\n self.assertEqual(x[0], "foo3")\n\n def test_compile_time_concat_errors(self):\n self.assertAllRaise(\n SyntaxError,\n "cannot mix bytes and nonbytes literals",\n [\n r"""f\'\' b\'\'""",\n r"""b\'\' f\'\'""",\n ],\n )\n\n def test_literal(self):\n self.assertEqual(f"", "")\n self.assertEqual(f"a", "a")\n self.assertEqual(f" ", " ")\n\n def test_unterminated_string(self):\n self.assertAllRaise(\n SyntaxError,\n "unterminated string",\n [\n r"""f\'{"x\'""",\n r"""f\'{"x}\'""",\n r"""f\'{("x\'""",\n r"""f\'{("x}\'""",\n ],\n )\n\n @unittest.skipIf(support.is_wasi, "exhausts limited stack on WASI")\n def test_mismatched_parens(self):\n self.assertAllRaise(\n SyntaxError,\n r"closing parenthesis \'\\}\' " r"does not match opening parenthesis \'\\(\'",\n [\n "f\'{((}\'",\n ],\n )\n self.assertAllRaise(\n SyntaxError,\n r"closing parenthesis \'\\)\' " r"does not match opening parenthesis \'\\[\'",\n [\n "f\'{a[4)}\'",\n ],\n )\n self.assertAllRaise(\n SyntaxError,\n r"closing parenthesis \'\\]\' " r"does not match opening parenthesis \'\\(\'",\n [\n "f\'{a(4]}\'",\n ],\n )\n self.assertAllRaise(\n SyntaxError,\n r"closing parenthesis \'\\}\' " r"does not match opening parenthesis \'\\[\'",\n [\n "f\'{a[4}\'",\n ],\n )\n self.assertAllRaise(\n SyntaxError,\n r"closing parenthesis \'\\}\' " r"does not match opening parenthesis \'\\(\'",\n [\n "f\'{a(4}\'",\n ],\n )\n self.assertRaises(SyntaxError, eval, "f\'{" + "(" * 500 + "}\'")\n\n @unittest.skipIf(support.is_wasi, "exhausts limited stack on WASI")\n def test_fstring_nested_too_deeply(self):\n self.assertAllRaise(\n SyntaxError,\n "f-string: expressions nested too deeply",\n [\'f"{1+2:{1+2:{1+1:{1}}}}"\'],\n )\n\n def create_nested_fstring(n):\n if n == 0:\n return "1+1"\n prev = create_nested_fstring(n - 1)\n return f\'f"{{{prev}}}"\'\n\n self.assertAllRaise(\n SyntaxError, "too many nested f-strings", [create_nested_fstring(160)]\n )\n\n def test_syntax_error_in_nested_fstring(self):\n # See gh-104016 for more information on this crash\n self.assertAllRaise(\n SyntaxError, "invalid syntax", [\'f"{1 1:\' + (\'{f"1:\' * 199)]\n )\n\n def test_double_braces(self):\n self.assertEqual(f"{{", "{")\n self.assertEqual(f"a{{", "a{")\n self.assertEqual(f"{{b", "{b")\n self.assertEqual(f"a{{b", "a{b")\n self.assertEqual(f"}}", "}")\n self.assertEqual(f"a}}", "a}")\n self.assertEqual(f"}}b", "}b")\n self.assertEqual(f"a}}b", "a}b")\n self.assertEqual(f"{{}}", "{}")\n self.assertEqual(f"a{{}}", "a{}")\n self.assertEqual(f"{{b}}", "{b}")\n self.assertEqual(f"{{}}c", "{}c")\n self.assertEqual(f"a{{b}}", "a{b}")\n self.assertEqual(f"a{{}}c", "a{}c")\n self.assertEqual(f"{{b}}c", "{b}c")\n self.assertEqual(f"a{{b}}c", "a{b}c")\n\n self.assertEqual(f"{{{10}", "{10")\n self.assertEqual(f"}}{10}", "}10")\n self.assertEqual(f"}}{{{10}", "}{10")\n self.assertEqual(f"}}a{{{10}", "}a{10")\n\n self.assertEqual(f"{10}{{", "10{")\n self.assertEqual(f"{10}}}", "10}")\n self.assertEqual(f"{10}}}{{", "10}{")\n self.assertEqual(f"{10}}}a{{" "}", "10}a{}")\n\n # Inside of strings, don\'t interpret doubled brackets.\n self.assertEqual(f\'{"{{}}"}\', "{{}}")\n\n self.assertAllRaise(\n TypeError,\n "unhashable type",\n [\n "f\'{ {{}} }\'", # dict in a set\n ],\n )\n\n def test_compile_time_concat(self):\n x = "def"\n self.assertEqual("abc" f"## {x}ghi", "abc## defghi")\n self.assertEqual("abc" f"{x}" "ghi", "abcdefghi")\n self.assertEqual("abc" f"{x}" "gh" f"i{x:4}", "abcdefghidef ")\n self.assertEqual("{x}" f"{x}", "{x}def")\n self.assertEqual("{x" f"{x}", "{xdef")\n self.assertEqual("{x}" f"{x}", "{x}def")\n self.assertEqual("{{x}}" f"{x}", "{{x}}def")\n self.assertEqual("{{x" f"{x}", "{{xdef")\n self.assertEqual("x}}" f"{x}", "x}}def")\n self.assertEqual(f"{x}" "x}}", "defx}}")\n self.assertEqual(f"{x}" "", "def")\n self.assertEqual("" f"{x}" "", "def")\n self.assertEqual("" f"{x}", "def")\n self.assertEqual(f"{x}" "2", "def2")\n self.assertEqual("1" f"{x}" "2", "1def2")\n self.assertEqual("1" f"{x}", "1def")\n self.assertEqual(f"{x}" f"-{x}", "def-def")\n self.assertEqual("" f"", "")\n self.assertEqual("" f"" "", "")\n self.assertEqual("" f"" "" f"", "")\n self.assertEqual(f"", "")\n self.assertEqual(f"" "", "")\n self.assertEqual(f"" "" f"", "")\n self.assertEqual(f"" "" f"" "", "")\n\n # This is not really [f\'{\'] + [f\'}\'] since we treat the inside\n # of braces as a purely new context, so it is actually f\'{ and\n # then eval(\' f\') (a valid expression) and then }\' which would\n # constitute a valid f-string.\n self.assertEqual(f\'{\' f\'}\', " f")\n\n self.assertAllRaise(\n SyntaxError,\n "expecting \'}\'",\n [\n \'\'\'f\'{3\' f"}"\'\'\', # can\'t concat to get a valid f-string\n ],\n )\n\n def test_comments(self):\n # These aren\'t comments, since they\'re in strings.\n d = {"#": "hash"}\n self.assertEqual(f\'{"#"}\', "#")\n self.assertEqual(f\'{d["#"]}\', "hash")\n\n self.assertAllRaise(\n SyntaxError,\n "\'{\' was never closed",\n [\n "f\'{1#}\'", # error because everything after \'#\' is a comment\n "f\'{#}\'",\n "f\'one: {1#}\'",\n "f\'{1# one} {2 this is a comment still#}\'",\n ],\n )\n self.assertAllRaise(\n SyntaxError,\n r"f-string: unmatched \'\\)\'",\n [\n "f\'{)#}\'", # When wrapped in parens, this becomes\n # \'()#)\'. Make sure that doesn\'t compile.\n ],\n )\n self.assertEqual(\n f"""A complex trick: {\n2 # two\n}""",\n "A complex trick: 2",\n )\n self.assertEqual(\n f"""\n{\n40 # forty\n+ # plus\n2 # two\n}""",\n "\\n42",\n )\n self.assertEqual(\n f"""\n{\n40 # forty\n+ # plus\n2 # two\n}""",\n "\\n42",\n )\n\n self.assertEqual(\n f"""\n# this is not a comment\n{ # the following operation it\'s\n3 # this is a number\n* 2}""",\n "\\n# this is not a comment\\n6",\n )\n self.assertEqual(\n f"""\n{# f\'a {comment}\'\n86 # constant\n# nothing more\n}""",\n "\\n86",\n )\n\n self.assertAllRaise(\n SyntaxError,\n r"f-string: valid expression required before \'}\'",\n [\n """f\'\'\'\n{\n# only a comment\n}\'\'\'\n""", # this is equivalent to f\'{}\'\n ],\n )\n\n def test_many_expressions(self):\n # Create a string with many expressions in it. Note that\n # because we have a space in here as a literal, we\'re actually\n # going to use twice as many ast nodes: one for each literal\n # plus one for each expression.\n def build_fstr(n, extra=""):\n return "f\'" + ("{x} " * n) + extra + "\'"\n\n x = "X"\n width = 1\n\n # Test around 256.\n for i in range(250, 260):\n self.assertEqual(eval(build_fstr(i)), (x + " ") * i)\n\n # Test concatenating 2 largs fstrings.\n self.assertEqual(eval(build_fstr(255) * 256), (x + " ") * (255 * 256))\n\n s = build_fstr(253, "{x:{width}} ")\n self.assertEqual(eval(s), (x + " ") * 254)\n\n # Test lots of expressions and constants, concatenated.\n s = "f\'{1}\' \'x\' \'y\'" * 1024\n self.assertEqual(eval(s), "1xy" * 1024)\n\n def test_format_specifier_expressions(self):\n width = 10\n precision = 4\n value = decimal.Decimal("12.34567")\n self.assertEqual(f"result: {value:{width}.{precision}}", "result: 12.35")\n self.assertEqual(f"result: {value:{width!r}.{precision}}", "result: 12.35")\n self.assertEqual(\n f"result: {value:{width:0}.{precision:1}}", "result: 12.35"\n )\n self.assertEqual(\n f"result: {value:{1}{0:0}.{precision:1}}", "result: 12.35"\n )\n self.assertEqual(\n f"result: {value:{ 1}{ 0:0}.{ precision:1}}", "result: 12.35"\n )\n self.assertEqual(f"{10:#{1}0x}", " 0xa")\n self.assertEqual(f\'{10:{"#"}1{0}{"x"}}\', " 0xa")\n self.assertEqual(f\'{-10:-{"#"}1{0}x}\', " -0xa")\n self.assertEqual(f\'{-10:{"-"}#{1}0{"x"}}\', " -0xa")\n self.assertEqual(f"{10:#{3 != {4:5} and width}x}", " 0xa")\n self.assertEqual(\n f"result: {value:{width:{0}}.{precision:1}}", "result: 12.35"\n )\n\n self.assertAllRaise(\n SyntaxError,\n "f-string: expecting \':\' or \'}\'",\n [\n """f\'{"s"!r{":10"}}\'""",\n # This looks like a nested format spec.\n ],\n )\n\n self.assertAllRaise(\n SyntaxError,\n "f-string: expecting a valid expression after \'{\'",\n [ # Invalid syntax inside a nested spec.\n "f\'{4:{/5}}\'",\n ],\n )\n\n self.assertAllRaise(\n SyntaxError,\n "f-string: invalid conversion character",\n [ # No expansion inside conversion or for\n # the : or ! itself.\n """f\'{"s"!{"r"}}\'""",\n ],\n )\n\n def test_custom_format_specifier(self):\n class CustomFormat:\n def __format__(self, format_spec):\n return format_spec\n\n self.assertEqual(f"{CustomFormat():\\n}", "\\n")\n self.assertEqual(f"{CustomFormat():\\u2603}", "☃")\n with self.assertWarns(SyntaxWarning):\n exec(r\'f"{F():¯\\_(ツ)_/¯}"\', {"F": CustomFormat})\n\n def test_side_effect_order(self):\n class X:\n def __init__(self):\n self.i = 0\n\n def __format__(self, spec):\n self.i += 1\n return str(self.i)\n\n x = X()\n self.assertEqual(f"{x} {x}", "1 2")\n\n def test_missing_expression(self):\n self.assertAllRaise(\n SyntaxError,\n "f-string: valid expression required before \'}\'",\n [\n "f\'{}\'",\n "f\'{ }\'" "f\' {} \'",\n "f\'{10:{ }}\'",\n "f\' { } \'",\n # The Python parser ignores also the following\n # whitespace characters in additional to a space.\n "f\'\'\'{\\t\\f\\r\\n}\'\'\'",\n ],\n )\n\n self.assertAllRaise(\n SyntaxError,\n "f-string: valid expression required before \'!\'",\n [\n "f\'{!r}\'",\n "f\'{ !r}\'",\n "f\'{!}\'",\n "f\'\'\'{\\t\\f\\r\\n!a}\'\'\'",\n # Catch empty expression before the\n # missing closing brace.\n "f\'{!\'",\n "f\'{!s:\'",\n # Catch empty expression before the\n # invalid conversion.\n "f\'{!x}\'",\n "f\'{ !xr}\'",\n "f\'{!x:}\'",\n "f\'{!x:a}\'",\n "f\'{ !xr:}\'",\n "f\'{ !xr:a}\'",\n ],\n )\n\n self.assertAllRaise(\n SyntaxError,\n "f-string: valid expression required before \':\'",\n [\n "f\'{:}\'",\n "f\'{ :!}\'",\n "f\'{:2}\'",\n "f\'\'\'{\\t\\f\\r\\n:a}\'\'\'",\n "f\'{:\'",\n "F\'{[F\'{:\'}[F\'{:\'}]]]",\n ],\n )\n\n self.assertAllRaise(\n SyntaxError,\n "f-string: valid expression required before \'=\'",\n [\n "f\'{=}\'",\n "f\'{ =}\'",\n "f\'{ =:}\'",\n "f\'{ =!}\'",\n "f\'\'\'{\\t\\f\\r\\n=}\'\'\'",\n "f\'{=\'",\n ],\n )\n\n # Different error message is raised for other whitespace characters.\n self.assertAllRaise(\n SyntaxError,\n r"invalid non-printable character U\\+00A0",\n [\n "f\'\'\'{\\xa0}\'\'\'",\n "\\xa0",\n ],\n )\n\n def test_parens_in_expressions(self):\n self.assertEqual(f"{3,}", "(3,)")\n\n self.assertAllRaise(\n SyntaxError,\n "f-string: expecting a valid expression after \'{\'",\n [\n "f\'{,}\'",\n ],\n )\n\n self.assertAllRaise(\n SyntaxError,\n r"f-string: unmatched \'\\)\'",\n [\n "f\'{3)+(4}\'",\n ],\n )\n\n def test_newlines_before_syntax_error(self):\n self.assertAllRaise(\n SyntaxError,\n "f-string: expecting a valid expression after \'{\'",\n ["f\'{.}\'", "\\nf\'{.}\'", "\\n\\nf\'{.}\'"],\n )\n\n def test_backslashes_in_string_part(self):\n self.assertEqual(f"\\t", "\\t")\n self.assertEqual(r"\\t", "\\\\t")\n self.assertEqual(rf"\\t", "\\\\t")\n self.assertEqual(f"{2}\\t", "2\\t")\n self.assertEqual(f"{2}\\t{3}", "2\\t3")\n self.assertEqual(f"\\t{3}", "\\t3")\n\n self.assertEqual(f"\\u0394", "\\u0394")\n self.assertEqual(r"\\u0394", "\\\\u0394")\n self.assertEqual(rf"\\u0394", "\\\\u0394")\n self.assertEqual(f"{2}\\u0394", "2\\u0394")\n self.assertEqual(f"{2}\\u0394{3}", "2\\u03943")\n self.assertEqual(f"\\u0394{3}", "\\u03943")\n\n self.assertEqual(f"\\U00000394", "\\u0394")\n self.assertEqual(r"\\U00000394", "\\\\U00000394")\n self.assertEqual(rf"\\U00000394", "\\\\U00000394")\n self.assertEqual(f"{2}\\U00000394", "2\\u0394")\n self.assertEqual(f"{2}\\U00000394{3}", "2\\u03943")\n self.assertEqual(f"\\U00000394{3}", "\\u03943")\n\n self.assertEqual(f"\\N{GREEK CAPITAL LETTER DELTA}", "\\u0394")\n self.assertEqual(f"{2}\\N{GREEK CAPITAL LETTER DELTA}", "2\\u0394")\n self.assertEqual(f"{2}\\N{GREEK CAPITAL LETTER DELTA}{3}", "2\\u03943")\n self.assertEqual(f"\\N{GREEK CAPITAL LETTER DELTA}{3}", "\\u03943")\n self.assertEqual(f"2\\N{GREEK CAPITAL LETTER DELTA}", "2\\u0394")\n self.assertEqual(f"2\\N{GREEK CAPITAL LETTER DELTA}3", "2\\u03943")\n self.assertEqual(f"\\N{GREEK CAPITAL LETTER DELTA}3", "\\u03943")\n\n self.assertEqual(f"\\x20", " ")\n self.assertEqual(r"\\x20", "\\\\x20")\n self.assertEqual(rf"\\x20", "\\\\x20")\n self.assertEqual(f"{2}\\x20", "2 ")\n self.assertEqual(f"{2}\\x20{3}", "2 3")\n self.assertEqual(f"\\x20{3}", " 3")\n\n self.assertEqual(f"2\\x20", "2 ")\n self.assertEqual(f"2\\x203", "2 3")\n self.assertEqual(f"\\x203", " 3")\n\n with self.assertWarns(SyntaxWarning): # invalid escape sequence\n value = eval(r"f\'\\{6*7}\'")\n self.assertEqual(value, "\\\\42")\n with self.assertWarns(SyntaxWarning): # invalid escape sequence\n value = eval(r"f\'\\g\'")\n self.assertEqual(value, "\\\\g")\n self.assertEqual(f"\\\\{6*7}", "\\\\42")\n self.assertEqual(rf"\\{6*7}", "\\\\42")\n\n AMPERSAND = "spam"\n # Get the right unicode character (&), or pick up local variable\n # depending on the number of backslashes.\n self.assertEqual(f"\\N{AMPERSAND}", "&")\n self.assertEqual(f"\\\\N{AMPERSAND}", "\\\\Nspam")\n self.assertEqual(rf"\\N{AMPERSAND}", "\\\\Nspam")\n self.assertEqual(f"\\\\\\N{AMPERSAND}", "\\\\&")\n\n def test_misformed_unicode_character_name(self):\n # These test are needed because unicode names are parsed\n # differently inside f-strings.\n self.assertAllRaise(\n SyntaxError,\n r"\\(unicode error\\) \'unicodeescape\' codec can\'t decode bytes in position .*: malformed \\\\N character escape",\n [\n r"f\'\\N\'",\n r"f\'\\N \'",\n r"f\'\\N \'", # See bpo-46503.\n r"f\'\\N{\'",\n r"f\'\\N{GREEK CAPITAL LETTER DELTA\'",\n # Here are the non-f-string versions,\n # which should give the same errors.\n r"\'\\N\'",\n r"\'\\N \'",\n r"\'\\N \'",\n r"\'\\N{\'",\n r"\'\\N{GREEK CAPITAL LETTER DELTA\'",\n ],\n )\n\n def test_backslashes_in_expression_part(self):\n self.assertEqual(\n f"{(\n 1 +\n 2\n )}",\n "3",\n )\n\n self.assertEqual("\\N{LEFT CURLY BRACKET}", "{")\n self.assertEqual(f\'{"\\N{LEFT CURLY BRACKET}"}\', "{")\n self.assertEqual(rf\'{"\\N{LEFT CURLY BRACKET}"}\', "{")\n\n self.assertAllRaise(\n SyntaxError,\n "f-string: valid expression required before \'}\'",\n [\n "f\'{\\n}\'",\n ],\n )\n\n def test_invalid_backslashes_inside_fstring_context(self):\n # All of these variations are invalid python syntax,\n # so they are also invalid in f-strings as well.\n cases = [\n formatting.format(expr=expr)\n for formatting in [\n "{expr}",\n "f\'{{{expr}}}\'",\n "rf\'{{{expr}}}\'",\n ]\n for expr in [\n r"\\\'a\\\'",\n r"\\t3",\n r"\\\\"[0],\n ]\n ]\n self.assertAllRaise(\n SyntaxError, "unexpected character after line continuation", cases\n )\n\n def test_no_escapes_for_braces(self):\n """\n Only literal curly braces begin an expression.\n """\n # \\x7b is \'{\'.\n self.assertEqual(f"\\x7b1+1}}", "{1+1}")\n self.assertEqual(f"\\x7b1+1", "{1+1")\n self.assertEqual(f"\\u007b1+1", "{1+1")\n self.assertEqual(f"\\N{LEFT CURLY BRACKET}1+1\\N{RIGHT CURLY BRACKET}", "{1+1}")\n\n def test_newlines_in_expressions(self):\n self.assertEqual(f"{0}", "0")\n self.assertEqual(\n rf"""{3+\n4}""",\n "7",\n )\n\n def test_lambda(self):\n x = 5\n self.assertEqual(f\'{(lambda y:x*y)("8")!r}\', "\'88888\'")\n self.assertEqual(f\'{(lambda y:x*y)("8")!r:10}\', "\'88888\' ")\n self.assertEqual(f\'{(lambda y:x*y)("8"):10}\', "88888 ")\n\n # lambda doesn\'t work without parens, because the colon\n # makes the parser think it\'s a format_spec\n # emit warning if we can match a format_spec\n self.assertAllRaise(\n SyntaxError,\n "f-string: lambda expressions are not allowed " "without parentheses",\n [\n "f\'{lambda x:x}\'",\n "f\'{lambda :x}\'",\n "f\'{lambda *arg, :x}\'",\n "f\'{1, lambda:x}\'",\n "f\'{lambda x:}\'",\n "f\'{lambda :}\'",\n ],\n )\n # Ensure the detection of invalid lambdas doesn\'t trigger detection\n # for valid lambdas in the second error pass\n with self.assertRaisesRegex(SyntaxError, "invalid syntax"):\n compile("lambda name_3=f\'{name_4}\': {name_3}\\n1 $ 1", "<string>", "exec")\n\n # but don\'t emit the paren warning in general cases\n with self.assertRaisesRegex(\n SyntaxError, "f-string: expecting a valid expression after \'{\'"\n ):\n eval("f\'{+ lambda:None}\'")\n\n def test_valid_prefixes(self):\n self.assertEqual(f"{1}", "1")\n self.assertEqual(Rf"{2}", "2")\n self.assertEqual(Rf"{3}", "3")\n\n def test_roundtrip_raw_quotes(self):\n self.assertEqual(rf"\\\'", "\\\\\'")\n self.assertEqual(rf"\\"", \'\\\\"\')\n self.assertEqual(rf"\\"\\\'", "\\\\\\"\\\\\'")\n self.assertEqual(rf"\\\'\\"", "\\\\\'\\\\\\"")\n self.assertEqual(rf"\\"\\\'\\"", \'\\\\"\\\\\\\'\\\\"\')\n self.assertEqual(rf"\\\'\\"\\\'", "\\\\\'\\\\\\"\\\\\'")\n self.assertEqual(rf"\\"\\\'\\"\\\'", "\\\\\\"\\\\\'\\\\\\"\\\\\'")\n\n def test_fstring_backslash_before_double_bracket(self):\n deprecated_cases = [\n (r"f\'\\{{\\}}\'", "\\\\{\\\\}"),\n (r"f\'\\{{\'", "\\\\{"),\n (r"f\'\\{{{1+1}\'", "\\\\{2"),\n (r"f\'\\}}{1+1}\'", "\\\\}2"),\n (r"f\'{1+1}\\}}\'", "2\\\\}"),\n ]\n for case, expected_result in deprecated_cases:\n with self.subTest(case=case, expected_result=expected_result):\n with self.assertWarns(SyntaxWarning):\n result = eval(case)\n self.assertEqual(result, expected_result)\n self.assertEqual(rf"\\{{\\}}", "\\\\{\\\\}")\n self.assertEqual(rf"\\{{", "\\\\{")\n self.assertEqual(rf"\\{{{1+1}", "\\\\{2")\n self.assertEqual(rf"\\}}{1+1}", "\\\\}2")\n self.assertEqual(rf"{1+1}\\}}", "2\\\\}")\n\n def test_fstring_backslash_before_double_bracket_warns_once(self):\n with self.assertWarns(SyntaxWarning) as w:\n eval(r"f\'\\{{\'")\n self.assertEqual(len(w.warnings), 1)\n self.assertEqual(w.warnings[0].category, SyntaxWarning)\n\n def test_fstring_backslash_prefix_raw(self):\n self.assertEqual(f"\\\\", "\\\\")\n self.assertEqual(f"\\\\\\\\", "\\\\\\\\")\n self.assertEqual(rf"\\\\", r"\\\\")\n self.assertEqual(rf"\\\\\\\\", r"\\\\\\\\")\n self.assertEqual(rf"\\\\", r"\\\\")\n self.assertEqual(rf"\\\\\\\\", r"\\\\\\\\")\n self.assertEqual(Rf"\\\\", R"\\\\")\n self.assertEqual(Rf"\\\\\\\\", R"\\\\\\\\")\n self.assertEqual(Rf"\\\\", R"\\\\")\n self.assertEqual(Rf"\\\\\\\\", R"\\\\\\\\")\n self.assertEqual(Rf"\\\\", R"\\\\")\n self.assertEqual(Rf"\\\\\\\\", R"\\\\\\\\")\n\n def test_fstring_format_spec_greedy_matching(self):\n self.assertEqual(f"{1:}}}", "1}")\n self.assertEqual(f"{1:>3{5}}}}", " 1}")\n\n def test_yield(self):\n # Not terribly useful, but make sure the yield turns\n # a function into a generator\n def fn(y):\n f"y:{yield y*2}"\n f"{yield}"\n\n g = fn(4)\n self.assertEqual(next(g), 8)\n self.assertEqual(next(g), None)\n\n def test_yield_send(self):\n def fn(x):\n yield f"x:{yield (lambda i: x * i)}"\n\n g = fn(10)\n the_lambda = next(g)\n self.assertEqual(the_lambda(4), 40)\n self.assertEqual(g.send("string"), "x:string")\n\n def test_expressions_with_triple_quoted_strings(self):\n self.assertEqual(f"{\'\'\'x\'\'\'}", "x")\n self.assertEqual(f"{\'\'\'eric\'s\'\'\'}", "eric\'s")\n\n # Test concatenation within an expression\n self.assertEqual(f\'{"x" """eric"s""" "y"}\', \'xeric"sy\')\n self.assertEqual(f\'{"x" """eric"s"""}\', \'xeric"s\')\n self.assertEqual(f\'{"""eric"s""" "y"}\', \'eric"sy\')\n self.assertEqual(f\'{"""x""" """eric"s""" "y"}\', \'xeric"sy\')\n self.assertEqual(f\'{"""x""" """eric"s""" """y"""}\', \'xeric"sy\')\n self.assertEqual(f\'{r"""x""" """eric"s""" """y"""}\', \'xeric"sy\')\n\n def test_multiple_vars(self):\n x = 98\n y = "abc"\n self.assertEqual(f"{x}{y}", "98abc")\n\n self.assertEqual(f"X{x}{y}", "X98abc")\n self.assertEqual(f"{x}X{y}", "98Xabc")\n self.assertEqual(f"{x}{y}X", "98abcX")\n\n self.assertEqual(f"X{x}Y{y}", "X98Yabc")\n self.assertEqual(f"X{x}{y}Y", "X98abcY")\n self.assertEqual(f"{x}X{y}Y", "98XabcY")\n\n self.assertEqual(f"X{x}Y{y}Z", "X98YabcZ")\n\n def test_closure(self):\n def outer(x):\n def inner():\n return f"x:{x}"\n\n return inner\n\n self.assertEqual(outer("987")(), "x:987")\n self.assertEqual(outer(7)(), "x:7")\n\n def test_arguments(self):\n y = 2\n\n def f(x, width):\n return f"x={x*y:{width}}"\n\n self.assertEqual(f("foo", 10), "x=foofoo ")\n x = "bar"\n self.assertEqual(f(10, 10), "x= 20")\n\n def test_locals(self):\n value = 123\n self.assertEqual(f"v:{value}", "v:123")\n\n def test_missing_variable(self):\n with self.assertRaises(NameError):\n f"v:{value}"\n\n def test_missing_format_spec(self):\n class O:\n def __format__(self, spec):\n if not spec:\n return "*"\n return spec\n\n self.assertEqual(f"{O():x}", "x")\n self.assertEqual(f"{O()}", "*")\n self.assertEqual(f"{O():}", "*")\n\n self.assertEqual(f"{3:}", "3")\n self.assertEqual(f"{3!s:}", "3")\n\n def test_global(self):\n self.assertEqual(f"g:{a_global}", "g:global variable")\n self.assertEqual(f"g:{a_global!r}", "g:\'global variable\'")\n\n a_local = "local variable"\n self.assertEqual(\n f"g:{a_global} l:{a_local}", "g:global variable l:local variable"\n )\n self.assertEqual(f"g:{a_global!r}", "g:\'global variable\'")\n self.assertEqual(\n f"g:{a_global} l:{a_local!r}", "g:global variable l:\'local variable\'"\n )\n\n self.assertIn("module \'unittest\' from", f"{unittest}")\n\n def test_shadowed_global(self):\n a_global = "really a local"\n self.assertEqual(f"g:{a_global}", "g:really a local")\n self.assertEqual(f"g:{a_global!r}", "g:\'really a local\'")\n\n a_local = "local variable"\n self.assertEqual(\n f"g:{a_global} l:{a_local}", "g:really a local l:local variable"\n )\n self.assertEqual(f"g:{a_global!r}", "g:\'really a local\'")\n self.assertEqual(\n f"g:{a_global} l:{a_local!r}", "g:really a local l:\'local variable\'"\n )\n\n def test_call(self):\n def foo(x):\n return "x=" + str(x)\n\n self.assertEqual(f"{foo(10)}", "x=10")\n\n def test_nested_fstrings(self):\n y = 5\n self.assertEqual(f\'{f"{0}"*3}\', "000")\n self.assertEqual(f\'{f"{y}"*3}\', "555")\n\n def test_invalid_string_prefixes(self):\n single_quote_cases = [\n "fu\'\'",\n "uf\'\'",\n "Fu\'\'",\n "fU\'\'",\n "Uf\'\'",\n "uF\'\'",\n "ufr\'\'",\n "urf\'\'",\n "fur\'\'",\n "fru\'\'",\n "rfu\'\'",\n "ruf\'\'",\n "FUR\'\'",\n "Fur\'\'",\n "fb\'\'",\n "fB\'\'",\n "Fb\'\'",\n "FB\'\'",\n "bf\'\'",\n "bF\'\'",\n "Bf\'\'",\n "BF\'\'",\n ]\n double_quote_cases = [case.replace("\'", \'"\') for case in single_quote_cases]\n self.assertAllRaise(\n SyntaxError, "invalid syntax", single_quote_cases + double_quote_cases\n )\n\n def test_leading_trailing_spaces(self):\n self.assertEqual(f"{ 3}", "3")\n self.assertEqual(f"{ 3}", "3")\n self.assertEqual(f"{3 }", "3")\n self.assertEqual(f"{3 }", "3")\n\n self.assertEqual(f"expr={ {x: y for x, y in [(1, 2), ]}}", "expr={1: 2}")\n self.assertEqual(f"expr={ {x: y for x, y in [(1, 2), ]} }", "expr={1: 2}")\n\n def test_not_equal(self):\n # There\'s a special test for this because there\'s a special\n # case in the f-string parser to look for != as not ending an\n # expression. Normally it would, while looking for !s or !r.\n\n self.assertEqual(f"{3!=4}", "True")\n self.assertEqual(f"{3!=4:}", "True")\n self.assertEqual(f"{3!=4!s}", "True")\n self.assertEqual(f"{3!=4!s:.3}", "Tru")\n\n def test_equal_equal(self):\n # Because an expression ending in = has special meaning,\n # there\'s a special test for ==. Make sure it works.\n\n self.assertEqual(f"{0==1}", "False")\n\n def test_conversions(self):\n self.assertEqual(f"{3.14:10.10}", " 3.14")\n self.assertEqual(f"{3.14!s:10.10}", "3.14 ")\n self.assertEqual(f"{3.14!r:10.10}", "3.14 ")\n self.assertEqual(f"{3.14!a:10.10}", "3.14 ")\n\n self.assertEqual(f\'{"a"}\', "a")\n self.assertEqual(f\'{"a"!r}\', "\'a\'")\n self.assertEqual(f\'{"a"!a}\', "\'a\'")\n\n # Conversions can have trailing whitespace after them since it\n # does not provide any significance\n self.assertEqual(f"{3!s }", "3")\n self.assertEqual(f"{3.14!s :10.10}", "3.14 ")\n\n # Not a conversion.\n self.assertEqual(f\'{"a!r"}\', "a!r")\n\n # Not a conversion, but show that ! is allowed in a format spec.\n self.assertEqual(f"{3.14:!<10.10}", "3.14!!!!!!")\n\n self.assertAllRaise(\n SyntaxError,\n "f-string: expecting \'}\'",\n [\n "f\'{3!\'",\n "f\'{3!s\'",\n "f\'{3!g\'",\n ],\n )\n\n self.assertAllRaise(\n SyntaxError,\n "f-string: missing conversion character",\n [\n "f\'{3!}\'",\n "f\'{3!:\'",\n "f\'{3!:}\'",\n ],\n )\n\n for conv_identifier in "g", "A", "G", "ä", "ɐ":\n self.assertAllRaise(\n SyntaxError,\n "f-string: invalid conversion character %r: "\n "expected \'s\', \'r\', or \'a\'" % conv_identifier,\n ["f\'{3!" + conv_identifier + "}\'"],\n )\n\n for conv_non_identifier in "3", "!":\n self.assertAllRaise(\n SyntaxError,\n "f-string: invalid conversion character",\n ["f\'{3!" + conv_non_identifier + "}\'"],\n )\n\n for conv in " s", " s ":\n self.assertAllRaise(\n SyntaxError,\n "f-string: conversion type must come right after the"\n " exclamanation mark",\n ["f\'{3!" + conv + "}\'"],\n )\n\n self.assertAllRaise(\n SyntaxError,\n "f-string: invalid conversion character \'ss\': " "expected \'s\', \'r\', or \'a\'",\n [\n "f\'{3!ss}\'",\n "f\'{3!ss:}\'",\n "f\'{3!ss:s}\'",\n ],\n )\n\n def test_assignment(self):\n self.assertAllRaise(\n SyntaxError,\n r"invalid syntax",\n [\n "f\'\' = 3",\n "f\'{0}\' = x",\n "f\'{x}\' = x",\n ],\n )\n\n def test_del(self):\n self.assertAllRaise(\n SyntaxError,\n "invalid syntax",\n [\n "del f\'\'",\n "del \'\' f\'\'",\n ],\n )\n\n def test_mismatched_braces(self):\n self.assertAllRaise(\n SyntaxError,\n "f-string: single \'}\' is not allowed",\n [\n "f\'{{}\'",\n "f\'{{}}}\'",\n "f\'}\'",\n "f\'x}\'",\n "f\'x}x\'",\n r"f\'\\u007b}\'",\n # Can\'t have { or } in a format spec.\n "f\'{3:}>10}\'",\n "f\'{3:}}>10}\'",\n ],\n )\n\n self.assertAllRaise(\n SyntaxError,\n "f-string: expecting \'}\'",\n [\n "f\'{3\'",\n "f\'{3!\'",\n "f\'{3:\'",\n "f\'{3!s\'",\n "f\'{3!s:\'",\n "f\'{3!s:3\'",\n "f\'x{\'",\n "f\'x{x\'",\n "f\'{x\'",\n "f\'{3:s\'",\n "f\'{{{\'",\n "f\'{{}}{\'",\n "f\'{\'",\n "f\'{i=\'", # See gh-93418.\n ],\n )\n\n self.assertAllRaise(\n SyntaxError,\n "f-string: expecting a valid expression after \'{\'",\n [\n "f\'{3:{{>10}\'",\n ],\n )\n\n # But these are just normal strings.\n self.assertEqual(f\'{"{"}\', "{")\n self.assertEqual(f\'{"}"}\', "}")\n self.assertEqual(f\'{3:{"}"}>10}\', "}}}}}}}}}3")\n self.assertEqual(f\'{2:{"{"}>10}\', "{{{{{{{{{2")\n\n def test_if_conditional(self):\n # There\'s special logic in compile.c to test if the\n # conditional for an if (and while) are constants. Exercise\n # that code.\n\n def test_fstring(x, expected):\n flag = 0\n if f"{x}":\n flag = 1\n else:\n flag = 2\n self.assertEqual(flag, expected)\n\n def test_concat_empty(x, expected):\n flag = 0\n if "" f"{x}":\n flag = 1\n else:\n flag = 2\n self.assertEqual(flag, expected)\n\n def test_concat_non_empty(x, expected):\n flag = 0\n if " " f"{x}":\n flag = 1\n else:\n flag = 2\n self.assertEqual(flag, expected)\n\n test_fstring("", 2)\n test_fstring(" ", 1)\n\n test_concat_empty("", 2)\n test_concat_empty(" ", 1)\n\n test_concat_non_empty("", 1)\n test_concat_non_empty(" ", 1)\n\n def test_empty_format_specifier(self):\n x = "test"\n self.assertEqual(f"{x}", "test")\n self.assertEqual(f"{x:}", "test")\n self.assertEqual(f"{x!s:}", "test")\n self.assertEqual(f"{x!r:}", "\'test\'")\n\n def test_str_format_differences(self):\n d = {\n "a": "string",\n 0: "integer",\n }\n a = 0\n self.assertEqual(f"{d[0]}", "integer")\n self.assertEqual(f\'{d["a"]}\', "string")\n self.assertEqual(f"{d[a]}", "integer")\n self.assertEqual("{d[a]}".format(d=d), "string")\n self.assertEqual("{d[0]}".format(d=d), "integer")\n\n def test_errors(self):\n # see issue 26287\n self.assertAllRaise(\n TypeError,\n "unsupported",\n [\n r"f\'{(lambda: 0):x}\'",\n r"f\'{(0,):x}\'",\n ],\n )\n self.assertAllRaise(\n ValueError,\n "Unknown format code",\n [\n r"f\'{1000:j}\'",\n r"f\'{1000:j}\'",\n ],\n )\n\n def test_filename_in_syntaxerror(self):\n # see issue 38964\n with temp_cwd() as cwd:\n file_path = os.path.join(cwd, "t.py")\n with open(file_path, "w", encoding="utf-8") as f:\n f.write(\'f"{a b}"\') # This generates a SyntaxError\n _, _, stderr = assert_python_failure(file_path, PYTHONIOENCODING="ascii")\n self.assertIn(file_path.encode("ascii", "backslashreplace"), stderr)\n\n def test_loop(self):\n for i in range(1000):\n self.assertEqual(f"i:{i}", "i:" + str(i))\n\n def test_dict(self):\n d = {\n \'"\': "dquote",\n "\'": "squote",\n "foo": "bar",\n }\n self.assertEqual(f"""{d["\'"]}""", "squote")\n self.assertEqual(f"""{d[\'"\']}""", "dquote")\n\n self.assertEqual(f\'{d["foo"]}\', "bar")\n self.assertEqual(f"{d[\'foo\']}", "bar")\n\n def test_backslash_char(self):\n # Check eval of a backslash followed by a control char.\n # See bpo-30682: this used to raise an assert in pydebug mode.\n self.assertEqual(eval(\'f"\\\\\\n"\'), "")\n self.assertEqual(eval(\'f"\\\\\\r"\'), "")\n\n def test_debug_conversion(self):\n x = "A string"\n self.assertEqual(f"{x=}", "x=" + repr(x))\n self.assertEqual(f"{x =}", "x =" + repr(x))\n self.assertEqual(f"{x=!s}", "x=" + str(x))\n self.assertEqual(f"{x=!r}", "x=" + repr(x))\n self.assertEqual(f"{x=!a}", "x=" + ascii(x))\n\n x = 2.71828\n self.assertEqual(f"{x=:.2f}", "x=" + format(x, ".2f"))\n self.assertEqual(f"{x=:}", "x=" + format(x, ""))\n self.assertEqual(f"{x=!r:^20}", "x=" + format(repr(x), "^20"))\n self.assertEqual(f"{x=!s:^20}", "x=" + format(str(x), "^20"))\n self.assertEqual(f"{x=!a:^20}", "x=" + format(ascii(x), "^20"))\n\n x = 9\n self.assertEqual(f"{3*x+15=}", "3*x+15=42")\n\n # There is code in ast.c that deals with non-ascii expression values. So,\n # use a unicode identifier to trigger that.\n tenπ = 31.4\n self.assertEqual(f"{tenπ=:.2f}", "tenπ=31.40")\n\n # Also test with Unicode in non-identifiers.\n self.assertEqual(f\'{"Σ"=}\', "\\"Σ\\"=\'Σ\'")\n\n # Make sure nested fstrings still work.\n self.assertEqual(f\'{f"{3.1415=:.1f}":*^20}\', "*****3.1415=3.1*****")\n\n # Make sure text before and after an expression with = works\n # correctly.\n pi = "π"\n self.assertEqual(f"alpha α {pi=} ω omega", "alpha α pi=\'π\' ω omega")\n\n # Check multi-line expressions.\n self.assertEqual(\n f"""{\n3\n=}""",\n "\\n3\\n=3",\n )\n\n # Since = is handled specially, make sure all existing uses of\n # it still work.\n\n self.assertEqual(f"{0==1}", "False")\n self.assertEqual(f"{0!=1}", "True")\n self.assertEqual(f"{0<=1}", "True")\n self.assertEqual(f"{0>=1}", "False")\n self.assertEqual(f\'{(x:="5")}\', "5")\n self.assertEqual(x, "5")\n self.assertEqual(f"{(x:=5)}", "5")\n self.assertEqual(x, 5)\n self.assertEqual(f\'{"="}\', "=")\n\n x = 20\n # This isn\'t an assignment expression, it\'s \'x\', with a format\n # spec of \'=10\'. See test_walrus: you need to use parens.\n self.assertEqual(f"{x:=10}", " 20")\n\n # Test named function parameters, to make sure \'=\' parsing works\n # there.\n def f(a):\n nonlocal x\n oldx = x\n x = a\n return oldx\n\n x = 0\n self.assertEqual(f\'{f(a="3=")}\', "0")\n self.assertEqual(x, "3=")\n self.assertEqual(f"{f(a=4)}", "3=")\n self.assertEqual(x, 4)\n\n # Check debug expressions in format spec\n y = 20\n self.assertEqual(f"{2:{y=}}", "yyyyyyyyyyyyyyyyyyy2")\n self.assertEqual(\n f"{datetime.datetime.now():h1{y=}h2{y=}h3{y=}}", "h1y=20h2y=20h3y=20"\n )\n\n # Make sure __format__ is being called.\n class C:\n def __format__(self, s):\n return f"FORMAT-{s}"\n\n def __repr__(self):\n return "REPR"\n\n self.assertEqual(f"{C()=}", "C()=REPR")\n self.assertEqual(f"{C()=!r}", "C()=REPR")\n self.assertEqual(f"{C()=:}", "C()=FORMAT-")\n self.assertEqual(f"{C()=: }", "C()=FORMAT- ")\n self.assertEqual(f"{C()=:x}", "C()=FORMAT-x")\n self.assertEqual(f"{C()=!r:*^20}", "C()=********REPR********")\n self.assertEqual(f"{C():{20=}}", "FORMAT-20=20")\n\n self.assertRaises(SyntaxError, eval, "f\'{C=]\'")\n\n # Make sure leading and following text works.\n x = "foo"\n self.assertEqual(f"X{x=}Y", "Xx=" + repr(x) + "Y")\n\n # Make sure whitespace around the = works.\n self.assertEqual(f"X{x =}Y", "Xx =" + repr(x) + "Y")\n self.assertEqual(f"X{x= }Y", "Xx= " + repr(x) + "Y")\n self.assertEqual(f"X{x = }Y", "Xx = " + repr(x) + "Y")\n self.assertEqual(f"sadsd {1 + 1 = :{1 + 1:1d}f}", "sadsd 1 + 1 = 2.000000")\n\n self.assertEqual(\n f"{1+2 = # my comment\n }",\n "1+2 = \\n 3",\n )\n\n # These next lines contains tabs. Backslash escapes don\'t\n # work in f-strings.\n # patchcheck doesn\'t like these tabs. So the only way to test\n # this will be to dynamically created and exec the f-strings. But\n # that\'s such a hassle I\'ll save it for another day. For now, convert\n # the tabs to spaces just to shut up patchcheck.\n # self.assertEqual(f\'X{x =}Y\', \'Xx\\t=\'+repr(x)+\'Y\')\n # self.assertEqual(f\'X{x = }Y\', \'Xx\\t=\\t\'+repr(x)+\'Y\')\n\n def test_debug_expressions_are_raw_strings(self):\n\n self.assertEqual(f\'{b"\\N{OX}"=}\', \'b"\\\\N{OX}"=b\\\'\\\\\\\\N{OX}\\\'\')\n self.assertEqual(f\'{r"\\xff"=}\', \'r"\\\\xff"=\\\'\\\\\\\\xff\\\'\')\n self.assertEqual(f\'{r"\\n"=}\', \'r"\\\\n"=\\\'\\\\\\\\n\\\'\')\n self.assertEqual(f"{\'\\\'\'=}", "\'\\\\\'\'=\\"\'\\"")\n self.assertEqual(f\'{\'\\xc5\'=}\', r"\'\\xc5\'=\'Å\'")\n\n def test_walrus(self):\n x = 20\n # This isn\'t an assignment expression, it\'s \'x\', with a format\n # spec of \'=10\'.\n self.assertEqual(f"{x:=10}", " 20")\n\n # This is an assignment expression, which requires parens.\n self.assertEqual(f"{(x:=10)}", "10")\n self.assertEqual(x, 10)\n\n def test_invalid_syntax_error_message(self):\n with self.assertRaisesRegex(\n SyntaxError, "f-string: expecting \'=\', or \'!\', or \':\', or \'}\'"\n ):\n compile("f\'{a $ b}\'", "?", "exec")\n\n def test_with_two_commas_in_format_specifier(self):\n error_msg = re.escape("Cannot specify \',\' with \',\'.")\n with self.assertRaisesRegex(ValueError, error_msg):\n f"{1:,,}"\n\n def test_with_two_underscore_in_format_specifier(self):\n error_msg = re.escape("Cannot specify \'_\' with \'_\'.")\n with self.assertRaisesRegex(ValueError, error_msg):\n f"{1:__}"\n\n def test_with_a_commas_and_an_underscore_in_format_specifier(self):\n error_msg = re.escape("Cannot specify both \',\' and \'_\'.")\n with self.assertRaisesRegex(ValueError, error_msg):\n f"{1:,_}"\n\n def test_with_an_underscore_and_a_comma_in_format_specifier(self):\n error_msg = re.escape("Cannot specify both \',\' and \'_\'.")\n with self.assertRaisesRegex(ValueError, error_msg):\n f"{1:_,}"\n\n def test_syntax_error_for_starred_expressions(self):\n with self.assertRaisesRegex(SyntaxError, "can\'t use starred expression here"):\n compile("f\'{*a}\'", "?", "exec")\n\n with self.assertRaisesRegex(\n SyntaxError, "f-string: expecting a valid expression after \'{\'"\n ):\n compile("f\'{**a}\'", "?", "exec")\n\n def test_not_closing_quotes(self):\n self.assertAllRaise(SyntaxError, "unterminated f-string literal", [\'f"\', "f\'"])\n self.assertAllRaise(\n SyntaxError, "unterminated triple-quoted f-string literal", [\'f"""\', "f\'\'\'"]\n )\n # Ensure that the errors are reported at the correct line number.\n data = \'\'\'\\\nx = 1 + 1\ny = 2 + 2\nz = f"""\nsdfjnsdfjsdf\nsdfsdfs{1+\n2} dfigdf {3+\n4}sdufsd""\n\'\'\'\n try:\n compile(data, "?", "exec")\n except SyntaxError as e:\n self.assertEqual(e.text, \'z = f"""\')\n self.assertEqual(e.lineno, 3)\n\n def test_syntax_error_after_debug(self):\n self.assertAllRaise(\n SyntaxError,\n "f-string: expecting a valid expression after \'{\'",\n [\n "f\'{1=}{;\'",\n "f\'{1=}{+;\'",\n "f\'{1=}{2}{;\'",\n "f\'{1=}{3}{;\'",\n ],\n )\n self.assertAllRaise(\n SyntaxError,\n "f-string: expecting \'=\', or \'!\', or \':\', or \'}\'",\n [\n "f\'{1=}{1;\'",\n "f\'{1=}{1;}\'",\n ],\n )\n\n def test_debug_in_file(self):\n with temp_cwd():\n script = "script.py"\n with open("script.py", "w") as f:\n f.write(f"""\\\nprint(f\'\'\'{{\n3\n=}}\'\'\')""")\n\n _, stdout, _ = assert_python_ok(script)\n self.assertEqual(\n stdout.decode("utf-8").strip().replace("\\r\\n", "\\n").replace("\\r", "\\n"),\n "3\\n=3",\n )\n\n def test_syntax_warning_infinite_recursion_in_file(self):\n with temp_cwd():\n script = "script.py"\n with open(script, "w") as f:\n f.write(r"print(f\'\\{1}\')")\n\n _, stdout, stderr = assert_python_ok(script)\n self.assertIn(rb"\\1", stdout)\n self.assertEqual(len(stderr.strip().splitlines()), 2)\n\n def test_gh129093(self):\n self.assertEqual(f\'{1==2=}\', \'1==2=False\')\n self.assertEqual(f\'{1 == 2=}\', \'1 == 2=False\')\n self.assertEqual(f\'{1!=2=}\', \'1!=2=True\')\n self.assertEqual(f\'{1 != 2=}\', \'1 != 2=True\')\n\n self.assertEqual(f\'{(1) != 2=}\', \'(1) != 2=True\')\n self.assertEqual(f\'{(1*2) != (3)=}\', \'(1*2) != (3)=True\')\n\n self.assertEqual(f\'{1 != 2 == 3 != 4=}\', \'1 != 2 == 3 != 4=False\')\n self.assertEqual(f\'{1 == 2 != 3 == 4=}\', \'1 == 2 != 3 == 4=False\')\n\n self.assertEqual(f\'{f\'{1==2=}\'=}\', "f\'{1==2=}\'=\'1==2=False\'")\n self.assertEqual(f\'{f\'{1 == 2=}\'=}\', "f\'{1 == 2=}\'=\'1 == 2=False\'")\n self.assertEqual(f\'{f\'{1!=2=}\'=}\', "f\'{1!=2=}\'=\'1!=2=True\'")\n self.assertEqual(f\'{f\'{1 != 2=}\'=}\', "f\'{1 != 2=}\'=\'1 != 2=True\'")\n\n\nif __name__ == "__main__":\n\n unittest.main()\n', ast_parse_kwargs={}, filename=PosixPath('/Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/test_fstring.py'))
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/test_unparse.py", line 138, in check_ast_roundtrip
self.assertASTEqual(ast1, ast2)
^^^^^^^^^^^^^^
File "/Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/support/ast_helper.py", line 43, in assertASTEqual
traverse_compare(ast1, ast2)
File "/Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/support/ast_helper.py", line 24, in traverse_compare
traverse_compare(value1, value2)
File "/Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/support/ast_helper.py", line 28, in traverse_compare
traverse_compare(node1, node2)
File "/Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/support/ast_helper.py", line 24, in traverse_compare
traverse_compare(value1, value2)
File "/Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/support/ast_helper.py", line 28, in traverse_compare
traverse_compare(node1, node2)
File "/Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/support/ast_helper.py", line 24, in traverse_compare
traverse_compare(value1, value2)
File "/Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/support/ast_helper.py", line 28, in traverse_compare
traverse_compare(node1, node2)
File "/Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/support/ast_helper.py", line 24, in traverse_compare
traverse_compare(value1, value2)
File "/Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/support/ast_helper.py", line 24, in traverse_compare
traverse_compare(value1, value2)
File "/Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/support/ast_helper.py", line 28, in traverse_compare
traverse_compare(node1, node2)
File "/Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/support/ast_helper.py", line 24, in traverse_compare
traverse_compare(value1, value2)
File "/Users/matti/build-worker-x86_64/pypy-c-jit-macos-x86-64/build/lib-python/3/test/support/ast_helper.py", line 40, in traverse_compare
raise self.failureException(msg) from None
AssertionError: First list contains 1 additional elements.
----------------------------------------------------------------------
Ran 69 tests in 1.193s
FAILED (failures=3)
0:00:01 load avg: 1.41 [1/1/1] test_unparse failed (3 failures)
== Tests result: FAILURE ==
1 test failed:
test_unparse
Total duration: 1.3 sec
Total tests: run=69 failures=3
Total test files: run=1/1 failed=1
Result: FAILURE
<unknown>:1769: SyntaxWarning: invalid escape sequence '\N'
test test_unparse failed
builder: pypy-c-jit-macos-x86-64 build #1326+
test: lib-python/3/test/test_unparse.py::unmodified