@@ -693,15 +693,13 @@ def test_format_specifier_expressions(self):
693
693
self .assertEqual (f'{ 10 :#{3 != {4 :5 } and width }x} ' , ' 0xa' )
694
694
self .assertEqual (f'result: { value :{width :{0 }}.{precision :1}} ' , 'result: 12.35' )
695
695
696
- self .assertAllRaise (SyntaxError ,
697
- """f-string: invalid conversion character 'r{"': """
698
- """expected 's', 'r', or 'a'""" ,
696
+ self .assertAllRaise (SyntaxError , "f-string: expecting ':' or '}'" ,
699
697
["""f'{"s"!r{":10"}}'""" ,
700
-
701
698
# This looks like a nested format spec.
702
699
])
703
700
704
- self .assertAllRaise (SyntaxError , "f-string: invalid syntax" ,
701
+ self .assertAllRaise (SyntaxError ,
702
+ "f-string: expecting a valid expression after '{'" ,
705
703
[# Invalid syntax inside a nested spec.
706
704
"f'{4:{/5}}'" ,
707
705
])
@@ -724,7 +722,8 @@ def __format__(self, spec):
724
722
self .assertEqual (f'{ x } { x } ' , '1 2' )
725
723
726
724
def test_missing_expression (self ):
727
- self .assertAllRaise (SyntaxError , 'f-string: empty expression not allowed' ,
725
+ self .assertAllRaise (SyntaxError ,
726
+ "f-string: valid expression required before '}'" ,
728
727
["f'{}'" ,
729
728
"f'{ }'"
730
729
"f' {} '" ,
@@ -736,8 +735,8 @@ def test_missing_expression(self):
736
735
"f'''{\t \f \r \n }'''" ,
737
736
])
738
737
739
- # Different error messages are raised when a specifier ('!', ':' or '=') is used after an empty expression
740
- self . assertAllRaise ( SyntaxError , "f-string: expression required before '!'" ,
738
+ self . assertAllRaise ( SyntaxError ,
739
+ "f-string: valid expression required before '!'" ,
741
740
["f'{!r}'" ,
742
741
"f'{ !r}'" ,
743
742
"f'{!}'" ,
@@ -758,15 +757,17 @@ def test_missing_expression(self):
758
757
"f'{ !xr:a}'" ,
759
758
])
760
759
761
- self .assertAllRaise (SyntaxError , "f-string: expression required before ':'" ,
760
+ self .assertAllRaise (SyntaxError ,
761
+ "f-string: valid expression required before ':'" ,
762
762
["f'{:}'" ,
763
763
"f'{ :!}'" ,
764
764
"f'{:2}'" ,
765
765
"f'''{\t \f \r \n :a}'''" ,
766
766
"f'{:'" ,
767
767
])
768
768
769
- self .assertAllRaise (SyntaxError , "f-string: expression required before '='" ,
769
+ self .assertAllRaise (SyntaxError ,
770
+ "f-string: valid expression required before '='" ,
770
771
["f'{=}'" ,
771
772
"f'{ =}'" ,
772
773
"f'{ =:}'" ,
@@ -784,21 +785,18 @@ def test_missing_expression(self):
784
785
def test_parens_in_expressions (self ):
785
786
self .assertEqual (f'{ 3 ,} ' , '(3,)' )
786
787
787
- # Add these because when an expression is evaluated, parens
788
- # are added around it. But we shouldn't go from an invalid
789
- # expression to a valid one. The added parens are just
790
- # supposed to allow whitespace (including newlines).
791
- self .assertAllRaise (SyntaxError , 'invalid syntax' ,
788
+ self .assertAllRaise (SyntaxError ,
789
+ "f-string: expecting a valid expression after '{'" ,
792
790
["f'{,}'" ,
793
- "f'{,}'" , # this is (,), which is an error
794
791
])
795
792
796
793
self .assertAllRaise (SyntaxError , r"f-string: unmatched '\)'" ,
797
794
["f'{3)+(4}'" ,
798
795
])
799
796
800
797
def test_newlines_before_syntax_error (self ):
801
- self .assertAllRaise (SyntaxError , "invalid syntax" ,
798
+ self .assertAllRaise (SyntaxError ,
799
+ "f-string: expecting a valid expression after '{'" ,
802
800
["f'{.}'" , "\n f'{.}'" , "\n \n f'{.}'" ])
803
801
804
802
def test_backslashes_in_string_part (self ):
@@ -885,7 +883,8 @@ def test_backslashes_in_expression_part(self):
885
883
self .assertEqual (f'{ "\N{LEFT CURLY BRACKET} " } ' , '{' )
886
884
self .assertEqual (rf'{ "\N{LEFT CURLY BRACKET} " } ' , '{' )
887
885
888
- self .assertAllRaise (SyntaxError , 'empty expression not allowed' ,
886
+ self .assertAllRaise (SyntaxError ,
887
+ "f-string: valid expression required before '}'" ,
889
888
["f'{\n }'" ,
890
889
])
891
890
@@ -930,9 +929,23 @@ def test_lambda(self):
930
929
self .assertEqual (f'{ (lambda y :x * y )("8" ):10} ' , "88888 " )
931
930
932
931
# lambda doesn't work without parens, because the colon
933
- # makes the parser think it's a format_spec
934
- self .assertAllRaise (SyntaxError , 'invalid syntax' ,
932
+ # makes the parser think it's a format_spec
933
+ # emit warning if we can match a format_spec
934
+ self .assertAllRaise (SyntaxError ,
935
+ "f-string: lambda expressions are not allowed "
936
+ "without parentheses" ,
935
937
["f'{lambda x:x}'" ,
938
+ "f'{lambda :x}'" ,
939
+ "f'{lambda *arg, :x}'" ,
940
+ "f'{1, lambda:x}'" ,
941
+ ])
942
+
943
+ # but don't emit the paren warning in general cases
944
+ self .assertAllRaise (SyntaxError ,
945
+ "f-string: expecting a valid expression after '{'" ,
946
+ ["f'{lambda x:}'" ,
947
+ "f'{lambda :}'" ,
948
+ "f'{+ lambda:None}'" ,
936
949
])
937
950
938
951
def test_valid_prefixes (self ):
@@ -1185,7 +1198,7 @@ def test_conversions(self):
1185
1198
"f'{3!g'" ,
1186
1199
])
1187
1200
1188
- self .assertAllRaise (SyntaxError , 'f-string: missed conversion character' ,
1201
+ self .assertAllRaise (SyntaxError , 'f-string: missing conversion character' ,
1189
1202
["f'{3!}'" ,
1190
1203
"f'{3!:'" ,
1191
1204
"f'{3!:}'" ,
@@ -1244,8 +1257,7 @@ def test_mismatched_braces(self):
1244
1257
])
1245
1258
1246
1259
self .assertAllRaise (SyntaxError , "f-string: expecting '}'" ,
1247
- ["f'{3:{{>10}'" ,
1248
- "f'{3'" ,
1260
+ ["f'{3'" ,
1249
1261
"f'{3!'" ,
1250
1262
"f'{3:'" ,
1251
1263
"f'{3!s'" ,
@@ -1261,6 +1273,11 @@ def test_mismatched_braces(self):
1261
1273
"f'{i='" , # See gh-93418.
1262
1274
])
1263
1275
1276
+ self .assertAllRaise (SyntaxError ,
1277
+ "f-string: expecting a valid expression after '{'" ,
1278
+ ["f'{3:{{>10}'" ,
1279
+ ])
1280
+
1264
1281
# But these are just normal strings.
1265
1282
self .assertEqual (f'{ "{" } ' , '{' )
1266
1283
self .assertEqual (f'{ "}" } ' , '}' )
@@ -1481,7 +1498,8 @@ def test_walrus(self):
1481
1498
self .assertEqual (x , 10 )
1482
1499
1483
1500
def test_invalid_syntax_error_message (self ):
1484
- with self .assertRaisesRegex (SyntaxError , "invalid syntax" ):
1501
+ with self .assertRaisesRegex (SyntaxError ,
1502
+ "f-string: expecting '=', or '!', or ':', or '}'" ):
1485
1503
compile ("f'{a $ b}'" , "?" , "exec" )
1486
1504
1487
1505
def test_with_two_commas_in_format_specifier (self ):
@@ -1505,12 +1523,11 @@ def test_with_an_underscore_and_a_comma_in_format_specifier(self):
1505
1523
f'{ 1 :_,} '
1506
1524
1507
1525
def test_syntax_error_for_starred_expressions (self ):
1508
- error_msg = re .escape ("can't use starred expression here" )
1509
- with self .assertRaisesRegex (SyntaxError , error_msg ):
1526
+ with self .assertRaisesRegex (SyntaxError , "can't use starred expression here" ):
1510
1527
compile ("f'{*a}'" , "?" , "exec" )
1511
1528
1512
- error_msg = re . escape ( "invalid syntax" )
1513
- with self . assertRaisesRegex ( SyntaxError , error_msg ):
1529
+ with self . assertRaisesRegex ( SyntaxError ,
1530
+ "f-string: expecting a valid expression after '{'" ):
1514
1531
compile ("f'{**a}'" , "?" , "exec" )
1515
1532
1516
1533
if __name__ == '__main__' :
0 commit comments