summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/c-api/long.rst13
-rw-r--r--Doc/whatsnew/3.14.rst3
-rw-r--r--Include/cpython/longobject.h10
-rw-r--r--Lib/test/test_capi/test_long.py16
-rw-r--r--Misc/NEWS.d/next/C API/2024-03-10-14-55-51.gh-issue-116560.x2mZaO.rst1
-rw-r--r--Modules/_testcapi/long.c14
-rw-r--r--Objects/longobject.c12
7 files changed, 66 insertions, 3 deletions
diff --git a/Doc/c-api/long.rst b/Doc/c-api/long.rst
index 522c028..a0e111a 100644
--- a/Doc/c-api/long.rst
+++ b/Doc/c-api/long.rst
@@ -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.
diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst
index 47f3e30..b2dd80b 100644
--- a/Doc/whatsnew/3.14.rst
+++ b/Doc/whatsnew/3.14.rst
@@ -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
----------------------
diff --git a/Include/cpython/longobject.h b/Include/cpython/longobject.h
index 9681593..19a6722 100644
--- a/Include/cpython/longobject.h
+++ b/Include/cpython/longobject.h
@@ -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
diff --git a/Lib/test/test_capi/test_long.py b/Lib/test/test_capi/test_long.py
index 83f894e..06a29b5 100644
--- a/Lib/test/test_capi/test_long.py
+++ b/Lib/test/test_capi/test_long.py
@@ -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
index 0000000..9bcadfd
--- /dev/null
+++ b/Misc/NEWS.d/next/C API/2024-03-10-14-55-51.gh-issue-116560.x2mZaO.rst
@@ -0,0 +1 @@
+Add :c:func:`PyLong_GetSign` function. Patch by Sergey B Kirpichev.
diff --git a/Modules/_testcapi/long.c b/Modules/_testcapi/long.c
index 769c390..2b5e85d 100644
--- a/Modules/_testcapi/long.c
+++ b/Modules/_testcapi/long.c
@@ -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},
};
diff --git a/Objects/longobject.c b/Objects/longobject.c
index 2dc2cb7..0546894 100644
--- a/Objects/longobject.c
+++ b/Objects/longobject.c
@@ -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)
{