]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-116560: Add PyLong_GetSign() public function (#116561)
authorSergey B Kirpichev <skirpichev@gmail.com>
Mon, 3 Jun 2024 12:06:31 +0000 (15:06 +0300)
committerGitHub <noreply@github.com>
Mon, 3 Jun 2024 12:06:31 +0000 (14:06 +0200)
Co-authored-by: Victor Stinner <vstinner@python.org>
Doc/c-api/long.rst
Doc/whatsnew/3.14.rst
Include/cpython/longobject.h
Lib/test/test_capi/test_long.py
Misc/NEWS.d/next/C API/2024-03-10-14-55-51.gh-issue-116560.x2mZaO.rst [new file with mode: 0644]
Modules/_testcapi/long.c
Objects/longobject.c

index 522c028cfb8d40663ecc220af70ffad7b40b26cf..a0e111af5996d70372933586361dfd9d03e16eb2 100644 (file)
@@ -494,6 +494,19 @@ distinguished from a number.  Use :c:func:`PyErr_Occurred` to disambiguate.
    .. versionadded:: 3.13
 
 
+.. c:function:: int PyLong_GetSign(PyObject *obj, int *sign)
+
+   Get the sign of the integer object *obj*.
+
+   On success, set *\*sign* to the integer sign  (0, -1 or +1 for zero, negative or
+   positive integer, respectively) and return 0.
+
+   On failure, return -1 with an exception set.  This function always succeeds
+   if *obj* is a :c:type:`PyLongObject` or its subtype.
+
+   .. versionadded:: 3.14
+
+
 .. c:function:: int PyUnstable_Long_IsCompact(const PyLongObject* op)
 
    Return 1 if *op* is compact, 0 otherwise.
index 47f3e30942397f7ced18af3304e1d9c9b9859ffd..b2dd80b64a691afd74a67742044ed8f9357b8259 100644 (file)
@@ -255,6 +255,9 @@ C API Changes
 New Features
 ------------
 
+* Add :c:func:`PyLong_GetSign` function to get the sign of :class:`int` objects.
+  (Contributed by Sergey B Kirpichev in :gh:`116560`.)
+
 Porting to Python 3.14
 ----------------------
 
index 96815938c8277a59a5673718b5e117f190f69d23..19a6722d07734a366c6185338a400b2c86ff3338 100644 (file)
@@ -55,9 +55,13 @@ PyAPI_FUNC(PyObject*) PyLong_FromUnsignedNativeBytes(const void* buffer,
 PyAPI_FUNC(int) PyUnstable_Long_IsCompact(const PyLongObject* op);
 PyAPI_FUNC(Py_ssize_t) PyUnstable_Long_CompactValue(const PyLongObject* op);
 
-// _PyLong_Sign.  Return 0 if v is 0, -1 if v < 0, +1 if v > 0.
-// v must not be NULL, and must be a normalized long.
-// There are no error cases.
+/* PyLong_GetSign.  Get the sign of an integer object:
+   0, -1 or +1 for zero, negative or positive integer, respectively.
+
+   - On success, set '*sign' to the integer sign, and return 0.
+   - On failure, set an exception, and return -1. */
+PyAPI_FUNC(int) PyLong_GetSign(PyObject *v, int *sign);
+
 PyAPI_FUNC(int) _PyLong_Sign(PyObject *v);
 
 /* _PyLong_NumBits.  Return the number of bits needed to represent the
index 83f894e552f9832dcb67514f009bbb2c59ad1f9e..06a29b5a0505b427c60806815875365b99f87692 100644 (file)
@@ -721,6 +721,22 @@ class LongTests(unittest.TestCase):
                 self.assertEqual(expect_u, fromnativebytes(v_be, n, 4, 1),
                     f"PyLong_FromNativeBytes(buffer, {n}, <big|unsigned>)")
 
+    def test_long_getsign(self):
+        # Test PyLong_GetSign()
+        getsign = _testcapi.pylong_getsign
+        self.assertEqual(getsign(1), 1)
+        self.assertEqual(getsign(123456), 1)
+        self.assertEqual(getsign(-2), -1)
+        self.assertEqual(getsign(0), 0)
+        self.assertEqual(getsign(True), 1)
+        self.assertEqual(getsign(IntSubclass(-11)), -1)
+        self.assertEqual(getsign(False), 0)
+
+        self.assertRaises(TypeError, getsign, 1.0)
+        self.assertRaises(TypeError, getsign, Index(123))
+
+        # CRASHES getsign(NULL)
+
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/Misc/NEWS.d/next/C API/2024-03-10-14-55-51.gh-issue-116560.x2mZaO.rst b/Misc/NEWS.d/next/C API/2024-03-10-14-55-51.gh-issue-116560.x2mZaO.rst
new file mode 100644 (file)
index 0000000..9bcadfd
--- /dev/null
@@ -0,0 +1 @@
+Add :c:func:`PyLong_GetSign` function.  Patch by Sergey B Kirpichev.
index 769c3909ea3fb1eeabac105d4369561c64942677..2b5e85d570752269850d5c33b288f08f2cf25565 100644 (file)
@@ -92,6 +92,19 @@ pylong_fromnativebytes(PyObject *module, PyObject *args)
     return res;
 }
 
+
+static PyObject *
+pylong_getsign(PyObject *module, PyObject *arg)
+{
+    int sign;
+    NULLABLE(arg);
+    if (PyLong_GetSign(arg, &sign) == -1) {
+        return NULL;
+    }
+    return PyLong_FromLong(sign);
+}
+
+
 static PyObject *
 pylong_aspid(PyObject *module, PyObject *arg)
 {
@@ -109,6 +122,7 @@ static PyMethodDef test_methods[] = {
     {"pylong_fromunicodeobject",    pylong_fromunicodeobject,   METH_VARARGS},
     {"pylong_asnativebytes",        pylong_asnativebytes,       METH_VARARGS},
     {"pylong_fromnativebytes",      pylong_fromnativebytes,     METH_VARARGS},
+    {"pylong_getsign",              pylong_getsign,             METH_O},
     {"pylong_aspid",                pylong_aspid,               METH_O},
     {NULL},
 };
index 2dc2cb7a47b46030ba67e12b28b096c9deb1ab17..054689471e7aa93b9b3ab7de2c324988443f8aa5 100644 (file)
@@ -770,6 +770,18 @@ _PyLong_Sign(PyObject *vv)
     return _PyLong_NonCompactSign(v);
 }
 
+int
+PyLong_GetSign(PyObject *vv, int *sign)
+{
+    if (!PyLong_Check(vv)) {
+        PyErr_Format(PyExc_TypeError, "expect int, got %T", vv);
+        return -1;
+    }
+
+    *sign = _PyLong_Sign(vv);
+    return 0;
+}
+
 static int
 bit_length_digit(digit x)
 {