Skip to content

Commit

Permalink
pythongh-124502: Add PyUnicode_Equal() function
Browse files Browse the repository at this point in the history
  • Loading branch information
vstinner committed Sep 25, 2024
1 parent 54dd77f commit 8162129
Show file tree
Hide file tree
Showing 11 changed files with 94 additions and 0 deletions.
18 changes: 18 additions & 0 deletions Doc/c-api/unicode.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1423,6 +1423,24 @@ 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.
See also :c:func:`PyUnicode_Equal`.
.. 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
Python :class:`str` object.
The function always succeed if *a* and *b* are Python :class:`str` objects.
See also :c:func:`PyUnicode_Compare`.
.. 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.

3 changes: 3 additions & 0 deletions Doc/whatsnew/3.14.rst
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,9 @@ New Features
<https://peps.python.org/pep-0630/#type-checking>`__ mentioned in :pep:`630`
(:gh:`124153`).

* Add :c:func:`PyUnicode_Equal` function to 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
27 changes: 27 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,33 @@ 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"))

# 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 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'
18 changes: 18 additions & 0 deletions Modules/_testlimitedcapi/unicode.c
Original file line number Diff line number Diff line change
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
17 changes: 17 additions & 0 deletions Objects/unicodeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -11009,6 +11009,23 @@ _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 8162129

Please sign in to comment.