gh-124502: Add PyUnicode_Equal() function (#124504)

This commit is contained in:
Victor Stinner 2024-10-07 23:24:53 +02:00 committed by GitHub
parent c5df1cb7bd
commit a7f0727ca5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 111 additions and 2 deletions

View File

@ -1438,6 +1438,31 @@ They all return ``NULL`` or ``-1`` if an exception occurs.
This function returns ``-1`` upon failure, so one should call
:c:func:`PyErr_Occurred` to check for errors.
.. seealso::
The :c:func:`PyUnicode_Equal` function.
.. c:function:: int PyUnicode_Equal(PyObject *a, PyObject *b)
Test if two strings are equal:
* Return ``1`` if *a* is equal to *b*.
* Return ``0`` if *a* is not equal to *b*.
* Set a :exc:`TypeError` exception and return ``-1`` if *a* or *b* is not a
:class:`str` object.
The function always succeeds if *a* and *b* are :class:`str` objects.
The function works for :class:`str` subclasses, but does not honor custom
``__eq__()`` method.
.. seealso::
The :c:func:`PyUnicode_Compare` function.
.. versionadded:: 3.14
.. c:function:: int PyUnicode_EqualToUTF8AndSize(PyObject *unicode, const char *string, Py_ssize_t size)

View File

@ -783,6 +783,7 @@ func,PyUnicode_DecodeUnicodeEscape,3.2,,
func,PyUnicode_EncodeCodePage,3.7,on Windows,
func,PyUnicode_EncodeFSDefault,3.2,,
func,PyUnicode_EncodeLocale,3.7,,
func,PyUnicode_Equal,3.14,,
func,PyUnicode_EqualToUTF8,3.13,,
func,PyUnicode_EqualToUTF8AndSize,3.13,,
func,PyUnicode_FSConverter,3.2,,

View File

@ -687,6 +687,10 @@ New Features
<https://peps.python.org/pep-0630/#type-checking>`__ mentioned in :pep:`630`
(:gh:`124153`).
* Add :c:func:`PyUnicode_Equal` function to the limited C API:
test if two strings are equal.
(Contributed by Victor Stinner in :gh:`124502`.)
Porting to Python 3.14
----------------------

View File

@ -966,6 +966,10 @@ PyAPI_FUNC(int) PyUnicode_EqualToUTF8(PyObject *, const char *);
PyAPI_FUNC(int) PyUnicode_EqualToUTF8AndSize(PyObject *, const char *, Py_ssize_t);
#endif
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030e0000
PyAPI_FUNC(int) PyUnicode_Equal(PyObject *str1, PyObject *str2);
#endif
/* Rich compare two strings and return one of the following:
- NULL in case an exception was raised

View File

@ -1903,6 +1903,39 @@ class PyUnicodeWriterFormatTest(unittest.TestCase):
self.assertEqual(writer.finish(), 'Hello World.')
def test_unicode_equal(self):
unicode_equal = _testlimitedcapi.unicode_equal
def copy(text):
return text.encode().decode()
self.assertTrue(unicode_equal("", ""))
self.assertTrue(unicode_equal("abc", "abc"))
self.assertTrue(unicode_equal("abc", copy("abc")))
self.assertTrue(unicode_equal("\u20ac", copy("\u20ac")))
self.assertTrue(unicode_equal("\U0010ffff", copy("\U0010ffff")))
self.assertFalse(unicode_equal("abc", "abcd"))
self.assertFalse(unicode_equal("\u20ac", "\u20ad"))
self.assertFalse(unicode_equal("\U0010ffff", "\U0010fffe"))
# str subclass
self.assertTrue(unicode_equal("abc", Str("abc")))
self.assertTrue(unicode_equal(Str("abc"), "abc"))
self.assertFalse(unicode_equal("abc", Str("abcd")))
self.assertFalse(unicode_equal(Str("abc"), "abcd"))
# invalid type
for invalid_type in (b'bytes', 123, ("tuple",)):
with self.subTest(invalid_type=invalid_type):
with self.assertRaises(TypeError):
unicode_equal("abc", invalid_type)
with self.assertRaises(TypeError):
unicode_equal(invalid_type, "abc")
# CRASHES unicode_equal("abc", NULL)
# CRASHES unicode_equal(NULL, "abc")
if __name__ == "__main__":
unittest.main()

View File

@ -805,6 +805,7 @@ SYMBOL_NAMES = (
"PyUnicode_DecodeUnicodeEscape",
"PyUnicode_EncodeFSDefault",
"PyUnicode_EncodeLocale",
"PyUnicode_Equal",
"PyUnicode_EqualToUTF8",
"PyUnicode_EqualToUTF8AndSize",
"PyUnicode_FSConverter",

View File

@ -0,0 +1,2 @@
Add :c:func:`PyUnicode_Equal` function to the limited C API: test if two
strings are equal. Patch by Victor Stinner.

View File

@ -2536,3 +2536,5 @@
added = '3.14'
[const.Py_TP_USE_SPEC]
added = '3.14'
[function.PyUnicode_Equal]
added = '3.14'

View File

@ -1,7 +1,7 @@
#include "pyconfig.h" // Py_GIL_DISABLED
#ifndef Py_GIL_DISABLED
// Need limited C API 3.13 to test PyUnicode_EqualToUTF8()
# define Py_LIMITED_API 0x030d0000
// Need limited C API 3.14 to test PyUnicode_Equal()
# define Py_LIMITED_API 0x030e0000
#endif
#include "parts.h"
@ -1837,6 +1837,23 @@ test_string_from_format(PyObject *self, PyObject *Py_UNUSED(ignored))
#undef CHECK_FORMAT_0
}
/* Test PyUnicode_Equal() */
static PyObject *
unicode_equal(PyObject *module, PyObject *args)
{
PyObject *str1, *str2;
if (!PyArg_ParseTuple(args, "OO", &str1, &str2)) {
return NULL;
}
NULLABLE(str1);
NULLABLE(str2);
RETURN_INT(PyUnicode_Equal(str1, str2));
}
static PyMethodDef TestMethods[] = {
{"codec_incrementalencoder", codec_incrementalencoder, METH_VARARGS},
{"codec_incrementaldecoder", codec_incrementaldecoder, METH_VARARGS},
@ -1924,6 +1941,7 @@ static PyMethodDef TestMethods[] = {
{"unicode_format", unicode_format, METH_VARARGS},
{"unicode_contains", unicode_contains, METH_VARARGS},
{"unicode_isidentifier", unicode_isidentifier, METH_O},
{"unicode_equal", unicode_equal, METH_VARARGS},
{NULL},
};

View File

@ -11001,6 +11001,24 @@ _PyUnicode_Equal(PyObject *str1, PyObject *str2)
}
int
PyUnicode_Equal(PyObject *str1, PyObject *str2)
{
if (!PyUnicode_Check(str1)) {
PyErr_Format(PyExc_TypeError,
"first argument must be str, not %T", str1);
return -1;
}
if (!PyUnicode_Check(str2)) {
PyErr_Format(PyExc_TypeError,
"second argument must be str, not %T", str2);
return -1;
}
return _PyUnicode_Equal(str1, str2);
}
int
PyUnicode_Compare(PyObject *left, PyObject *right)
{

1
PC/python3dll.c generated
View File

@ -717,6 +717,7 @@ EXPORT_FUNC(PyUnicode_DecodeUTF8Stateful)
EXPORT_FUNC(PyUnicode_EncodeCodePage)
EXPORT_FUNC(PyUnicode_EncodeFSDefault)
EXPORT_FUNC(PyUnicode_EncodeLocale)
EXPORT_FUNC(PyUnicode_Equal)
EXPORT_FUNC(PyUnicode_EqualToUTF8)
EXPORT_FUNC(PyUnicode_EqualToUTF8AndSize)
EXPORT_FUNC(PyUnicode_Find)