summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/c-api/unicode.rst25
-rw-r--r--Doc/data/stable_abi.dat1
-rw-r--r--Doc/whatsnew/3.14.rst4
-rw-r--r--Include/unicodeobject.h4
-rw-r--r--Lib/test/test_capi/test_unicode.py33
-rw-r--r--Lib/test/test_stable_abi_ctypes.py1
-rw-r--r--Misc/NEWS.d/next/C_API/2024-09-25-11-44-02.gh-issue-124502.qWuDjT.rst2
-rw-r--r--Misc/stable_abi.toml2
-rw-r--r--Modules/_testlimitedcapi/unicode.c22
-rw-r--r--Objects/unicodeobject.c18
-rwxr-xr-xPC/python3dll.c1
11 files changed, 111 insertions, 2 deletions
diff --git a/Doc/c-api/unicode.rst b/Doc/c-api/unicode.rst
index b2ac0c9..f5704cf 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 19dc71a..9314fac 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 67d8d38..f1f78ed 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 dee0071..2ce3a00 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 e6f8542..65d8242 100644
--- a/Lib/test/test_capi/test_unicode.py
+++ b/Lib/test/test_capi/test_unicode.py
@@ -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()
diff --git a/Lib/test/test_stable_abi_ctypes.py b/Lib/test/test_stable_abi_ctypes.py
index d16ad7e..b14d500 100644
--- a/Lib/test/test_stable_abi_ctypes.py
+++ b/Lib/test/test_stable_abi_ctypes.py
@@ -805,6 +805,7 @@ SYMBOL_NAMES = (
"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 0000000..f515619
--- /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 fe0a5e4..6297826 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 2b70d09..c7a23d5 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 e9589cf..60d4875 100644
--- a/Objects/unicodeobject.c
+++ b/Objects/unicodeobject.c
@@ -11002,6 +11002,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)
{
if (PyUnicode_Check(left) && PyUnicode_Check(right)) {
diff --git a/PC/python3dll.c b/PC/python3dll.c
index 6b8208a..9296474 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)