Skip to content

Commit 8162129

Browse files
committed
pythongh-124502: Add PyUnicode_Equal() function
1 parent 54dd77f commit 8162129

File tree

11 files changed

+94
-0
lines changed

11 files changed

+94
-0
lines changed

Doc/c-api/unicode.rst

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1423,6 +1423,24 @@ They all return ``NULL`` or ``-1`` if an exception occurs.
14231423
This function returns ``-1`` upon failure, so one should call
14241424
:c:func:`PyErr_Occurred` to check for errors.
14251425
1426+
See also :c:func:`PyUnicode_Equal`.
1427+
1428+
1429+
.. c:function:: int PyUnicode_Equal(PyObject *a, PyObject *b)
1430+
1431+
Test if two strings are equal:
1432+
1433+
* Return ``1`` if *a* is equal to *b*.
1434+
* Return ``0`` if *a* is not equal to *b*.
1435+
* Set a :exc:`TypeError` exception and return ``-1`` if *a* or *b* is not a
1436+
Python :class:`str` object.
1437+
1438+
The function always succeed if *a* and *b* are Python :class:`str` objects.
1439+
1440+
See also :c:func:`PyUnicode_Compare`.
1441+
1442+
.. versionadded:: 3.14
1443+
14261444
14271445
.. c:function:: int PyUnicode_EqualToUTF8AndSize(PyObject *unicode, const char *string, Py_ssize_t size)
14281446

Doc/data/stable_abi.dat

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Doc/whatsnew/3.14.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,9 @@ New Features
646646
<https://peps.python.org/pep-0630/#type-checking>`__ mentioned in :pep:`630`
647647
(:gh:`124153`).
648648

649+
* Add :c:func:`PyUnicode_Equal` function to test if two strings are equal.
650+
(Contributed by Victor Stinner in :gh:`124502`.)
651+
649652

650653
Porting to Python 3.14
651654
----------------------

Include/unicodeobject.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -966,6 +966,10 @@ PyAPI_FUNC(int) PyUnicode_EqualToUTF8(PyObject *, const char *);
966966
PyAPI_FUNC(int) PyUnicode_EqualToUTF8AndSize(PyObject *, const char *, Py_ssize_t);
967967
#endif
968968

969+
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030e0000
970+
PyAPI_FUNC(int) PyUnicode_Equal(PyObject *str1, PyObject *str2);
971+
#endif
972+
969973
/* Rich compare two strings and return one of the following:
970974
971975
- NULL in case an exception was raised

Lib/test/test_capi/test_unicode.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1903,6 +1903,33 @@ def test_recover_error(self):
19031903

19041904
self.assertEqual(writer.finish(), 'Hello World.')
19051905

1906+
def test_unicode_equal(self):
1907+
unicode_equal = _testlimitedcapi.unicode_equal
1908+
1909+
def copy(text):
1910+
return text.encode().decode()
1911+
1912+
self.assertTrue(unicode_equal("", ""))
1913+
self.assertTrue(unicode_equal("abc", "abc"))
1914+
self.assertTrue(unicode_equal("abc", copy("abc")))
1915+
self.assertTrue(unicode_equal("\u20ac", copy("\u20ac")))
1916+
self.assertTrue(unicode_equal("\U0010ffff", copy("\U0010ffff")))
1917+
1918+
self.assertFalse(unicode_equal("abc", "abcd"))
1919+
self.assertFalse(unicode_equal("\u20ac", "\u20ad"))
1920+
self.assertFalse(unicode_equal("\U0010ffff", "\U0010fffe"))
1921+
1922+
# invalid type
1923+
for invalid_type in (b'bytes', 123, ("tuple",)):
1924+
with self.subTest(invalid_type=invalid_type):
1925+
with self.assertRaises(TypeError):
1926+
unicode_equal("abc", invalid_type)
1927+
with self.assertRaises(TypeError):
1928+
unicode_equal(invalid_type, "abc")
1929+
1930+
# CRASHES unicode_equal("abc", NULL)
1931+
# CRASHES unicode_equal(NULL, "abc")
1932+
19061933

19071934
if __name__ == "__main__":
19081935
unittest.main()

Lib/test/test_stable_abi_ctypes.py

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add :c:func:`PyUnicode_Equal` function to test if two strings are equal.
2+
Patch by Victor Stinner.

Misc/stable_abi.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2536,3 +2536,5 @@
25362536
added = '3.14'
25372537
[const.Py_TP_USE_SPEC]
25382538
added = '3.14'
2539+
[function.PyUnicode_Equal]
2540+
added = '3.14'

Modules/_testlimitedcapi/unicode.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1837,6 +1837,23 @@ test_string_from_format(PyObject *self, PyObject *Py_UNUSED(ignored))
18371837
#undef CHECK_FORMAT_0
18381838
}
18391839

1840+
1841+
/* Test PyUnicode_Equal() */
1842+
static PyObject *
1843+
unicode_equal(PyObject *module, PyObject *args)
1844+
{
1845+
PyObject *str1, *str2;
1846+
if (!PyArg_ParseTuple(args, "OO", &str1, &str2)) {
1847+
return NULL;
1848+
}
1849+
1850+
NULLABLE(str1);
1851+
NULLABLE(str2);
1852+
RETURN_INT(PyUnicode_Equal(str1, str2));
1853+
}
1854+
1855+
1856+
18401857
static PyMethodDef TestMethods[] = {
18411858
{"codec_incrementalencoder", codec_incrementalencoder, METH_VARARGS},
18421859
{"codec_incrementaldecoder", codec_incrementaldecoder, METH_VARARGS},
@@ -1924,6 +1941,7 @@ static PyMethodDef TestMethods[] = {
19241941
{"unicode_format", unicode_format, METH_VARARGS},
19251942
{"unicode_contains", unicode_contains, METH_VARARGS},
19261943
{"unicode_isidentifier", unicode_isidentifier, METH_O},
1944+
{"unicode_equal", unicode_equal, METH_VARARGS},
19271945
{NULL},
19281946
};
19291947

Objects/unicodeobject.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11009,6 +11009,23 @@ _PyUnicode_Equal(PyObject *str1, PyObject *str2)
1100911009
}
1101011010

1101111011

11012+
int
11013+
PyUnicode_Equal(PyObject *str1, PyObject *str2)
11014+
{
11015+
if (!PyUnicode_Check(str1)) {
11016+
PyErr_Format(PyExc_TypeError,
11017+
"first argument must be str, not %T", str1);
11018+
return -1;
11019+
}
11020+
if (!PyUnicode_Check(str2)) {
11021+
PyErr_Format(PyExc_TypeError,
11022+
"second argument must be str, not %T", str2);
11023+
return -1;
11024+
}
11025+
return _PyUnicode_Equal(str1, str2);
11026+
}
11027+
11028+
1101211029
int
1101311030
PyUnicode_Compare(PyObject *left, PyObject *right)
1101411031
{

PC/python3dll.c

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)