Skip to content

Commit 684360e

Browse files
committed
gh-120389: Add PyLong_FromInt64() and PyLong_ToInt64()
Add new functions to convert C <stdint.h> numbers from/to Python int: * PyLong_FromInt32() * PyLong_FromUInt32() * PyLong_FromInt64() * PyLong_FromUInt64() * PyLong_ToInt32() * PyLong_ToUInt32() * PyLong_ToInt64() * PyLong_ToUInt64()
1 parent ace2045 commit 684360e

File tree

11 files changed

+277
-32
lines changed

11 files changed

+277
-32
lines changed

Doc/data/stable_abi.dat

+8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Doc/whatsnew/3.14.rst

+14
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,20 @@ New Features
298298

299299
(Contributed by Victor Stinner in :gh:`119182`.)
300300

301+
* Add new functions to convert C ``<stdint.h>`` numbers from/to Python
302+
:class:`int`:
303+
304+
* :c:func:`PyLong_FromInt32`
305+
* :c:func:`PyLong_FromUInt32`
306+
* :c:func:`PyLong_FromInt64`
307+
* :c:func:`PyLong_FromUInt64`
308+
* :c:func:`PyLong_ToInt32`
309+
* :c:func:`PyLong_ToUInt32`
310+
* :c:func:`PyLong_ToInt64`
311+
* :c:func:`PyLong_ToUInt64`
312+
313+
(Contributed by Victor Stinner in :gh:`120389`.)
314+
301315
Porting to Python 3.14
302316
----------------------
303317

Include/longobject.h

+12
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,18 @@ 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_FromInt32(int32_t value);
35+
PyAPI_FUNC(PyObject*) PyLong_FromUInt32(uint32_t value);
36+
PyAPI_FUNC(PyObject*) PyLong_FromInt64(int64_t value);
37+
PyAPI_FUNC(PyObject*) PyLong_FromUInt64(uint64_t value);
38+
39+
PyAPI_FUNC(int) PyLong_ToInt32(PyObject *obj, int32_t *value);
40+
PyAPI_FUNC(int) PyLong_ToUInt32(PyObject *obj, uint32_t *value);
41+
PyAPI_FUNC(int) PyLong_ToInt64(PyObject *obj, int64_t *value);
42+
PyAPI_FUNC(int) PyLong_ToUInt64(PyObject *obj, uint64_t *value);
43+
#endif
44+
3345
PyAPI_FUNC(PyObject *) PyLong_GetInfo(void);
3446

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

Lib/test/test_capi/test_long.py

+59-30
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,29 @@ def test_long_getsign(self):
737743

738744
# CRASHES getsign(NULL)
739745

746+
def test_long_asint32(self):
747+
# Test PyLong_ToInt32() and PyLong_FromInt32()
748+
to_int32 = _testlimitedcapi.pylong_toint32
749+
from _testcapi import INT32_MIN, INT32_MAX
750+
self.check_long_asint(to_int32, INT32_MIN, INT32_MAX)
751+
752+
def test_long_asuint32(self):
753+
# Test PyLong_ToUInt32() and PyLong_FromUInt32()
754+
to_uint32 = _testlimitedcapi.pylong_touint32
755+
from _testcapi import UINT32_MAX
756+
self.check_long_asunsignedint(to_uint32, UINT32_MAX)
757+
758+
def test_long_asint64(self):
759+
# Test PyLong_ToInt64() and PyLong_FromInt64()
760+
to_int64 = _testlimitedcapi.pylong_toint64
761+
from _testcapi import INT64_MIN, INT64_MAX
762+
self.check_long_asint(to_int64, INT64_MIN, INT64_MAX)
763+
764+
def test_long_asuint64(self):
765+
# Test PyLong_ToUInt64() and PyLong_FromUInt64()
766+
to_uint64 = _testlimitedcapi.pylong_touint64
767+
from _testcapi import UINT64_MAX
768+
self.check_long_asunsignedint(to_uint64, UINT64_MAX)
740769

741770
if __name__ == "__main__":
742771
unittest.main()

Lib/test/test_stable_abi_ctypes.py

+8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Add new functions to convert C ``<stdint.h>`` numbers from/to Python
2+
:class:`int`:
3+
4+
* :c:func:`PyLong_FromInt32`
5+
* :c:func:`PyLong_FromUInt32`
6+
* :c:func:`PyLong_FromInt64`
7+
* :c:func:`PyLong_FromUInt64`
8+
* :c:func:`PyLong_ToInt32`
9+
* :c:func:`PyLong_ToUInt32`
10+
* :c:func:`PyLong_ToInt64`
11+
* :c:func:`PyLong_ToUInt64`
12+
13+
Patch by Victor Stinner.

Misc/stable_abi.toml

+16
Original file line numberDiff line numberDiff line change
@@ -2510,3 +2510,19 @@
25102510

25112511
[function.Py_TYPE]
25122512
added = '3.14'
2513+
[function.PyLong_FromInt32]
2514+
added = '3.14'
2515+
[function.PyLong_FromUInt32]
2516+
added = '3.14'
2517+
[function.PyLong_ToInt32]
2518+
added = '3.14'
2519+
[function.PyLong_ToUInt32]
2520+
added = '3.14'
2521+
[function.PyLong_FromInt64]
2522+
added = '3.14'
2523+
[function.PyLong_FromUInt64]
2524+
added = '3.14'
2525+
[function.PyLong_ToInt64]
2526+
added = '3.14'
2527+
[function.PyLong_ToUInt64]
2528+
added = '3.14'

Modules/_testcapimodule.c

+6
Original file line numberDiff line numberDiff line change
@@ -4047,6 +4047,12 @@ PyInit__testcapi(void)
40474047

40484048
PyModule_AddIntConstant(m, "the_number_three", 3);
40494049
PyModule_AddIntMacro(m, Py_C_RECURSION_LIMIT);
4050+
PyModule_AddObject(m, "INT32_MIN", PyLong_FromInt32(INT32_MIN));
4051+
PyModule_AddObject(m, "INT32_MAX", PyLong_FromInt32(INT32_MAX));
4052+
PyModule_AddObject(m, "UINT32_MAX", PyLong_FromUInt32(UINT32_MAX));
4053+
PyModule_AddObject(m, "INT64_MIN", PyLong_FromInt64(INT64_MIN));
4054+
PyModule_AddObject(m, "INT64_MAX", PyLong_FromInt64(INT64_MAX));
4055+
PyModule_AddObject(m, "UINT64_MAX", PyLong_FromUInt64(UINT64_MAX));
40504056

40514057
if (PyModule_AddIntMacro(m, Py_single_input)) {
40524058
return NULL;

Modules/_testlimitedcapi/long.c

+52-2
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,52 @@ pylong_aspid(PyObject *module, PyObject *arg)
758758
}
759759

760760

761+
static PyObject *
762+
pylong_toint32(PyObject *module, PyObject *arg)
763+
{
764+
NULLABLE(arg);
765+
int32_t value;
766+
if (PyLong_ToInt32(arg, &value) < 0) {
767+
return NULL;
768+
}
769+
return PyLong_FromInt32(value);
770+
}
771+
772+
static PyObject *
773+
pylong_touint32(PyObject *module, PyObject *arg)
774+
{
775+
NULLABLE(arg);
776+
uint32_t value;
777+
if (PyLong_ToUInt32(arg, &value) < 0) {
778+
return NULL;
779+
}
780+
return PyLong_FromUInt32(value);
781+
}
782+
783+
784+
static PyObject *
785+
pylong_toint64(PyObject *module, PyObject *arg)
786+
{
787+
NULLABLE(arg);
788+
int64_t value;
789+
if (PyLong_ToInt64(arg, &value) < 0) {
790+
return NULL;
791+
}
792+
return PyLong_FromInt64(value);
793+
}
794+
795+
static PyObject *
796+
pylong_touint64(PyObject *module, PyObject *arg)
797+
{
798+
NULLABLE(arg);
799+
uint64_t value;
800+
if (PyLong_ToUInt64(arg, &value) < 0) {
801+
return NULL;
802+
}
803+
return PyLong_FromUInt64(value);
804+
}
805+
806+
761807
static PyMethodDef test_methods[] = {
762808
_TESTLIMITEDCAPI_TEST_LONG_AND_OVERFLOW_METHODDEF
763809
_TESTLIMITEDCAPI_TEST_LONG_API_METHODDEF
@@ -785,6 +831,10 @@ static PyMethodDef test_methods[] = {
785831
{"pylong_asdouble", pylong_asdouble, METH_O},
786832
{"pylong_asvoidptr", pylong_asvoidptr, METH_O},
787833
{"pylong_aspid", pylong_aspid, METH_O},
834+
{"pylong_toint32", pylong_toint32, METH_O},
835+
{"pylong_touint32", pylong_touint32, METH_O},
836+
{"pylong_toint64", pylong_toint64, METH_O},
837+
{"pylong_touint64", pylong_touint64, METH_O},
788838
{NULL},
789839
};
790840

0 commit comments

Comments
 (0)