summaryrefslogtreecommitdiffstats
path: root/Lib/test
diff options
context:
space:
mode:
authorEric V. Smith <eric@trueblade.com>2016-09-10 01:56:20 (GMT)
committerEric V. Smith <eric@trueblade.com>2016-09-10 01:56:20 (GMT)
commit451d0e38fcf50d976236d7d00ccfe8c1a2305086 (patch)
tree29e15833e76714f8f0f8b906871b82c8c1a42967 /Lib/test
parent052828db1538bf0d42d7e256da13c6e183974a13 (diff)
downloadcpython-451d0e38fcf50d976236d7d00ccfe8c1a2305086.zip
cpython-451d0e38fcf50d976236d7d00ccfe8c1a2305086.tar.gz
cpython-451d0e38fcf50d976236d7d00ccfe8c1a2305086.tar.bz2
Issue 27948: Allow backslashes in the literal string portion of f-strings, but not in the expressions. Also, require expressions to begin and end with literal curly braces.
Diffstat (limited to 'Lib/test')
-rw-r--r--Lib/test/libregrtest/save_env.py2
-rw-r--r--Lib/test/test_faulthandler.py4
-rw-r--r--Lib/test/test_fstring.py132
-rw-r--r--Lib/test/test_tools/test_unparse.py10
-rw-r--r--Lib/test/test_traceback.py28
5 files changed, 108 insertions, 68 deletions
diff --git a/Lib/test/libregrtest/save_env.py b/Lib/test/libregrtest/save_env.py
index eefbc14..96ad3af 100644
--- a/Lib/test/libregrtest/save_env.py
+++ b/Lib/test/libregrtest/save_env.py
@@ -280,6 +280,6 @@ class saved_test_environment:
print(f"Warning -- {name} was modified by {self.testname}",
file=sys.stderr, flush=True)
if self.verbose > 1:
- print(f" Before: {original}""\n"f" After: {current} ",
+ print(f" Before: {original}\n After: {current} ",
file=sys.stderr, flush=True)
return False
diff --git a/Lib/test/test_faulthandler.py b/Lib/test/test_faulthandler.py
index d2bd2d2..22ccbc9 100644
--- a/Lib/test/test_faulthandler.py
+++ b/Lib/test/test_faulthandler.py
@@ -735,11 +735,11 @@ class FaultHandlerTests(unittest.TestCase):
('EXCEPTION_INT_DIVIDE_BY_ZERO', 'int divide by zero'),
('EXCEPTION_STACK_OVERFLOW', 'stack overflow'),
):
- self.check_windows_exception("""
+ self.check_windows_exception(f"""
import faulthandler
faulthandler.enable()
faulthandler._raise_exception(faulthandler._{exc})
- """.format(exc=exc),
+ """,
3,
name)
diff --git a/Lib/test/test_fstring.py b/Lib/test/test_fstring.py
index 2ba1b21..e61f635 100644
--- a/Lib/test/test_fstring.py
+++ b/Lib/test/test_fstring.py
@@ -119,6 +119,14 @@ f'{a * x()}'"""
self.assertEqual(f'a}}', 'a}')
self.assertEqual(f'}}b', '}b')
self.assertEqual(f'a}}b', 'a}b')
+ self.assertEqual(f'{{}}', '{}')
+ self.assertEqual(f'a{{}}', 'a{}')
+ self.assertEqual(f'{{b}}', '{b}')
+ self.assertEqual(f'{{}}c', '{}c')
+ self.assertEqual(f'a{{b}}', 'a{b}')
+ self.assertEqual(f'a{{}}c', 'a{}c')
+ self.assertEqual(f'{{b}}c', '{b}c')
+ self.assertEqual(f'a{{b}}c', 'a{b}c')
self.assertEqual(f'{{{10}', '{10')
self.assertEqual(f'}}{10}', '}10')
@@ -302,56 +310,79 @@ f'{a * x()}'"""
["f'{\n}'",
])
- def test_no_backslashes(self):
- # See issue 27921
-
- # These should work, but currently don't
- self.assertAllRaise(SyntaxError, 'backslashes not allowed',
- [r"f'\t'",
- r"f'{2}\t'",
- r"f'{2}\t{3}'",
- r"f'\t{3}'",
-
- r"f'\N{GREEK CAPITAL LETTER DELTA}'",
- r"f'{2}\N{GREEK CAPITAL LETTER DELTA}'",
- r"f'{2}\N{GREEK CAPITAL LETTER DELTA}{3}'",
- r"f'\N{GREEK CAPITAL LETTER DELTA}{3}'",
-
- r"f'\u0394'",
- r"f'{2}\u0394'",
- r"f'{2}\u0394{3}'",
- r"f'\u0394{3}'",
-
- r"f'\U00000394'",
- r"f'{2}\U00000394'",
- r"f'{2}\U00000394{3}'",
- r"f'\U00000394{3}'",
-
- r"f'\x20'",
- r"f'{2}\x20'",
- r"f'{2}\x20{3}'",
- r"f'\x20{3}'",
-
- r"f'2\x20'",
- r"f'2\x203'",
- r"f'2\x203'",
+ def test_backslashes_in_string_part(self):
+ self.assertEqual(f'\t', '\t')
+ self.assertEqual(r'\t', '\\t')
+ self.assertEqual(rf'\t', '\\t')
+ self.assertEqual(f'{2}\t', '2\t')
+ self.assertEqual(f'{2}\t{3}', '2\t3')
+ self.assertEqual(f'\t{3}', '\t3')
+
+ self.assertEqual(f'\u0394', '\u0394')
+ self.assertEqual(r'\u0394', '\\u0394')
+ self.assertEqual(rf'\u0394', '\\u0394')
+ self.assertEqual(f'{2}\u0394', '2\u0394')
+ self.assertEqual(f'{2}\u0394{3}', '2\u03943')
+ self.assertEqual(f'\u0394{3}', '\u03943')
+
+ self.assertEqual(f'\U00000394', '\u0394')
+ self.assertEqual(r'\U00000394', '\\U00000394')
+ self.assertEqual(rf'\U00000394', '\\U00000394')
+ self.assertEqual(f'{2}\U00000394', '2\u0394')
+ self.assertEqual(f'{2}\U00000394{3}', '2\u03943')
+ self.assertEqual(f'\U00000394{3}', '\u03943')
+
+ self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}', '\u0394')
+ self.assertEqual(f'{2}\N{GREEK CAPITAL LETTER DELTA}', '2\u0394')
+ self.assertEqual(f'{2}\N{GREEK CAPITAL LETTER DELTA}{3}', '2\u03943')
+ self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}{3}', '\u03943')
+ self.assertEqual(f'2\N{GREEK CAPITAL LETTER DELTA}', '2\u0394')
+ self.assertEqual(f'2\N{GREEK CAPITAL LETTER DELTA}3', '2\u03943')
+ self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}3', '\u03943')
+
+ self.assertEqual(f'\x20', ' ')
+ self.assertEqual(r'\x20', '\\x20')
+ self.assertEqual(rf'\x20', '\\x20')
+ self.assertEqual(f'{2}\x20', '2 ')
+ self.assertEqual(f'{2}\x20{3}', '2 3')
+ self.assertEqual(f'\x20{3}', ' 3')
+
+ self.assertEqual(f'2\x20', '2 ')
+ self.assertEqual(f'2\x203', '2 3')
+ self.assertEqual(f'\x203', ' 3')
+
+ def test_misformed_unicode_character_name(self):
+ # These test are needed because unicode names are parsed
+ # differently inside f-strings.
+ self.assertAllRaise(SyntaxError, r"\(unicode error\) 'unicodeescape' codec can't decode bytes in position .*: malformed \\N character escape",
+ [r"f'\N'",
+ r"f'\N{'",
+ r"f'\N{GREEK CAPITAL LETTER DELTA'",
+
+ # Here are the non-f-string versions,
+ # which should give the same errors.
+ r"'\N'",
+ r"'\N{'",
+ r"'\N{GREEK CAPITAL LETTER DELTA'",
])
- # And these don't work now, and shouldn't work in the future.
- self.assertAllRaise(SyntaxError, 'backslashes not allowed',
+ def test_no_backslashes_in_expression_part(self):
+ self.assertAllRaise(SyntaxError, 'f-string expression part cannot include a backslash',
[r"f'{\'a\'}'",
r"f'{\t3}'",
+ r"f'{\}'",
+ r"rf'{\'a\'}'",
+ r"rf'{\t3}'",
+ r"rf'{\}'",
+ r"""rf'{"\N{LEFT CURLY BRACKET}"}'""",
])
- # add this when backslashes are allowed again. see issue 27921
- # these test will be needed because unicode names will be parsed
- # differently once backslashes are allowed inside expressions
- ## def test_misformed_unicode_character_name(self):
- ## self.assertAllRaise(SyntaxError, 'xx',
- ## [r"f'\N'",
- ## [r"f'\N{'",
- ## [r"f'\N{GREEK CAPITAL LETTER DELTA'",
- ## ])
+ def test_no_escapes_for_braces(self):
+ # \x7b is '{'. Make sure it doesn't start an expression.
+ self.assertEqual(f'\x7b2}}', '{2}')
+ self.assertEqual(f'\x7b2', '{2')
+ self.assertEqual(f'\u007b2', '{2')
+ self.assertEqual(f'\N{LEFT CURLY BRACKET}2\N{RIGHT CURLY BRACKET}', '{2}')
def test_newlines_in_expressions(self):
self.assertEqual(f'{0}', '0')
@@ -509,6 +540,14 @@ f'{a * x()}'"""
"ruf''",
"FUR''",
"Fur''",
+ "fb''",
+ "fB''",
+ "Fb''",
+ "FB''",
+ "bf''",
+ "bF''",
+ "Bf''",
+ "BF''",
])
def test_leading_trailing_spaces(self):
@@ -551,8 +590,8 @@ f'{a * x()}'"""
self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character',
["f'{3!g}'",
"f'{3!A}'",
- "f'{3!A}'",
- "f'{3!A}'",
+ "f'{3!3}'",
+ "f'{3!G}'",
"f'{3!!}'",
"f'{3!:}'",
"f'{3! s}'", # no space before conversion char
@@ -601,6 +640,7 @@ f'{a * x()}'"""
"f'{3!s:3'",
"f'x{'",
"f'x{x'",
+ "f'{x'",
"f'{3:s'",
"f'{{{'",
"f'{{}}{'",
diff --git a/Lib/test/test_tools/test_unparse.py b/Lib/test/test_tools/test_unparse.py
index ed0001a..65dee1b 100644
--- a/Lib/test/test_tools/test_unparse.py
+++ b/Lib/test/test_tools/test_unparse.py
@@ -285,12 +285,12 @@ class DirectoryTestCase(ASTTestCase):
if test.support.verbose:
print('Testing %s' % filename)
- # it's very much a hack that I'm skipping these files, but
- # I can't figure out why they fail. I'll fix it when I
- # address issue #27948.
- if os.path.basename(filename) in ('test_fstring.py', 'test_traceback.py'):
+ # Some f-strings are not correctly round-tripped by
+ # Tools/parser/unparse.py. See issue 28002 for details.
+ # We need to skip files that contain such f-strings.
+ if os.path.basename(filename) in ('test_fstring.py', ):
if test.support.verbose:
- print(f'Skipping {filename}: see issue 27921')
+ print(f'Skipping {filename}: see issue 28002')
continue
with self.subTest(filename=filename):
diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py
index 446b91e..037d883 100644
--- a/Lib/test/test_traceback.py
+++ b/Lib/test/test_traceback.py
@@ -326,13 +326,13 @@ class TracebackFormatTests(unittest.TestCase):
lineno_f = f.__code__.co_firstlineno
result_f = (
'Traceback (most recent call last):\n'
- f' File "{__file__}", line {lineno_f+5}, in _check_recursive_traceback_display''\n'
+ f' File "{__file__}", line {lineno_f+5}, in _check_recursive_traceback_display\n'
' f()\n'
- f' File "{__file__}", line {lineno_f+1}, in f''\n'
+ f' File "{__file__}", line {lineno_f+1}, in f\n'
' f()\n'
- f' File "{__file__}", line {lineno_f+1}, in f''\n'
+ f' File "{__file__}", line {lineno_f+1}, in f\n'
' f()\n'
- f' File "{__file__}", line {lineno_f+1}, in f''\n'
+ f' File "{__file__}", line {lineno_f+1}, in f\n'
' f()\n'
# XXX: The following line changes depending on whether the tests
# are run through the interactive interpreter or with -m
@@ -371,20 +371,20 @@ class TracebackFormatTests(unittest.TestCase):
lineno_g = g.__code__.co_firstlineno
result_g = (
- f' File "{__file__}", line {lineno_g+2}, in g''\n'
+ f' File "{__file__}", line {lineno_g+2}, in g\n'
' return g(count-1)\n'
- f' File "{__file__}", line {lineno_g+2}, in g''\n'
+ f' File "{__file__}", line {lineno_g+2}, in g\n'
' return g(count-1)\n'
- f' File "{__file__}", line {lineno_g+2}, in g''\n'
+ f' File "{__file__}", line {lineno_g+2}, in g\n'
' return g(count-1)\n'
' [Previous line repeated 6 more times]\n'
- f' File "{__file__}", line {lineno_g+3}, in g''\n'
+ f' File "{__file__}", line {lineno_g+3}, in g\n'
' raise ValueError\n'
'ValueError\n'
)
tb_line = (
'Traceback (most recent call last):\n'
- f' File "{__file__}", line {lineno_g+7}, in _check_recursive_traceback_display''\n'
+ f' File "{__file__}", line {lineno_g+7}, in _check_recursive_traceback_display\n'
' g()\n'
)
expected = (tb_line + result_g).splitlines()
@@ -408,16 +408,16 @@ class TracebackFormatTests(unittest.TestCase):
lineno_h = h.__code__.co_firstlineno
result_h = (
'Traceback (most recent call last):\n'
- f' File "{__file__}", line {lineno_h+7}, in _check_recursive_traceback_display''\n'
+ f' File "{__file__}", line {lineno_h+7}, in _check_recursive_traceback_display\n'
' h()\n'
- f' File "{__file__}", line {lineno_h+2}, in h''\n'
+ f' File "{__file__}", line {lineno_h+2}, in h\n'
' return h(count-1)\n'
- f' File "{__file__}", line {lineno_h+2}, in h''\n'
+ f' File "{__file__}", line {lineno_h+2}, in h\n'
' return h(count-1)\n'
- f' File "{__file__}", line {lineno_h+2}, in h''\n'
+ f' File "{__file__}", line {lineno_h+2}, in h\n'
' return h(count-1)\n'
' [Previous line repeated 6 more times]\n'
- f' File "{__file__}", line {lineno_h+3}, in h''\n'
+ f' File "{__file__}", line {lineno_h+3}, in h\n'
' g()\n'
)
expected = (result_h + result_g).splitlines()