@@ -4007,13 +4007,14 @@ decode_unicode(struct compiling *c, const char *s, size_t len, const char *encod
40074007 expression. This is to allow strings with embedded newlines, for
40084008 example. */
40094009static expr_ty
4010- fstring_expression_compile (PyObject * str , Py_ssize_t expr_start ,
4011- Py_ssize_t expr_end , PyArena * arena )
4010+ fstring_compile_expr (PyObject * str , Py_ssize_t expr_start ,
4011+ Py_ssize_t expr_end , PyArena * arena )
40124012{
40134013 PyCompilerFlags cf ;
40144014 mod_ty mod ;
40154015 char * utf_expr ;
40164016 Py_ssize_t i ;
4017+ Py_UCS4 end_ch = -1 ;
40174018 int all_whitespace ;
40184019 PyObject * sub = NULL ;
40194020
@@ -4023,6 +4024,16 @@ fstring_expression_compile(PyObject *str, Py_ssize_t expr_start,
40234024
40244025 assert (str );
40254026
4027+ assert (expr_start >= 0 && expr_start < PyUnicode_GET_LENGTH (str ));
4028+ assert (expr_end >= 0 && expr_end < PyUnicode_GET_LENGTH (str ));
4029+ assert (expr_end >= expr_start );
4030+
4031+ /* There has to be at least on character on each side of the
4032+ expression inside this str. This will have been caught before
4033+ we're called. */
4034+ assert (expr_start >= 1 );
4035+ assert (expr_end <= PyUnicode_GET_LENGTH (str )- 1 );
4036+
40264037 /* If the substring is all whitespace, it's an error. We need to
40274038 catch this here, and not when we call PyParser_ASTFromString,
40284039 because turning the expression '' in to '()' would go from
@@ -4049,10 +4060,17 @@ fstring_expression_compile(PyObject *str, Py_ssize_t expr_start,
40494060 string directly. */
40504061
40514062 if (expr_start - 1 == 0 && expr_end + 1 == PyUnicode_GET_LENGTH (str )) {
4052- /* No need to actually remember these characters, because we
4053- know they must be braces. */
4063+ /* If str is well formed, then the first and last chars must
4064+ be '{' and '}', respectively. But, if there's a syntax
4065+ error, for example f'{3!', then the last char won't be a
4066+ closing brace. So, remember the last character we read in
4067+ order for us to restore it. */
4068+ end_ch = PyUnicode_ReadChar (str , expr_end - expr_start + 1 );
4069+ assert (end_ch != (Py_UCS4 )- 1 );
4070+
4071+ /* In all cases, however, start_ch must be '{'. */
40544072 assert (PyUnicode_ReadChar (str , 0 ) == '{' );
4055- assert ( PyUnicode_ReadChar ( str , expr_end - expr_start + 1 ) == '}' );
4073+
40564074 sub = str ;
40574075 } else {
40584076 /* Create a substring object. It must be a new object, with
@@ -4064,21 +4082,23 @@ fstring_expression_compile(PyObject *str, Py_ssize_t expr_start,
40644082 decref_sub = 1 ; /* Remember to deallocate it on error. */
40654083 }
40664084
4085+ /* Put () around the expression. */
40674086 if (PyUnicode_WriteChar (sub , 0 , '(' ) < 0 ||
40684087 PyUnicode_WriteChar (sub , expr_end - expr_start + 1 , ')' ) < 0 )
40694088 goto error ;
40704089
4071- cf .cf_flags = PyCF_ONLY_AST ;
4072-
40734090 /* No need to free the memory returned here: it's managed by the
40744091 string. */
40754092 utf_expr = PyUnicode_AsUTF8 (sub );
40764093 if (!utf_expr )
40774094 goto error ;
4095+
4096+ cf .cf_flags = PyCF_ONLY_AST ;
40784097 mod = PyParser_ASTFromString (utf_expr , "<fstring>" ,
40794098 Py_eval_input , & cf , arena );
40804099 if (!mod )
40814100 goto error ;
4101+
40824102 if (sub != str )
40834103 /* Clear instead of decref in case we ever modify this code to change
40844104 the error handling: this is safest because the XDECREF won't try
@@ -4089,9 +4109,10 @@ fstring_expression_compile(PyObject *str, Py_ssize_t expr_start,
40894109 Py_CLEAR (sub );
40904110 else {
40914111 assert (!decref_sub );
4112+ assert (end_ch != (Py_UCS4 )- 1 );
40924113 /* Restore str, which we earlier modified directly. */
40934114 if (PyUnicode_WriteChar (str , 0 , '{' ) < 0 ||
4094- PyUnicode_WriteChar (str , expr_end - expr_start + 1 , '}' ) < 0 )
4115+ PyUnicode_WriteChar (str , expr_end - expr_start + 1 , end_ch ) < 0 )
40954116 goto error ;
40964117 }
40974118 return mod -> v .Expression .body ;
@@ -4100,6 +4121,18 @@ fstring_expression_compile(PyObject *str, Py_ssize_t expr_start,
41004121 /* Only decref sub if it was the result of a call to SubString. */
41014122 if (decref_sub )
41024123 Py_XDECREF (sub );
4124+
4125+ if (end_ch != (Py_UCS4 )- 1 ) {
4126+ /* We only get here if we modified str. Make sure that's the
4127+ case: str will be equal to sub. */
4128+ if (str == sub ) {
4129+ /* Don't check the error, because we've already set the
4130+ error state (that's why we're in 'error', after
4131+ all). */
4132+ PyUnicode_WriteChar (str , 0 , '{' );
4133+ PyUnicode_WriteChar (str , expr_end - expr_start + 1 , end_ch );
4134+ }
4135+ }
41034136 return NULL ;
41044137}
41054138
@@ -4331,9 +4364,18 @@ fstring_find_expr(PyObject *str, Py_ssize_t *ofs, int recurse_lvl,
43314364 return -1 ;
43324365 }
43334366
4334- /* Check for a conversion char, if present. */
43354367 if (* ofs >= PyUnicode_GET_LENGTH (str ))
43364368 goto unexpected_end_of_string ;
4369+
4370+ /* Compile the expression as soon as possible, so we show errors
4371+ related to the expression before errors related to the
4372+ conversion or format_spec. */
4373+ simple_expression = fstring_compile_expr (str , expr_start , expr_end ,
4374+ c -> c_arena );
4375+ if (!simple_expression )
4376+ return -1 ;
4377+
4378+ /* Check for a conversion char, if present. */
43374379 if (PyUnicode_READ (kind , data , * ofs ) == '!' ) {
43384380 * ofs += 1 ;
43394381 if (* ofs >= PyUnicode_GET_LENGTH (str ))
@@ -4374,12 +4416,6 @@ fstring_find_expr(PyObject *str, Py_ssize_t *ofs, int recurse_lvl,
43744416 assert (PyUnicode_READ (kind , data , * ofs ) == '}' );
43754417 * ofs += 1 ;
43764418
4377- /* Compile the expression. */
4378- simple_expression = fstring_expression_compile (str , expr_start , expr_end ,
4379- c -> c_arena );
4380- if (!simple_expression )
4381- return -1 ;
4382-
43834419 /* And now create the FormattedValue node that represents this entire
43844420 expression with the conversion and format spec. */
43854421 * expression = FormattedValue (simple_expression , (int )conversion ,
0 commit comments