Skip to content

Commit

Permalink
pythongh-124502: Add PyUnicode_Equal() function (python#124504)
Browse files Browse the repository at this point in the history
  • Loading branch information
vstinner authored and efimov-mikhail committed Oct 9, 2024
1 parent 25e3a80 commit d0fa5ff
Show file tree
Hide file tree
Showing 11 changed files with 111 additions and 2 deletions.
25 changes: 25 additions & 0 deletions Doc/c-api/unicode.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions Doc/data/stable_abi.dat

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Doc/whatsnew/3.14.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
----------------------
Expand Down
4 changes: 4 additions & 0 deletions Include/unicodeobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
33 changes: 33 additions & 0 deletions Lib/test/test_capi/test_unicode.py
Original file line number Diff line number Diff line change
Expand Up @@ -1903,6 +1903,39 @@ def test_recover_error(self):

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()
1 change: 1 addition & 0 deletions Lib/test/test_stable_abi_ctypes.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -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.
2 changes: 2 additions & 0 deletions Misc/stable_abi.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2536,3 +2536,5 @@
added = '3.14'
[const.Py_TP_USE_SPEC]
added = '3.14'
[function.PyUnicode_Equal]
added = '3.14'
22 changes: 20 additions & 2 deletions Modules/_testlimitedcapi/unicode.c
Original file line number Diff line number Diff line change
@@ -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"
Expand Down Expand Up @@ -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},
Expand Down Expand Up @@ -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},
};

Expand Down
18 changes: 18 additions & 0 deletions Objects/unicodeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
1 change: 1 addition & 0 deletions PC/python3dll.c

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit d0fa5ff

Please sign in to comment.