Skip to content

Commit 570b1c9

Browse files
[3.6] bpo-30529: Fix errors for invalid whitespaces in f-string subexpressions. (GH-1888) (#2013)
'invalid character in identifier' now is raised instead of 'f-string: empty expression not allowed' if a subexpression contains only whitespaces and they are not accepted by Python parser. (cherry picked from commit 2e9cd58)
1 parent b319d09 commit 570b1c9

File tree

2 files changed

+17
-24
lines changed

2 files changed

+17
-24
lines changed

Lib/test/test_fstring.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,10 @@ def test_missing_expression(self):
280280
"f'{10:{ }}'",
281281
"f' { } '",
282282

283+
# The Python parser ignores also the following
284+
# whitespace characters in additional to a space.
285+
"f'''{\t\f\r\n}'''",
286+
283287
# Catch the empty expression before the
284288
# invalid conversion.
285289
"f'{!x}'",
@@ -300,6 +304,12 @@ def test_missing_expression(self):
300304
"f'{:x'",
301305
])
302306

307+
# Different error message is raised for other whitespace characters.
308+
self.assertAllRaise(SyntaxError, 'invalid character in identifier',
309+
["f'''{\xa0}'''",
310+
"\xa0",
311+
])
312+
303313
def test_parens_in_expressions(self):
304314
self.assertEqual(f'{3,}', '(3,)')
305315

Python/ast.c

Lines changed: 7 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4247,49 +4247,32 @@ fstring_compile_expr(const char *expr_start, const char *expr_end,
42474247
struct compiling *c, const node *n)
42484248

42494249
{
4250-
int all_whitespace = 1;
4251-
int kind;
4252-
void *data;
42534250
PyCompilerFlags cf;
42544251
mod_ty mod;
42554252
char *str;
4256-
PyObject *o;
42574253
Py_ssize_t len;
4258-
Py_ssize_t i;
4254+
const char *s;
42594255

42604256
assert(expr_end >= expr_start);
42614257
assert(*(expr_start-1) == '{');
42624258
assert(*expr_end == '}' || *expr_end == '!' || *expr_end == ':');
42634259

4264-
/* We know there are no escapes here, because backslashes are not allowed,
4265-
and we know it's utf-8 encoded (per PEP 263). But, in order to check
4266-
that each char is not whitespace, we need to decode it to unicode.
4267-
Which is unfortunate, but such is life. */
4268-
42694260
/* If the substring is all whitespace, it's an error. We need to catch
42704261
this here, and not when we call PyParser_ASTFromString, because turning
42714262
the expression '' in to '()' would go from being invalid to valid. */
4272-
/* Note that this code says an empty string is all whitespace. That's
4273-
important. There's a test for it: f'{}'. */
4274-
o = PyUnicode_DecodeUTF8(expr_start, expr_end-expr_start, NULL);
4275-
if (o == NULL)
4276-
return NULL;
4277-
len = PyUnicode_GET_LENGTH(o);
4278-
kind = PyUnicode_KIND(o);
4279-
data = PyUnicode_DATA(o);
4280-
for (i = 0; i < len; i++) {
4281-
if (!Py_UNICODE_ISSPACE(PyUnicode_READ(kind, data, i))) {
4282-
all_whitespace = 0;
4263+
for (s = expr_start; s != expr_end; s++) {
4264+
char c = *s;
4265+
/* The Python parser ignores only the following whitespace
4266+
characters (\r already is converted to \n). */
4267+
if (!(c == ' ' || c == '\t' || c == '\n' || c == '\f')) {
42834268
break;
42844269
}
42854270
}
4286-
Py_DECREF(o);
4287-
if (all_whitespace) {
4271+
if (s == expr_end) {
42884272
ast_error(c, n, "f-string: empty expression not allowed");
42894273
return NULL;
42904274
}
42914275

4292-
/* Reuse len to be the length of the utf-8 input string. */
42934276
len = expr_end - expr_start;
42944277
/* Allocate 3 extra bytes: open paren, close paren, null byte. */
42954278
str = PyMem_RawMalloc(len + 3);

0 commit comments

Comments
 (0)