Skip to content

Commit 61d3ab3

Browse files
skirpichevvstinner
andauthored
gh-116560: Add PyLong_GetSign() public function (#116561)
Co-authored-by: Victor Stinner <[email protected]>
1 parent 367adc9 commit 61d3ab3

File tree

7 files changed

+66
-3
lines changed

7 files changed

+66
-3
lines changed

Doc/c-api/long.rst

+13
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,19 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate.
494494
.. versionadded:: 3.13
495495
496496
497+
.. c:function:: int PyLong_GetSign(PyObject *obj, int *sign)
498+
499+
Get the sign of the integer object *obj*.
500+
501+
On success, set *\*sign* to the integer sign (0, -1 or +1 for zero, negative or
502+
positive integer, respectively) and return 0.
503+
504+
On failure, return -1 with an exception set. This function always succeeds
505+
if *obj* is a :c:type:`PyLongObject` or its subtype.
506+
507+
.. versionadded:: 3.14
508+
509+
497510
.. c:function:: int PyUnstable_Long_IsCompact(const PyLongObject* op)
498511
499512
Return 1 if *op* is compact, 0 otherwise.

Doc/whatsnew/3.14.rst

+3
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,9 @@ C API Changes
255255
New Features
256256
------------
257257

258+
* Add :c:func:`PyLong_GetSign` function to get the sign of :class:`int` objects.
259+
(Contributed by Sergey B Kirpichev in :gh:`116560`.)
260+
258261
Porting to Python 3.14
259262
----------------------
260263

Include/cpython/longobject.h

+7-3
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,13 @@ PyAPI_FUNC(PyObject*) PyLong_FromUnsignedNativeBytes(const void* buffer,
5555
PyAPI_FUNC(int) PyUnstable_Long_IsCompact(const PyLongObject* op);
5656
PyAPI_FUNC(Py_ssize_t) PyUnstable_Long_CompactValue(const PyLongObject* op);
5757

58-
// _PyLong_Sign. Return 0 if v is 0, -1 if v < 0, +1 if v > 0.
59-
// v must not be NULL, and must be a normalized long.
60-
// There are no error cases.
58+
/* PyLong_GetSign. Get the sign of an integer object:
59+
0, -1 or +1 for zero, negative or positive integer, respectively.
60+
61+
- On success, set '*sign' to the integer sign, and return 0.
62+
- On failure, set an exception, and return -1. */
63+
PyAPI_FUNC(int) PyLong_GetSign(PyObject *v, int *sign);
64+
6165
PyAPI_FUNC(int) _PyLong_Sign(PyObject *v);
6266

6367
/* _PyLong_NumBits. Return the number of bits needed to represent the

Lib/test/test_capi/test_long.py

+16
Original file line numberDiff line numberDiff line change
@@ -721,6 +721,22 @@ def test_long_fromnativebytes(self):
721721
self.assertEqual(expect_u, fromnativebytes(v_be, n, 4, 1),
722722
f"PyLong_FromNativeBytes(buffer, {n}, <big|unsigned>)")
723723

724+
def test_long_getsign(self):
725+
# Test PyLong_GetSign()
726+
getsign = _testcapi.pylong_getsign
727+
self.assertEqual(getsign(1), 1)
728+
self.assertEqual(getsign(123456), 1)
729+
self.assertEqual(getsign(-2), -1)
730+
self.assertEqual(getsign(0), 0)
731+
self.assertEqual(getsign(True), 1)
732+
self.assertEqual(getsign(IntSubclass(-11)), -1)
733+
self.assertEqual(getsign(False), 0)
734+
735+
self.assertRaises(TypeError, getsign, 1.0)
736+
self.assertRaises(TypeError, getsign, Index(123))
737+
738+
# CRASHES getsign(NULL)
739+
724740

725741
if __name__ == "__main__":
726742
unittest.main()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add :c:func:`PyLong_GetSign` function. Patch by Sergey B Kirpichev.

Modules/_testcapi/long.c

+14
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,19 @@ pylong_fromnativebytes(PyObject *module, PyObject *args)
9292
return res;
9393
}
9494

95+
96+
static PyObject *
97+
pylong_getsign(PyObject *module, PyObject *arg)
98+
{
99+
int sign;
100+
NULLABLE(arg);
101+
if (PyLong_GetSign(arg, &sign) == -1) {
102+
return NULL;
103+
}
104+
return PyLong_FromLong(sign);
105+
}
106+
107+
95108
static PyObject *
96109
pylong_aspid(PyObject *module, PyObject *arg)
97110
{
@@ -109,6 +122,7 @@ static PyMethodDef test_methods[] = {
109122
{"pylong_fromunicodeobject", pylong_fromunicodeobject, METH_VARARGS},
110123
{"pylong_asnativebytes", pylong_asnativebytes, METH_VARARGS},
111124
{"pylong_fromnativebytes", pylong_fromnativebytes, METH_VARARGS},
125+
{"pylong_getsign", pylong_getsign, METH_O},
112126
{"pylong_aspid", pylong_aspid, METH_O},
113127
{NULL},
114128
};

Objects/longobject.c

+12
Original file line numberDiff line numberDiff line change
@@ -770,6 +770,18 @@ _PyLong_Sign(PyObject *vv)
770770
return _PyLong_NonCompactSign(v);
771771
}
772772

773+
int
774+
PyLong_GetSign(PyObject *vv, int *sign)
775+
{
776+
if (!PyLong_Check(vv)) {
777+
PyErr_Format(PyExc_TypeError, "expect int, got %T", vv);
778+
return -1;
779+
}
780+
781+
*sign = _PyLong_Sign(vv);
782+
return 0;
783+
}
784+
773785
static int
774786
bit_length_digit(digit x)
775787
{

0 commit comments

Comments
 (0)