https://github.com/python/cpython/commit/a7f0727ca575fef4d8891b5ebfe71ef2a774868b
commit: a7f0727ca575fef4d8891b5ebfe71ef2a774868b
branch: main
author: Victor Stinner <[email protected]>
committer: vstinner <[email protected]>
date: 2024-10-07T21:24:53Z
summary:
gh-124502: Add PyUnicode_Equal() function (#124504)
files:
A Misc/NEWS.d/next/C_API/2024-09-25-11-44-02.gh-issue-124502.qWuDjT.rst
M Doc/c-api/unicode.rst
M Doc/data/stable_abi.dat
M Doc/whatsnew/3.14.rst
M Include/unicodeobject.h
M Lib/test/test_capi/test_unicode.py
M Lib/test/test_stable_abi_ctypes.py
M Misc/stable_abi.toml
M Modules/_testlimitedcapi/unicode.c
M Objects/unicodeobject.c
M PC/python3dll.c
diff --git a/Doc/c-api/unicode.rst b/Doc/c-api/unicode.rst
index b2ac0c903c2bd7..f5704cffa199a5 100644
--- a/Doc/c-api/unicode.rst
+++ b/Doc/c-api/unicode.rst
@@ -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)
diff --git a/Doc/data/stable_abi.dat b/Doc/data/stable_abi.dat
index 19dc71a345b474..9314facd2ad873 100644
--- a/Doc/data/stable_abi.dat
+++ b/Doc/data/stable_abi.dat
@@ -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,,
diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst
index 67d8d389b58082..f1f78ed843f313 100644
--- a/Doc/whatsnew/3.14.rst
+++ b/Doc/whatsnew/3.14.rst
@@ -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
----------------------
diff --git a/Include/unicodeobject.h b/Include/unicodeobject.h
index dee00715b3c51d..2ce3a008b7129e 100644
--- a/Include/unicodeobject.h
+++ b/Include/unicodeobject.h
@@ -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
diff --git a/Lib/test/test_capi/test_unicode.py
b/Lib/test/test_capi/test_unicode.py
index e6f85427214958..65d8242ad3fc60 100644
--- a/Lib/test/test_capi/test_unicode.py
+++ b/Lib/test/test_capi/test_unicode.py
@@ -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()
diff --git a/Lib/test/test_stable_abi_ctypes.py
b/Lib/test/test_stable_abi_ctypes.py
index d16ad7ef5d4328..b14d500a9c6e97 100644
--- a/Lib/test/test_stable_abi_ctypes.py
+++ b/Lib/test/test_stable_abi_ctypes.py
@@ -805,6 +805,7 @@ def test_windows_feature_macros(self):
"PyUnicode_DecodeUnicodeEscape",
"PyUnicode_EncodeFSDefault",
"PyUnicode_EncodeLocale",
+ "PyUnicode_Equal",
"PyUnicode_EqualToUTF8",
"PyUnicode_EqualToUTF8AndSize",
"PyUnicode_FSConverter",
diff --git
a/Misc/NEWS.d/next/C_API/2024-09-25-11-44-02.gh-issue-124502.qWuDjT.rst
b/Misc/NEWS.d/next/C_API/2024-09-25-11-44-02.gh-issue-124502.qWuDjT.rst
new file mode 100644
index 00000000000000..f515619328b359
--- /dev/null
+++ b/Misc/NEWS.d/next/C_API/2024-09-25-11-44-02.gh-issue-124502.qWuDjT.rst
@@ -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.
diff --git a/Misc/stable_abi.toml b/Misc/stable_abi.toml
index fe0a5e44f8fb15..62978261745d79 100644
--- a/Misc/stable_abi.toml
+++ b/Misc/stable_abi.toml
@@ -2536,3 +2536,5 @@
added = '3.14'
[const.Py_TP_USE_SPEC]
added = '3.14'
+[function.PyUnicode_Equal]
+ added = '3.14'
diff --git a/Modules/_testlimitedcapi/unicode.c
b/Modules/_testlimitedcapi/unicode.c
index 2b70d09108a333..c7a23d5d1cbd71 100644
--- a/Modules/_testlimitedcapi/unicode.c
+++ b/Modules/_testlimitedcapi/unicode.c
@@ -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},
};
diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c
index e9589cfe44f3bf..60d4875d3b393e 100644
--- a/Objects/unicodeobject.c
+++ b/Objects/unicodeobject.c
@@ -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)
{
diff --git a/PC/python3dll.c b/PC/python3dll.c
index 6b8208ab90bd95..9296474617e115 100755
--- a/PC/python3dll.c
+++ b/PC/python3dll.c
@@ -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)
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]