@@ -119,6 +119,14 @@ def test_double_braces(self):
119119 self .assertEqual (f'a}}' , 'a}' )
120120 self .assertEqual (f'}}b' , '}b' )
121121 self .assertEqual (f'a}}b' , 'a}b' )
122+ self .assertEqual (f'{{}}' , '{}' )
123+ self .assertEqual (f'a{{}}' , 'a{}' )
124+ self .assertEqual (f'{{b}}' , '{b}' )
125+ self .assertEqual (f'{{}}c' , '{}c' )
126+ self .assertEqual (f'a{{b}}' , 'a{b}' )
127+ self .assertEqual (f'a{{}}c' , 'a{}c' )
128+ self .assertEqual (f'{{b}}c' , '{b}c' )
129+ self .assertEqual (f'a{{b}}c' , 'a{b}c' )
122130
123131 self .assertEqual (f'{{{ 10 } ' , '{10' )
124132 self .assertEqual (f'}}{ 10 } ' , '}10' )
@@ -302,56 +310,79 @@ def test_parens_in_expressions(self):
302310 ["f'{\n }'" ,
303311 ])
304312
305- def test_no_backslashes (self ):
306- # See issue 27921
307-
308- # These should work, but currently don't
309- self .assertAllRaise (SyntaxError , 'backslashes not allowed' ,
310- [r"f'\t'" ,
311- r"f'{2}\t'" ,
312- r"f'{2}\t{3}'" ,
313- r"f'\t{3}'" ,
314-
315- r"f'\N{GREEK CAPITAL LETTER DELTA}'" ,
316- r"f'{2}\N{GREEK CAPITAL LETTER DELTA}'" ,
317- r"f'{2}\N{GREEK CAPITAL LETTER DELTA}{3}'" ,
318- r"f'\N{GREEK CAPITAL LETTER DELTA}{3}'" ,
319-
320- r"f'\u0394'" ,
321- r"f'{2}\u0394'" ,
322- r"f'{2}\u0394{3}'" ,
323- r"f'\u0394{3}'" ,
324-
325- r"f'\U00000394'" ,
326- r"f'{2}\U00000394'" ,
327- r"f'{2}\U00000394{3}'" ,
328- r"f'\U00000394{3}'" ,
329-
330- r"f'\x20'" ,
331- r"f'{2}\x20'" ,
332- r"f'{2}\x20{3}'" ,
333- r"f'\x20{3}'" ,
334-
335- r"f'2\x20'" ,
336- r"f'2\x203'" ,
337- r"f'2\x203'" ,
313+ def test_backslashes_in_string_part (self ):
314+ self .assertEqual (f'\t ' , '\t ' )
315+ self .assertEqual (r'\t' , '\\ t' )
316+ self .assertEqual (rf'\t' , '\\ t' )
317+ self .assertEqual (f'{ 2 } \t ' , '2\t ' )
318+ self .assertEqual (f'{ 2 } \t { 3 } ' , '2\t 3' )
319+ self .assertEqual (f'\t { 3 } ' , '\t 3' )
320+
321+ self .assertEqual (f'\u0394 ' , '\u0394 ' )
322+ self .assertEqual (r'\u0394' , '\\ u0394' )
323+ self .assertEqual (rf'\u0394' , '\\ u0394' )
324+ self .assertEqual (f'{ 2 } \u0394 ' , '2\u0394 ' )
325+ self .assertEqual (f'{ 2 } \u0394 { 3 } ' , '2\u0394 3' )
326+ self .assertEqual (f'\u0394 { 3 } ' , '\u0394 3' )
327+
328+ self .assertEqual (f'\U00000394 ' , '\u0394 ' )
329+ self .assertEqual (r'\U00000394' , '\\ U00000394' )
330+ self .assertEqual (rf'\U00000394' , '\\ U00000394' )
331+ self .assertEqual (f'{ 2 } \U00000394 ' , '2\u0394 ' )
332+ self .assertEqual (f'{ 2 } \U00000394 { 3 } ' , '2\u0394 3' )
333+ self .assertEqual (f'\U00000394 { 3 } ' , '\u0394 3' )
334+
335+ self .assertEqual (f'\N{GREEK CAPITAL LETTER DELTA} ' , '\u0394 ' )
336+ self .assertEqual (f'{ 2 } \N{GREEK CAPITAL LETTER DELTA} ' , '2\u0394 ' )
337+ self .assertEqual (f'{ 2 } \N{GREEK CAPITAL LETTER DELTA} { 3 } ' , '2\u0394 3' )
338+ self .assertEqual (f'\N{GREEK CAPITAL LETTER DELTA} { 3 } ' , '\u0394 3' )
339+ self .assertEqual (f'2\N{GREEK CAPITAL LETTER DELTA} ' , '2\u0394 ' )
340+ self .assertEqual (f'2\N{GREEK CAPITAL LETTER DELTA} 3' , '2\u0394 3' )
341+ self .assertEqual (f'\N{GREEK CAPITAL LETTER DELTA} 3' , '\u0394 3' )
342+
343+ self .assertEqual (f'\x20 ' , ' ' )
344+ self .assertEqual (r'\x20' , '\\ x20' )
345+ self .assertEqual (rf'\x20' , '\\ x20' )
346+ self .assertEqual (f'{ 2 } \x20 ' , '2 ' )
347+ self .assertEqual (f'{ 2 } \x20 { 3 } ' , '2 3' )
348+ self .assertEqual (f'\x20 { 3 } ' , ' 3' )
349+
350+ self .assertEqual (f'2\x20 ' , '2 ' )
351+ self .assertEqual (f'2\x20 3' , '2 3' )
352+ self .assertEqual (f'\x20 3' , ' 3' )
353+
354+ def test_misformed_unicode_character_name (self ):
355+ # These test are needed because unicode names are parsed
356+ # differently inside f-strings.
357+ self .assertAllRaise (SyntaxError , r"\(unicode error\) 'unicodeescape' codec can't decode bytes in position .*: malformed \\N character escape" ,
358+ [r"f'\N'" ,
359+ r"f'\N{'" ,
360+ r"f'\N{GREEK CAPITAL LETTER DELTA'" ,
361+
362+ # Here are the non-f-string versions,
363+ # which should give the same errors.
364+ r"'\N'" ,
365+ r"'\N{'" ,
366+ r"'\N{GREEK CAPITAL LETTER DELTA'" ,
338367 ])
339368
340- # And these don't work now, and shouldn't work in the future.
341- self .assertAllRaise (SyntaxError , 'backslashes not allowed ' ,
369+ def test_no_backslashes_in_expression_part ( self ):
370+ self .assertAllRaise (SyntaxError , 'f-string expression part cannot include a backslash ' ,
342371 [r"f'{\'a\'}'" ,
343372 r"f'{\t3}'" ,
373+ r"f'{\}'" ,
374+ r"rf'{\'a\'}'" ,
375+ r"rf'{\t3}'" ,
376+ r"rf'{\}'" ,
377+ r"""rf'{"\N{LEFT CURLY BRACKET}"}'""" ,
344378 ])
345379
346- # add this when backslashes are allowed again. see issue 27921
347- # these test will be needed because unicode names will be parsed
348- # differently once backslashes are allowed inside expressions
349- ## def test_misformed_unicode_character_name(self):
350- ## self.assertAllRaise(SyntaxError, 'xx',
351- ## [r"f'\N'",
352- ## [r"f'\N{'",
353- ## [r"f'\N{GREEK CAPITAL LETTER DELTA'",
354- ## ])
380+ def test_no_escapes_for_braces (self ):
381+ # \x7b is '{'. Make sure it doesn't start an expression.
382+ self .assertEqual (f'\x7b 2}}' , '{2}' )
383+ self .assertEqual (f'\x7b 2' , '{2' )
384+ self .assertEqual (f'\u007b 2' , '{2' )
385+ self .assertEqual (f'\N{LEFT CURLY BRACKET} 2\N{RIGHT CURLY BRACKET} ' , '{2}' )
355386
356387 def test_newlines_in_expressions (self ):
357388 self .assertEqual (f'{ 0 } ' , '0' )
@@ -509,6 +540,14 @@ def test_invalid_string_prefixes(self):
509540 "ruf''" ,
510541 "FUR''" ,
511542 "Fur''" ,
543+ "fb''" ,
544+ "fB''" ,
545+ "Fb''" ,
546+ "FB''" ,
547+ "bf''" ,
548+ "bF''" ,
549+ "Bf''" ,
550+ "BF''" ,
512551 ])
513552
514553 def test_leading_trailing_spaces (self ):
@@ -551,8 +590,8 @@ def test_conversions(self):
551590 self .assertAllRaise (SyntaxError , 'f-string: invalid conversion character' ,
552591 ["f'{3!g}'" ,
553592 "f'{3!A}'" ,
554- "f'{3!A }'" ,
555- "f'{3!A }'" ,
593+ "f'{3!3 }'" ,
594+ "f'{3!G }'" ,
556595 "f'{3!!}'" ,
557596 "f'{3!:}'" ,
558597 "f'{3! s}'" , # no space before conversion char
@@ -601,6 +640,7 @@ def test_mismatched_braces(self):
601640 "f'{3!s:3'" ,
602641 "f'x{'" ,
603642 "f'x{x'" ,
643+ "f'{x'" ,
604644 "f'{3:s'" ,
605645 "f'{{{'" ,
606646 "f'{{}}{'" ,
0 commit comments