summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRUANG (James Roy) <longjinyii@outlook.com>2024-11-12 13:18:06 (GMT)
committerGitHub <noreply@github.com>2024-11-12 13:18:06 (GMT)
commit8ff7efb46d34ee454239bd86ff5136f386b9749b (patch)
tree0dae8e79e19bbec4ea0546670604b4ef0f8a590b
parentabb90ba46c597a1b192027e914ad312dd62d2462 (diff)
downloadcpython-8ff7efb46d34ee454239bd86ff5136f386b9749b.zip
cpython-8ff7efb46d34ee454239bd86ff5136f386b9749b.tar.gz
cpython-8ff7efb46d34ee454239bd86ff5136f386b9749b.tar.bz2
gh-126061: Add PyLong_IsPositive/Zero/Negative() functions (#126065)
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>
-rw-r--r--Doc/c-api/long.rst33
-rw-r--r--Doc/whatsnew/3.14.rst5
-rw-r--r--Include/cpython/longobject.h18
-rw-r--r--Lib/test/test_capi/test_long.py45
-rw-r--r--Misc/NEWS.d/next/C_API/2024-10-28-15-56-03.gh-issue-126061.Py51_1.rst3
-rw-r--r--Modules/_testcapi/long.c27
-rw-r--r--Objects/longobject.c33
7 files changed, 164 insertions, 0 deletions
diff --git a/Doc/c-api/long.rst b/Doc/c-api/long.rst
index 9ff3e52..32bb451 100644
--- a/Doc/c-api/long.rst
+++ b/Doc/c-api/long.rst
@@ -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
diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst
index 66f8c432..20bd3aa 100644
--- a/Doc/whatsnew/3.14.rst
+++ b/Doc/whatsnew/3.14.rst
@@ -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`:
diff --git a/Include/cpython/longobject.h b/Include/cpython/longobject.h
index c1214d5..4d6e618 100644
--- a/Include/cpython/longobject.h
+++ b/Include/cpython/longobject.h
@@ -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.
diff --git a/Lib/test/test_capi/test_long.py b/Lib/test/test_capi/test_long.py
index 925fccd..a770945 100644
--- a/Lib/test/test_capi/test_long.py
+++ b/Lib/test/test_capi/test_long.py
@@ -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
index 0000000..0a4ad4e
--- /dev/null
+++ b/Misc/NEWS.d/next/C_API/2024-10-28-15-56-03.gh-issue-126061.Py51_1.rst
@@ -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.
diff --git a/Modules/_testcapi/long.c b/Modules/_testcapi/long.c
index 2b5e85d..ebea090 100644
--- a/Modules/_testcapi/long.c
+++ b/Modules/_testcapi/long.c
@@ -106,6 +106,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)
{
NULLABLE(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},
};
diff --git a/Objects/longobject.c b/Objects/longobject.c
index b4c0f63..4aa3568 100644
--- a/Objects/longobject.c
+++ b/Objects/longobject.c
@@ -785,6 +785,39 @@ PyLong_AsUnsignedLongMask(PyObject *op)
}
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)
{
PyLongObject *v = (PyLongObject *)vv;