diff --git a/Doc/c-api/unicode.rst b/Doc/c-api/unicode.rst index 77f123cf1f2c08..93b52170f1d894 100644 --- a/Doc/c-api/unicode.rst +++ b/Doc/c-api/unicode.rst @@ -239,6 +239,17 @@ access internal read-only data of Unicode objects: Part of the old-style Unicode API, please migrate to using the :c:func:`PyUnicode_nBYTE_DATA` family of macros. +.. c:function:: int PyUnicode_IsIdentifier(PyObject *o) + + Return ``1`` if the string is a valid identifier according to the language + definition, section :ref:`identifiers`. Return ``0`` otherwise. + + Raise an exception and return ``-1`` on error. + + .. versionchanged:: 3.9 + The function now returns ``-1`` on error, instead of calling + :c:func:`Py_FatalError`. + Unicode Character Properties """""""""""""""""""""""""""" diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index c8f407751ec5e2..a5733ce7b81904 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -339,6 +339,10 @@ Build and C API Changes functions are now required to build Python. (Contributed by Victor Stinner in :issue:`39395`.) +* The :c:func:`PyUnicode_IsIdentifier` function now returns ``-1`` on error, + instead of calling :c:func:`Py_FatalError`. + (Contributed by Victor Stinner in :issue:`39500`.) + Deprecated ========== diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 8422a3c5a38c46..cbc7c81e83ace4 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -2241,7 +2241,11 @@ valid_identifier(PyObject *s) Py_TYPE(s)->tp_name); return 0; } - if (!PyUnicode_IsIdentifier(s)) { + int identifier = PyUnicode_IsIdentifier(s); + if (identifier < 0) { + return 0; + } + if (!identifier) { PyErr_SetString(PyExc_TypeError, "__slots__ must be identifiers"); return 0; diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 1ec2accdb09f20..0f262ccd954830 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -12209,13 +12209,13 @@ PyUnicode_IsIdentifier(PyObject *self) Py_UCS4 first; if (PyUnicode_READY(self) == -1) { - Py_FatalError("identifier not ready"); - return 0; + return -1; } - /* Special case for empty strings */ - if (PyUnicode_GET_LENGTH(self) == 0) + if (PyUnicode_GET_LENGTH(self) == 0) { + /* an empty string is not a valid identifier */ return 0; + } kind = PyUnicode_KIND(self); data = PyUnicode_DATA(self); @@ -12228,12 +12228,15 @@ PyUnicode_IsIdentifier(PyObject *self) to check just for these, except that _ must be allowed as starting an identifier. */ first = PyUnicode_READ(kind, data, 0); - if (!_PyUnicode_IsXidStart(first) && first != 0x5F /* LOW LINE */) + if (!_PyUnicode_IsXidStart(first) && first != 0x5F /* LOW LINE */) { return 0; + } - for (i = 1; i < PyUnicode_GET_LENGTH(self); i++) - if (!_PyUnicode_IsXidContinue(PyUnicode_READ(kind, data, i))) + for (i = 1; i < PyUnicode_GET_LENGTH(self); i++) { + if (!_PyUnicode_IsXidContinue(PyUnicode_READ(kind, data, i))) { return 0; + } + } return 1; } @@ -12250,7 +12253,11 @@ static PyObject * unicode_isidentifier_impl(PyObject *self) /*[clinic end generated code: output=fe585a9666572905 input=2d807a104f21c0c5]*/ { - return PyBool_FromLong(PyUnicode_IsIdentifier(self)); + int identifier = PyUnicode_IsIdentifier(self); + if (identifier < 0) { + return NULL; + } + return PyBool_FromLong(identifier); } /*[clinic input] diff --git a/Parser/tokenizer.c b/Parser/tokenizer.c index f73c32684c7b73..a885a69e23c4ca 100644 --- a/Parser/tokenizer.c +++ b/Parser/tokenizer.c @@ -1078,6 +1078,11 @@ verify_identifier(struct tok_state *tok) return 0; } result = PyUnicode_IsIdentifier(s); + assert(result >= 0); + if (result < 0) { + /* silently ignore error */ + PyErr_Clear(); + } Py_DECREF(s); if (result == 0) tok->done = E_IDENTIFIER;