]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-126061: Add PyLong_IsPositive/Zero/Negative() functions (#126065)
authorRUANG (James Roy) <longjinyii@outlook.com>
Tue, 12 Nov 2024 13:18:06 +0000 (21:18 +0800)
committerGitHub <noreply@github.com>
Tue, 12 Nov 2024 13:18:06 +0000 (14:18 +0100)
Co-authored-by: Sergey B Kirpichev <skirpichev@gmail.com>
Co-authored-by: Peter Bierma <zintensitydev@gmail.com>
Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
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-10-28-15-56-03.gh-issue-126061.Py51_1.rst [new file with mode: 0644]
Modules/_testcapi/long.c
Objects/longobject.c

index 9ff3e5265004a10d0144d8ab57ab751aac0a700f..32bb451b08d4138451d8fe51729a4281458ff1fb 100644 (file)
@@ -582,6 +582,39 @@ distinguished from a number.  Use :c:func:`PyErr_Occurred` to disambiguate.
    .. versionadded:: 3.14
 
 
+.. c:function:: int PyLong_IsPositive(PyObject *obj)
+
+   Check if the integer object *obj* is positive (``obj > 0``).
+
+   If *obj* is an instance of :c:type:`PyLongObject` or its subtype,
+   return ``1`` when it's positive and ``0`` otherwise.  Else set an
+   exception and return ``-1``.
+
+   .. versionadded:: next
+
+
+.. c:function:: int PyLong_IsNegative(PyObject *obj)
+
+   Check if the integer object *obj* is negative (``obj < 0``).
+
+   If *obj* is an instance of :c:type:`PyLongObject` or its subtype,
+   return ``1`` when it's negative and ``0`` otherwise.  Else set an
+   exception and return ``-1``.
+
+   .. versionadded:: next
+
+
+.. c:function:: int PyLong_IsZero(PyObject *obj)
+
+   Check if the integer object *obj* is zero.
+
+   If *obj* is an instance of :c:type:`PyLongObject` or its subtype,
+   return ``1`` when it's zero and ``0`` otherwise.  Else set an
+   exception and return ``-1``.
+
+   .. versionadded:: next
+
+
 .. c:function:: PyObject* PyLong_GetInfo(void)
 
    On success, return a read only :term:`named tuple`, that holds
index 66f8c432aead61f3e74c6890142609e340f062b0..20bd3aa522145402fe683529b778ec000451ac0d 100644 (file)
@@ -807,6 +807,11 @@ New features
   an interned string and deallocate it during module shutdown.
   (Contributed by Eddie Elizondo in :gh:`113601`.)
 
+* Add :c:func:`PyLong_IsPositive`, :c:func:`PyLong_IsNegative`
+  and :c:func:`PyLong_IsZero` for checking if :c:type:`PyLongObject`
+  is positive, negative, or zero, respectively.
+  (Contribued by James Roy and Sergey B Kirpichev in :gh:`126061`.)
+
 * Add new functions to convert C ``<stdint.h>`` numbers from/to Python
   :class:`int`:
 
index c1214d5e3714ead44bda318fc5d9031e78993622..4d6e618f831ad8aa849cf0101dc249443180e837 100644 (file)
@@ -61,6 +61,24 @@ 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_IsPositive.  Check if the integer object is positive.
+
+   - On success, return 1 if *obj is positive, and 0 otherwise.
+   - On failure, set an exception, and return -1. */
+PyAPI_FUNC(int) PyLong_IsPositive(PyObject *obj);
+
+/* PyLong_IsNegative.  Check if the integer object is negative.
+
+   - On success, return 1 if *obj is negative, and 0 otherwise.
+   - On failure, set an exception, and return -1. */
+PyAPI_FUNC(int) PyLong_IsNegative(PyObject *obj);
+
+/* PyLong_IsZero.  Check if the integer object is zero.
+
+   - On success, return 1 if *obj is zero, and 0 if it is non-zero.
+   - On failure, set an exception, and return -1. */
+PyAPI_FUNC(int) PyLong_IsZero(PyObject *obj);
+
 /* PyLong_GetSign.  Get the sign of an integer object:
    0, -1 or +1 for zero, negative or positive integer, respectively.
 
index 925fccd660bde38758991d8b5232d9d6a02a3dda..a77094588a0edf716c4eafb47b99488a9ceec345 100644 (file)
@@ -643,6 +643,51 @@ class LongTests(unittest.TestCase):
 
         # CRASHES getsign(NULL)
 
+    def test_long_ispositive(self):
+        # Test PyLong_IsPositive()
+        ispositive = _testcapi.pylong_ispositive
+        self.assertEqual(ispositive(1), 1)
+        self.assertEqual(ispositive(123), 1)
+        self.assertEqual(ispositive(-1), 0)
+        self.assertEqual(ispositive(0), 0)
+        self.assertEqual(ispositive(True), 1)
+        self.assertEqual(ispositive(False), 0)
+        self.assertEqual(ispositive(IntSubclass(-1)), 0)
+        self.assertRaises(TypeError, ispositive, 1.0)
+        self.assertRaises(TypeError, ispositive, Index(123))
+
+        # CRASHES ispositive(NULL)
+
+    def test_long_isnegative(self):
+        # Test PyLong_IsNegative()
+        isnegative = _testcapi.pylong_isnegative
+        self.assertEqual(isnegative(1), 0)
+        self.assertEqual(isnegative(123), 0)
+        self.assertEqual(isnegative(-1), 1)
+        self.assertEqual(isnegative(0), 0)
+        self.assertEqual(isnegative(True), 0)
+        self.assertEqual(isnegative(False), 0)
+        self.assertEqual(isnegative(IntSubclass(-1)), 1)
+        self.assertRaises(TypeError, isnegative, 1.0)
+        self.assertRaises(TypeError, isnegative, Index(123))
+
+        # CRASHES isnegative(NULL)
+
+    def test_long_iszero(self):
+        # Test PyLong_IsZero()
+        iszero = _testcapi.pylong_iszero
+        self.assertEqual(iszero(1), 0)
+        self.assertEqual(iszero(-1), 0)
+        self.assertEqual(iszero(0), 1)
+        self.assertEqual(iszero(True), 0)
+        self.assertEqual(iszero(False), 1)
+        self.assertEqual(iszero(IntSubclass(-1)), 0)
+        self.assertEqual(iszero(IntSubclass(0)), 1)
+        self.assertRaises(TypeError, iszero, 1.0)
+        self.assertRaises(TypeError, iszero, Index(123))
+
+        # CRASHES iszero(NULL)
+
     def test_long_asint32(self):
         # Test PyLong_AsInt32() and PyLong_FromInt32()
         to_int32 = _testlimitedcapi.pylong_asint32
diff --git a/Misc/NEWS.d/next/C_API/2024-10-28-15-56-03.gh-issue-126061.Py51_1.rst b/Misc/NEWS.d/next/C_API/2024-10-28-15-56-03.gh-issue-126061.Py51_1.rst
new file mode 100644 (file)
index 0000000..0a4ad4e
--- /dev/null
@@ -0,0 +1,3 @@
+Add :c:func:`PyLong_IsPositive`, :c:func:`PyLong_IsNegative`
+and :c:func:`PyLong_IsZero` for checking if a :c:type:`PyLongObject`
+is positive, negative, or zero, respectively.
index 2b5e85d570752269850d5c33b288f08f2cf25565..ebea09080ef11c7c580d175b1f8f237232da48ca 100644 (file)
@@ -105,6 +105,30 @@ pylong_getsign(PyObject *module, PyObject *arg)
 }
 
 
+static PyObject *
+pylong_ispositive(PyObject *module, PyObject *arg)
+{
+    NULLABLE(arg);
+    RETURN_INT(PyLong_IsPositive(arg));
+}
+
+
+static PyObject *
+pylong_isnegative(PyObject *module, PyObject *arg)
+{
+    NULLABLE(arg);
+    RETURN_INT(PyLong_IsNegative(arg));
+}
+
+
+static PyObject *
+pylong_iszero(PyObject *module, PyObject *arg)
+{
+    NULLABLE(arg);
+    RETURN_INT(PyLong_IsZero(arg));
+}
+
+
 static PyObject *
 pylong_aspid(PyObject *module, PyObject *arg)
 {
@@ -124,6 +148,9 @@ static PyMethodDef test_methods[] = {
     {"pylong_fromnativebytes",      pylong_fromnativebytes,     METH_VARARGS},
     {"pylong_getsign",              pylong_getsign,             METH_O},
     {"pylong_aspid",                pylong_aspid,               METH_O},
+    {"pylong_ispositive",           pylong_ispositive,          METH_O},
+    {"pylong_isnegative",           pylong_isnegative,          METH_O},
+    {"pylong_iszero",               pylong_iszero,              METH_O},
     {NULL},
 };
 
index b4c0f63a9843ce587931b417a7aed3d37cc5bb51..4aa35685b509f27d92bc8a8c12eaed43e344ee04 100644 (file)
@@ -784,6 +784,39 @@ PyLong_AsUnsignedLongMask(PyObject *op)
     return val;
 }
 
+int
+PyLong_IsPositive(PyObject *obj)
+{
+    assert(obj != NULL);
+    if (!PyLong_Check(obj)) {
+        PyErr_Format(PyExc_TypeError, "expected int, got %T", obj);
+        return -1;
+    }
+    return _PyLong_IsPositive((PyLongObject *)obj);
+}
+
+int
+PyLong_IsNegative(PyObject *obj)
+{
+    assert(obj != NULL);
+    if (!PyLong_Check(obj)) {
+        PyErr_Format(PyExc_TypeError, "expected int, got %T", obj);
+        return -1;
+    }
+    return _PyLong_IsNegative((PyLongObject *)obj);
+}
+
+int
+PyLong_IsZero(PyObject *obj)
+{
+    assert(obj != NULL);
+    if (!PyLong_Check(obj)) {
+        PyErr_Format(PyExc_TypeError, "expected int, got %T", obj);
+        return -1;
+    }
+    return _PyLong_IsZero((PyLongObject *)obj);
+}
+
 int
 _PyLong_Sign(PyObject *vv)
 {