Skip to content

Commit 14a0635

Browse files
committed
pythongh-120389: Add PyLong_FromInt64() and PyLong_AsInt64()
1 parent f5a9c34 commit 14a0635

File tree

5 files changed

+133
-32
lines changed

5 files changed

+133
-32
lines changed

Include/longobject.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,14 @@ PyAPI_FUNC(unsigned long) PyLong_AsUnsignedLongMask(PyObject *);
3030
PyAPI_FUNC(int) PyLong_AsInt(PyObject *);
3131
#endif
3232

33+
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030e0000
34+
PyAPI_FUNC(PyObject*) PyLong_FromInt64(int64_t value);
35+
PyAPI_FUNC(PyObject*) PyLong_FromUInt64(uint64_t value);
36+
37+
PyAPI_FUNC(int) PyLong_AsInt64(PyObject *obj, int64_t *value);
38+
PyAPI_FUNC(int) PyLong_AsUInt64(PyObject *obj, uint64_t *value);
39+
#endif
40+
3341
PyAPI_FUNC(PyObject *) PyLong_GetInfo(void);
3442

3543
/* It may be useful in the future. I've added it in the PyInt -> PyLong

Lib/test/test_capi/test_long.py

Lines changed: 47 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -185,25 +185,28 @@ def test_long_asint(self):
185185
self.assertRaises(TypeError, PyLong_AsInt, '3')
186186
self.assertRaises(SystemError, PyLong_AsInt, NULL)
187187

188+
def check_long_asint(self, long_asint, min_val, max_val):
189+
# round trip (object -> C integer -> object)
190+
for value in (min_val, max_val, -1, 0, 1, 1234):
191+
with self.subTest(value=value):
192+
self.assertEqual(long_asint(value), value)
193+
194+
self.assertEqual(long_asint(IntSubclass(42)), 42)
195+
self.assertEqual(long_asint(Index(42)), 42)
196+
self.assertEqual(long_asint(MyIndexAndInt()), 10)
197+
198+
self.assertRaises(OverflowError, long_asint, min_val - 1)
199+
self.assertRaises(OverflowError, long_asint, max_val + 1)
200+
self.assertRaises(TypeError, long_asint, 1.0)
201+
self.assertRaises(TypeError, long_asint, b'2')
202+
self.assertRaises(TypeError, long_asint, '3')
203+
self.assertRaises(SystemError, long_asint, NULL)
204+
188205
def test_long_aslong(self):
189206
# Test PyLong_AsLong() and PyLong_FromLong()
190207
aslong = _testlimitedcapi.pylong_aslong
191208
from _testcapi import LONG_MIN, LONG_MAX
192-
# round trip (object -> long -> object)
193-
for value in (LONG_MIN, LONG_MAX, -1, 0, 1, 1234):
194-
with self.subTest(value=value):
195-
self.assertEqual(aslong(value), value)
196-
197-
self.assertEqual(aslong(IntSubclass(42)), 42)
198-
self.assertEqual(aslong(Index(42)), 42)
199-
self.assertEqual(aslong(MyIndexAndInt()), 10)
200-
201-
self.assertRaises(OverflowError, aslong, LONG_MIN - 1)
202-
self.assertRaises(OverflowError, aslong, LONG_MAX + 1)
203-
self.assertRaises(TypeError, aslong, 1.0)
204-
self.assertRaises(TypeError, aslong, b'2')
205-
self.assertRaises(TypeError, aslong, '3')
206-
self.assertRaises(SystemError, aslong, NULL)
209+
self.check_long_asint(aslong, LONG_MIN, LONG_MAX)
207210

208211
def test_long_aslongandoverflow(self):
209212
# Test PyLong_AsLongAndOverflow()
@@ -223,25 +226,28 @@ def test_long_aslongandoverflow(self):
223226
# CRASHES aslongandoverflow(1.0)
224227
# CRASHES aslongandoverflow(NULL)
225228

226-
def test_long_asunsignedlong(self):
227-
# Test PyLong_AsUnsignedLong() and PyLong_FromUnsignedLong()
228-
asunsignedlong = _testlimitedcapi.pylong_asunsignedlong
229-
from _testcapi import ULONG_MAX
229+
def check_long_asunsignedint(self, long_asuint, max_val):
230230
# round trip (object -> unsigned long -> object)
231-
for value in (ULONG_MAX, 0, 1, 1234):
231+
for value in (0, 1, 1234, max_val):
232232
with self.subTest(value=value):
233-
self.assertEqual(asunsignedlong(value), value)
233+
self.assertEqual(long_asuint(value), value)
234+
235+
self.assertEqual(long_asuint(IntSubclass(42)), 42)
236+
self.assertRaises(TypeError, long_asuint, Index(42))
237+
self.assertRaises(TypeError, long_asuint, MyIndexAndInt())
234238

235-
self.assertEqual(asunsignedlong(IntSubclass(42)), 42)
236-
self.assertRaises(TypeError, asunsignedlong, Index(42))
237-
self.assertRaises(TypeError, asunsignedlong, MyIndexAndInt())
239+
self.assertRaises(OverflowError, long_asuint, -1)
240+
self.assertRaises(OverflowError, long_asuint, max_val + 1)
241+
self.assertRaises(TypeError, long_asuint, 1.0)
242+
self.assertRaises(TypeError, long_asuint, b'2')
243+
self.assertRaises(TypeError, long_asuint, '3')
244+
self.assertRaises(SystemError, long_asuint, NULL)
238245

239-
self.assertRaises(OverflowError, asunsignedlong, -1)
240-
self.assertRaises(OverflowError, asunsignedlong, ULONG_MAX + 1)
241-
self.assertRaises(TypeError, asunsignedlong, 1.0)
242-
self.assertRaises(TypeError, asunsignedlong, b'2')
243-
self.assertRaises(TypeError, asunsignedlong, '3')
244-
self.assertRaises(SystemError, asunsignedlong, NULL)
246+
def test_long_asunsignedlong(self):
247+
# Test PyLong_AsUnsignedLong() and PyLong_FromUnsignedLong()
248+
asunsignedlong = _testlimitedcapi.pylong_asunsignedlong
249+
from _testcapi import ULONG_MAX
250+
self.check_long_asunsignedint(asunsignedlong, ULONG_MAX)
245251

246252
def test_long_asunsignedlongmask(self):
247253
# Test PyLong_AsUnsignedLongMask()
@@ -737,6 +743,17 @@ def test_long_getsign(self):
737743

738744
# CRASHES getsign(NULL)
739745

746+
def test_long_asint64(self):
747+
# Test PyLong_AsInt64() and PyLong_FromInt64()
748+
asint64 = _testlimitedcapi.pylong_asint64
749+
from _testcapi import INT64_MIN, INT64_MAX
750+
self.check_long_asint(asint64, INT64_MIN, INT64_MAX)
751+
752+
def test_long_asuint64(self):
753+
# Test PyLong_AsUInt64() and PyLong_FromUInt64()
754+
asuint64 = _testlimitedcapi.pylong_asuint64
755+
from _testcapi import UINT64_MAX
756+
self.check_long_asunsignedint(asuint64, UINT64_MAX)
740757

741758
if __name__ == "__main__":
742759
unittest.main()

Modules/_testcapimodule.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4023,6 +4023,9 @@ PyInit__testcapi(void)
40234023

40244024
PyModule_AddIntConstant(m, "the_number_three", 3);
40254025
PyModule_AddIntMacro(m, Py_C_RECURSION_LIMIT);
4026+
PyModule_AddObject(m, "INT64_MIN", PyLong_FromInt64(INT64_MIN));
4027+
PyModule_AddObject(m, "INT64_MAX", PyLong_FromInt64(INT64_MAX));
4028+
PyModule_AddObject(m, "UINT64_MAX", PyLong_FromUInt64(UINT64_MAX));
40264029

40274030
if (PyModule_AddIntMacro(m, Py_single_input)) {
40284031
return NULL;

Modules/_testlimitedcapi/long.c

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#include "pyconfig.h" // Py_GIL_DISABLED
22
#ifndef Py_GIL_DISABLED
3-
// Need limited C API 3.13 to test PyLong_AsInt()
4-
# define Py_LIMITED_API 0x030d0000
3+
// Need limited C API 3.14 to test PyLong_AsInt64()
4+
# define Py_LIMITED_API 0x030e0000
55
#endif
66

77
#include "parts.h"
@@ -758,6 +758,29 @@ pylong_aspid(PyObject *module, PyObject *arg)
758758
}
759759

760760

761+
static PyObject *
762+
pylong_asint64(PyObject *module, PyObject *arg)
763+
{
764+
NULLABLE(arg);
765+
int64_t value;
766+
if (PyLong_AsInt64(arg, &value) < 0) {
767+
return NULL;
768+
}
769+
return PyLong_FromInt64(value);
770+
}
771+
772+
static PyObject *
773+
pylong_asuint64(PyObject *module, PyObject *arg)
774+
{
775+
NULLABLE(arg);
776+
uint64_t value;
777+
if (PyLong_AsUInt64(arg, &value) < 0) {
778+
return NULL;
779+
}
780+
return PyLong_FromUInt64(value);
781+
}
782+
783+
761784
static PyMethodDef test_methods[] = {
762785
_TESTLIMITEDCAPI_TEST_LONG_AND_OVERFLOW_METHODDEF
763786
_TESTLIMITEDCAPI_TEST_LONG_API_METHODDEF
@@ -785,6 +808,8 @@ static PyMethodDef test_methods[] = {
785808
{"pylong_asdouble", pylong_asdouble, METH_O},
786809
{"pylong_asvoidptr", pylong_asvoidptr, METH_O},
787810
{"pylong_aspid", pylong_aspid, METH_O},
811+
{"pylong_asint64", pylong_asint64, METH_O},
812+
{"pylong_asuint64", pylong_asuint64, METH_O},
788813
{NULL},
789814
};
790815

Objects/longobject.c

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,11 @@ PyLong_FromUnsignedLongLong(unsigned long long ival)
369369
PYLONG_FROM_UINT(unsigned long long, ival);
370370
}
371371

372+
PyObject* PyLong_FromUInt64(uint64_t value)
373+
{
374+
PYLONG_FROM_UINT(uint64_t, value);
375+
}
376+
372377
/* Create a new int object from a C size_t. */
373378

374379
PyObject *
@@ -6681,3 +6686,46 @@ Py_ssize_t
66816686
PyUnstable_Long_CompactValue(const PyLongObject* op) {
66826687
return _PyLong_CompactValue(op);
66836688
}
6689+
6690+
PyObject* PyLong_FromInt64(int64_t value)
6691+
{
6692+
#if SIZEOF_LONG >= 8
6693+
return PyLong_FromLong(value);
6694+
#elif SIZEOF_LONG_LONG >= 8
6695+
return PyLong_FromLongLong(value);
6696+
#else
6697+
# error "unknown long type"
6698+
#endif
6699+
}
6700+
6701+
int PyLong_AsInt64(PyObject *obj, int64_t *value)
6702+
{
6703+
#if SIZEOF_LONG >= 8
6704+
long res = PyLong_AsLong(obj);
6705+
#elif SIZEOF_LONG_LONG >= 8
6706+
long long res = PyLong_AsLongLong(obj);
6707+
#else
6708+
# error "unknown long type"
6709+
#endif
6710+
if (res == -1 && PyErr_Occurred()) {
6711+
return -1;
6712+
}
6713+
*value = res;
6714+
return 0;
6715+
}
6716+
6717+
int PyLong_AsUInt64(PyObject *obj, uint64_t *value)
6718+
{
6719+
#if SIZEOF_LONG >= 8
6720+
long res = PyLong_AsUnsignedLong(obj);
6721+
#elif SIZEOF_LONG_LONG >= 8
6722+
long long res = PyLong_AsUnsignedLongLong(obj);
6723+
#else
6724+
# error "unknown long type"
6725+
#endif
6726+
if (res == -1 && PyErr_Occurred()) {
6727+
return -1;
6728+
}
6729+
*value = res;
6730+
return 0;
6731+
}

0 commit comments

Comments
 (0)