Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 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)

Retrieve the sign of integer object *obj* (``0``, ``-1`` or ``+1`` for zero,
negative or positive integer, respectively) in a variable *sign*.
Comment thread
skirpichev marked this conversation as resolved.
Outdated

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

.. versionadded:: 3.13
Comment thread
skirpichev marked this conversation as resolved.
Outdated


.. 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.13.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2130,6 +2130,9 @@ New Features
between native integer types and Python :class:`int` objects.
(Contributed by Steve Dower in :gh:`111140`.)

* Add :c:func:`PyLong_GetSign` function to test sign of :class:`int` objects.
Comment thread
skirpichev marked this conversation as resolved.
Outdated
Contributed by Sergey B Kirpichev.
Comment thread
skirpichev marked this conversation as resolved.
Outdated

* Add :c:func:`PyType_GetFullyQualifiedName` function to get the type's fully
qualified name. Equivalent to ``f"{type.__module__}.{type.__qualname__}"``,
or ``type.__qualname__`` if ``type.__module__`` is not a string or is equal
Expand Down
7 changes: 4 additions & 3 deletions Include/cpython/longobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,10 @@ 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. Retrieve the sign of the integer value (0, -1 or +1) in
a variable sign. Return 0 on success, else -1 with an exception set. */
Comment thread
skirpichev marked this conversation as resolved.
Outdated
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
15 changes: 15 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,21 @@ 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_sign(self):
# Test PyLong_GetSign()
sign = _testcapi.pylong_getsign
self.assertEqual(sign(1), 1)
self.assertEqual(sign(123456), 1)
self.assertEqual(sign(-2), -1)
self.assertEqual(sign(0), 0)
self.assertEqual(sign(True), 1)
self.assertEqual(sign(IntSubclass(-11)), -1)
self.assertEqual(sign(False), 0)

self.assertRaises(TypeError, sign, 1.0)
self.assertRaises(TypeError, sign, Index(123))
self.assertRaises(SystemError, sign, NULL)


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)
{
NULLABLE(arg);
int sign;
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
16 changes: 16 additions & 0 deletions Objects/longobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,22 @@ _PyLong_Sign(PyObject *vv)
return _PyLong_NonCompactSign(v);
}

int
PyLong_GetSign(PyObject *vv, int *sign)
{
if (vv == NULL) {
PyErr_BadInternalCall();
return -1;
}
Comment thread
skirpichev marked this conversation as resolved.
Outdated
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