Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-116560: Add PyLong_GetSign() public function #116561

Merged
merged 18 commits into from
Jun 3, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Doc/c-api/long.rst
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,17 @@ 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*: ``0``, ``-1`` or ``+1`` for zero,
negative or positive integer, respectively.

Return ``0`` on success, else ``-1`` with an exception set. This function
always succeeds if *obj* is a :c:type:`PyLongObject` or its subtype.
vstinner marked this conversation as resolved.
Show resolved Hide resolved
vstinner marked this conversation as resolved.
Show resolved Hide resolved

.. versionadded:: 3.14


.. c:function:: int PyUnstable_Long_IsCompact(const PyLongObject* op)

Return 1 if *op* is compact, 0 otherwise.
Expand Down
3 changes: 3 additions & 0 deletions Doc/whatsnew/3.14.rst
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,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
----------------------

Expand Down
10 changes: 7 additions & 3 deletions Include/cpython/longobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 16 additions & 0 deletions Lib/test/test_capi/test_long.py
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,22 @@ def test_long_fromnativebytes(self):
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))

# CRACHES(getsign)
skirpichev marked this conversation as resolved.
Show resolved Hide resolved
skirpichev marked this conversation as resolved.
Show resolved Hide resolved


if __name__ == "__main__":
unittest.main()
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add :c:func:`PyLong_GetSign` function. Patch by Sergey B Kirpichev.
14 changes: 14 additions & 0 deletions Modules/_testcapi/long.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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},
};
Expand Down
12 changes: 12 additions & 0 deletions Objects/longobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
Loading