Skip to content

Commit ae5ad35

Browse files
committed
Add PyUnicode_EqualToUTF8() function
1 parent f78c780 commit ae5ad35

File tree

4 files changed

+80
-0
lines changed

4 files changed

+80
-0
lines changed

docs/api.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,12 @@ Python 3.12
129129
130130
Not available on PyPy.
131131
132+
.. c:function:: int PyUnicode_EqualToUTF8(PyObject *unicode, const char *str)
133+
134+
See `PyUnicode_EqualToUTF8() documentation <https://docs.python.org/dev/c-api/unicode.html#c.PyUnicode_EqualToUTF8>`__.
135+
136+
Not available on PyPy.
137+
132138
133139
Python 3.11
134140
-----------

docs/changelog.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
Changelog
22
=========
33

4+
* 2023-10-04: Add ``PyUnicode_EqualToUTF8()`` function.
45
* 2023-10-03: Add functions:
56

67
* ``PyObject_VisitManagedDict()``

pythoncapi_compat.h

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -939,6 +939,62 @@ PyThreadState_GetUnchecked(void)
939939
}
940940
#endif
941941

942+
// gh-110289 added PyUnicode_EqualToUTF8() to Python 3.13.0a1
943+
#if PY_VERSION_HEX < 0x030D00A1
944+
static inline int
945+
PyUnicode_EqualToUTF8(PyObject *unicode, const char *str)
946+
{
947+
Py_ssize_t len;
948+
const void *utf8;
949+
// Python 3.3.0a1 added PyUnicode_AsUTF8AndSize()
950+
#if PY_VERSION_HEX >= 0x030300A1
951+
if (PyUnicode_IS_ASCII(unicode)) {
952+
utf8 = PyUnicode_DATA(unicode);
953+
len = PyUnicode_GET_LENGTH(unicode);
954+
}
955+
else {
956+
utf8 = PyUnicode_AsUTF8AndSize(unicode, &len);
957+
if (utf8 == NULL) {
958+
// Memory allocation failure. The API cannot report error,
959+
// so clear the exception and return 0.
960+
PyErr_Clear();
961+
return 0;
962+
}
963+
}
964+
965+
if ((size_t)len != strlen(str)) {
966+
return 0;
967+
}
968+
return (memcmp(utf8, str, (size_t)len) == 0);
969+
#else
970+
int res;
971+
PyObject *bytes = PyUnicode_AsUTF8String(unicode);
972+
if (bytes == NULL) {
973+
// Memory allocation failure. The API cannot report error,
974+
// so clear the exception and return 0.
975+
PyErr_Clear();
976+
return 0;
977+
}
978+
979+
#if PY_VERSION_HEX >= 0x03000000
980+
len = PyBytes_GET_SIZE(bytes);
981+
utf8 = PyBytes_AS_STRING(bytes);
982+
#else
983+
len = PyString_GET_SIZE(bytes);
984+
utf8 = PyString_AS_STRING(bytes);
985+
#endif
986+
if ((size_t)len != strlen(str)) {
987+
Py_DECREF(bytes);
988+
return 0;
989+
}
990+
991+
res = (memcmp(utf8, str, (size_t)len) == 0);
992+
Py_DECREF(bytes);
993+
return res;
994+
#endif
995+
}
996+
#endif
997+
942998

943999
#ifdef __cplusplus
9441000
}

tests/test_pythoncapi_compat_cext.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1361,6 +1361,22 @@ test_managed_dict(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
13611361
#endif // PY_VERSION_HEX >= 0x030B00A3
13621362

13631363

1364+
static PyObject *
1365+
test_unicode(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
1366+
{
1367+
PyObject *abc = PyUnicode_FromString("abc");
1368+
if (abc == NULL) {
1369+
return NULL;
1370+
}
1371+
1372+
assert(PyUnicode_EqualToUTF8(abc, "abc") == 1);
1373+
assert(PyUnicode_EqualToUTF8(abc, "Python") == 0);
1374+
1375+
Py_DECREF(abc);
1376+
Py_RETURN_NONE;
1377+
}
1378+
1379+
13641380
static struct PyMethodDef methods[] = {
13651381
{"test_object", test_object, METH_NOARGS, _Py_NULL},
13661382
{"test_py_is", test_py_is, METH_NOARGS, _Py_NULL},
@@ -1390,6 +1406,7 @@ static struct PyMethodDef methods[] = {
13901406
#ifdef TEST_MANAGED_DICT
13911407
{"test_managed_dict", test_managed_dict, METH_NOARGS, _Py_NULL},
13921408
#endif
1409+
{"test_unicode", test_unicode, METH_NOARGS, _Py_NULL},
13931410
{_Py_NULL, _Py_NULL, 0, _Py_NULL}
13941411
};
13951412

0 commit comments

Comments
 (0)