8000 gh-126061: Add PyLong_IsPositive/Zero/Negative() functions (#126065) · python/cpython@8ff7efb · GitHub
[go: up one dir, main page]

Skip to content

Commit 8ff7efb

Browse files
rruuaanngskirpichevZeroIntensitypicnixz
authored
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>
1 parent abb90ba commit 8ff7efb

File tree

7 files changed

+164
-0
lines changed

7 files changed

+164
-0
lines changed

Doc/c-api/long.rst

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,39 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate.
582582
.. versionadded:: 3.14
583583
584584
585+
.. c:function:: int PyLong_IsPositive(PyObject *obj)
586+
587+
Check if the integer object *obj* is positive (``obj > 0``).
588+
589+
If *obj* is an instance of :c:type:`PyLongObject` or its subtype,
590+
return ``1`` when it's positive and ``0`` otherwise. Else set an
591+
exception and return ``-1``.
592+
593+
.. versionadded:: next
594+
595+
596+
.. c:function:: int PyLong_IsNegative(PyObject *obj)
597+
598+
Check if the integer object *obj* is negative (``obj < 0``).
599+
600+
If *obj* is an instance of :c:type:`PyLongObject` or its subtype,
601+
return ``1`` when it's negative and ``0`` otherwise. Else set an
602+
exception and return ``-1``.
603+
604+
.. versionadded:: next
605+
606+
607+
.. c:function:: int PyLong_IsZero(PyObject *obj)
608+
609+
Check if the integer object *obj* is zero.
610+
611+
If *obj* is an instance of :c:type:`PyLongObject` or its subtype,
612+
return ``1`` when it's zero and ``0`` otherwise. Else set an
613+
exception and return ``-1``.
614+
615+
.. versionadded:: next
616+
617+
585618
.. c:function:: PyObject* PyLong_GetInfo(void)
586619
587620
On success, return a read only :term:`named tuple`, that holds

Doc/whatsnew/3.14.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -807,6 +807,11 @@ New features
807807
an interned string and deallocate it during module shutdown.
808808
(Contributed by Eddie Elizondo in :gh:`113601`.)
809809

810+
* Add :c:func:`PyLong_IsPositive`, :c:func:`PyLong_IsNegative`
811+
and :c:func:`PyLong_IsZero` for checking if :c:type:`PyLongObject`
812+
is positive, negative, or zero, respectively.
813+
(Contribued by James Roy and Sergey B Kirpichev in :gh:`126061`.)
814+
810815
* Add new functions to convert C ``<stdint.h>`` numbers from/to Python
811816
:class:`int`:
812817

Include/cpython/longobject.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,24 @@ PyAPI_FUNC(PyObject*) PyLong_FromUnsignedNativeBytes(const void* buffer,
6161
PyAPI_FUNC(int) PyUnstable_Long_IsCompact(const PyLongObject* op);
6262
PyAPI_FUNC(Py_ssize_t) PyUnstable_Long_CompactValue(const PyLongObject* op);
6363

64+
/* PyLong_IsPositive. Check if the integer object is positive.
65+
66+
- On success, return 1 if *obj is positive, and 0 otherwise.
67+
- On failure, set an exception, and return -1. */
68+
PyAPI_FUNC(int) PyLong_IsPositive(PyObject *obj);
69+
70+
/* PyLong_IsNegative. Check if the integer object is negative.
71+
72+
- On success, return 1 if *obj is negative, and 0 otherwise.
73+
- On failure, set an exception, and return -1. */
74+
PyAPI_FUNC(int) PyLong_IsNegative(PyObject *obj);
75+
76+
/* PyLong_IsZero. Check if the integer object is zero.
77+
78+
- On success, return 1 if *obj is zero, and 0 if it is non-zero.
79+
- On failure, set an exception, and return -1. */
80+
PyAPI_FUNC(int) PyLong_IsZero(PyObject *obj);
81+
6482
/* PyLong_GetSign. Get the sign of an integer object:
6583
0, -1 or +1 for zero, negative or positive integer, respectively.
6684

Lib/test/test_capi/test_long.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,51 @@ def test_long_getsign(self):
643643

644644
# CRASHES getsign(NULL)
645645

646+
def test_long_ispositive(self):
647+
# Test PyLong_IsPositive()
648+
ispositive = _testcapi.pylong_ispositive
649+
self.assertEqual(ispositive(1), 1)
650+
self.assertEqual(ispositive(123), 1)
651+
self.assertEqual(ispositive(-1), 0)
652+
self.assertEqual(ispositive(0), 0)
653+
self.assertEqual(ispositive(True), 1)
654+
self.assertEqual(ispositive(False), 0)
655+
self.assertEqual(ispositive(IntSubclass(-1)), 0)
656+
self.assertRaises(TypeError, ispositive, 1.0)
657+
self.assertRaises(TypeError, ispositive, Index(123))
658+
659+
# CRASHES ispositive(NULL)
660+
661+
def test_long_isnegative(self):
662+
# Test PyLong_IsNegative()
663+
isnegative = _testcapi.pylong_isnegative
664+
self.assertEqual(isnegative(1), 0)
665+
self.assertEqual(isnegative(123), 0)
666+
self.assertEqual(isnegative(-1), 1)
667+
self.assertEqual(isnegative(0), 0)
668+
self.assertEqual(isnegative(True), 0)
669+
self.assertEqual(isnegative(False), 0)
670+
self.assertEqual(isnegative(IntSubclass(-1)), 1)
671+
self.assertRaises(TypeError, isnegative, 1.0)
672+
self.assertRaises(TypeError, isnegative, Index(123))
673+
674+
# CRASHES isnegative(NULL)
675+
676+
def test_long_iszero(self):
677+
# Test PyLong_IsZero()
678+
iszero = _testcapi.pylong_iszero
679+
self.assertEqual(iszero(1), 0)
680+
self.assertEqual(iszero(-1), 0)
681+
self.assertEqual(iszero(0), 1)
682+
self.assertEqual(iszero(True), 0)
683+
self.assertEqual(iszero(False), 1)
684+
self.assertEqual(iszero(IntSubclass(-1)), 0)
685+
self.assertEqual(iszero(IntSubclass(0)), 1)
686+
self.assertRaises(TypeError, iszero, 1.0)
687+
self.assertRaises(TypeError, iszero, Index(123))
688+
689+
# CRASHES iszero(NULL)
690+
646691
def test_long_asint32(self):
647692
# Test PyLong_AsInt32() and PyLong_FromInt32()
648693
to_int32 = _testlimitedcapi.pylong_asint32
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Add :c:func:`PyLong_IsPositive`, :c:func:`PyLong_IsNegative`
2+
and :c:func:`PyLong_IsZero` for checking if a :c:type:`PyLongObject`
3+
is positive, negative, or zero, respectively.

Modules/_testcapi/long.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,30 @@ pylong_getsign(PyObject *module, PyObject *arg)
105105
}
106106

107107

108+
static PyObject *
109+
pylong_ispositive(PyObject *module, PyObject *arg)
110+
{
111+
NULLABLE(arg);
112+
RETURN_INT(PyLong_IsPositive(arg));
113+
}
114+
115+
116+
static PyObject *
117+
pylong_isnegative(PyObject *module, PyObject *arg)
118+
{
119+
NULLABLE(arg);
120+
RETURN_INT(PyLong_IsNegative(arg));
121+
}
122+
123+
124+
static PyObject *
125+
pylong_iszero(PyObject *module, PyObject *arg)
126+
{
127+
NULLABLE(arg);
128+
RETURN_INT(PyLong_IsZero(arg));
129+
}
130+
131+
108132
static PyObject *
109133
pylong_aspid(PyObject *module, PyObject *arg)
110134
{
@@ -124,6 +148,9 @@ static PyMethodDef test_methods[] = {
124148
{"pylong_fromnativebytes", pylong_fromnativebytes, METH_VARARGS},
125149
{"pylong_getsign", pylong_getsign, METH_O},
126150
{"pylong_aspid", pylong_aspid, METH_O},
151+
{"pylong_ispositive", pylong_ispositive, METH_O},
152+
{"pylong_isnegative", pylong_isnegative, METH_O},
153+
{"pylong_iszero", pylong_iszero, METH_O},
127154
{NULL},
128155
};
129156

Objects/longobject.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -784,6 +784,39 @@ PyLong_AsUnsignedLongMask(PyObject *op)
784784
return val;
785785
}
786786

787+
int
788+
PyLong_IsPositive(PyObject *obj)
789+
{
790+
assert(obj != NULL);
791+
if (!PyLong_Check(obj)) {
792+
PyErr_Format(PyExc_TypeError, "expected int, got %T", obj);
793+
return -1;
794+
}
795+
return _PyLong_IsPositive((PyLongObject *)obj);
796+
}
797+
798+
int
799+
PyLong_IsNegative(PyObject *obj)
800+
{
801+
assert(obj != NULL);
802+
if (!PyLong_Check(obj)) {
803+
PyErr_Format(PyExc_TypeError, "expected int, got %T", obj);
804+
return -1;
805+
}
806+
return _PyLong_IsNegative((PyLongObject *)obj);
807+
}
808+
809+
int
810+
PyLong_IsZero(PyObject *obj)
811+
{
812+
assert(obj != NULL);
813+
if (!PyLong_Check(obj)) {
814+
PyErr_Format(PyExc_TypeError, "expected int, 6418 got %T", obj);
815+
return -1;
816+
}
817+
return _PyLong_IsZero((PyLongObject *)obj);
818+
}
819+
787820
int
788821
_PyLong_Sign(PyObject *vv)
789822
{

0 commit comments

Comments
 (0)
0