]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-124502: Add PyUnicode_Equal() function (#124504)
authorVictor Stinner <vstinner@python.org>
Mon, 7 Oct 2024 21:24:53 +0000 (23:24 +0200)
committerGitHub <noreply@github.com>
Mon, 7 Oct 2024 21:24:53 +0000 (21:24 +0000)
Doc/c-api/unicode.rst
Doc/data/stable_abi.dat
Doc/whatsnew/3.14.rst
Include/unicodeobject.h
Lib/test/test_capi/test_unicode.py
Lib/test/test_stable_abi_ctypes.py
Misc/NEWS.d/next/C_API/2024-09-25-11-44-02.gh-issue-124502.qWuDjT.rst [new file with mode: 0644]
Misc/stable_abi.toml
Modules/_testlimitedcapi/unicode.c
Objects/unicodeobject.c
PC/python3dll.c

index b2ac0c903c2bd7891ae431293494ef0d66f0057a..f5704cffa199a583389187e9bced0cd2acd53e22 100644 (file)
@@ -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)
 
index 19dc71a345b47415b798007cc794d262bc607fab..9314facd2ad8730fcf4f80e8325a94ac7cff7307 100644 (file)
@@ -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,,
index 67d8d389b5808295613a118faac48a752997a151..f1f78ed843f3133fa4597581b4922f045adb60fb 100644 (file)
@@ -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
 ----------------------
index dee00715b3c51d576da7bfbcd84ae362f5f14a0b..2ce3a008b7129ecf74e6e2080651267659720432 100644 (file)
@@ -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
index e6f85427214958734be274b27a0957abcec012fc..65d8242ad3fc60663827d9bdcccecf4b5c5a78ae 100644 (file)
@@ -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()
index d16ad7ef5d4328333e22d80360f432001766df26..b14d500a9c6e9720ee03a13abbbf5ef815ce645e 100644 (file)
@@ -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 (file)
index 0000000..f515619
--- /dev/null
@@ -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.
index fe0a5e44f8fb155f08091402cc149016540c5e3c..62978261745d79127e99682499149cc55211fc3c 100644 (file)
     added = '3.14'
 [const.Py_TP_USE_SPEC]
     added = '3.14'
+[function.PyUnicode_Equal]
+    added = '3.14'
index 2b70d09108a33354a2d8f448dc422a2137eb3269..c7a23d5d1cbd71aab96d13327422a2c74a7252e6 100644 (file)
@@ -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},
 };
 
index e9589cfe44f3bf3ad5a7a814cd74ff786d28972c..60d4875d3b393e84b181bd3c0326e81faddffec5 100644 (file)
@@ -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)
 {
index 6b8208ab90bd9504931d891e4f28fb074e59f850..9296474617e115f346871b865c636cd6e8b296a1 100755 (executable)
@@ -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)