Skip to content

Commit 4c6dca8

Browse files
authored
gh-120389: Add PyLong_FromInt64() and PyLong_AsInt64() (#120390)
Add new functions to convert C <stdint.h> numbers from/to Python int: * PyLong_FromInt32() * PyLong_FromUInt32() * PyLong_FromInt64() * PyLong_FromUInt64() * PyLong_AsInt32() * PyLong_AsUInt32() * PyLong_AsInt64() * PyLong_AsUInt64()
1 parent 1a0b828 commit 4c6dca8

File tree

13 files changed

+286
-2
lines changed

13 files changed

+286
-2
lines changed

Doc/c-api/long.rst

+57
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,32 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate.
6969
on failure.
7070
7171
72+
.. c:function:: PyObject* PyLong_FromInt32(int32_t value)
73+
PyObject* PyLong_FromInt64(int64_t value)
74+
75+
Return a new :c:type:`PyLongObject` object from a signed C
76+
:c:expr:`int32_t` or :c:expr:`int64_t`, or ``NULL``
77+
with an exception set on failure.
78+
79+
.. versionadded:: 3.14
80+
81+
7282
.. c:function:: PyObject* PyLong_FromUnsignedLongLong(unsigned long long v)
7383
7484
Return a new :c:type:`PyLongObject` object from a C :c:expr:`unsigned long long`,
7585
or ``NULL`` on failure.
7686
7787
88+
.. c:function:: PyObject* PyLong_FromUInt32(uint32_t value)
89+
PyObject* PyLong_FromUInt64(uint64_t value)
90+
91+
Return a new :c:type:`PyLongObject` object from an unsigned C
92+
:c:expr:`uint32_t` or :c:expr:`uint64_t`, or ``NULL``
93+
with an exception set on failure.
94+
95+
.. versionadded:: 3.14
96+
97+
7898
.. c:function:: PyObject* PyLong_FromDouble(double v)
7999
80100
Return a new :c:type:`PyLongObject` object from the integer part of *v*, or
@@ -337,6 +357,43 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate.
337357
This function will no longer use :meth:`~object.__int__`.
338358
339359
360+
.. c:function:: int PyLong_AsInt32(PyObject *obj, int32_t *value)
361+
int PyLong_AsInt64(PyObject *obj, int64_t *value)
362+
363+
Set *\*value* to a signed C :c:expr:`int32_t` or :c:expr:`int64_t`
364+
representation of *obj*.
365+
366+
If the *obj* value is out of range, raise an :exc:`OverflowError`.
367+
368+
Set *\*value* and return ``0`` on success.
369+
Set an exception and return ``-1`` on error.
370+
371+
*value* must not be ``NULL``.
372+
373+
.. versionadded:: 3.14
374+
375+
376+
.. c:function:: int PyLong_AsUInt32(PyObject *obj, uint32_t *value)
377+
int PyLong_AsUInt64(PyObject *obj, uint64_t *value)
378+
379+
Set *\*value* to an unsigned C :c:expr:`uint32_t` or :c:expr:`uint64_t`
380+
representation of *obj*.
381+
382+
If *obj* is not an instance of :c:type:`PyLongObject`, first call its
383+
:meth:`~object.__index__` method (if present) to convert it to a
384+
:c:type:`PyLongObject`.
385+
386+
* If *obj* is negative, raise a :exc:`ValueError`.
387+
* If the *obj* value is out of range, raise an :exc:`OverflowError`.
388+
389+
Set *\*value* and return ``0`` on success.
390+
Set an exception and return ``-1`` on error.
391+
392+
*value* must not be ``NULL``.
393+
394+
.. versionadded:: 3.14
395+
396+
340397
.. c:function:: double PyLong_AsDouble(PyObject *pylong)
341398
342399
Return a C :c:expr:`double` representation of *pylong*. *pylong* must be

Doc/conf.py

+1
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@
141141
('c:type', 'size_t'),
142142
('c:type', 'ssize_t'),
143143
('c:type', 'time_t'),
144+
('c:type', 'uint32_t'),
144145
('c:type', 'uint64_t'),
145146
('c:type', 'uintmax_t'),
146147
('c:type', 'uintptr_t'),

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
@@ -471,6 +471,20 @@ New Features
471471
an interned string and deallocate it during module shutdown.
472472
(Contribued by Eddie Elizondo in :gh:`113601`.)
473473

474+
* Add new functions to convert C ``<stdint.h>`` numbers from/to Python
475+
:class:`int`:
476+
477+
* :c:func:`PyLong_FromInt32`
478+
* :c:func:`PyLong_FromInt64`
479+
* :c:func:`PyLong_FromUInt32`
480+
* :c:func:`PyLong_FromUInt64`
481+
* :c:func:`PyLong_AsInt32`
482+
* :c:func:`PyLong_AsInt64`
483+
* :c:func:`PyLong_AsUInt32`
484+
* :c:func:`PyLong_AsUInt64`
485+
486+
(Contributed by Victor Stinner in :gh:`120389`.)
487+
474488
Porting to Python 3.14
475489
----------------------
476490

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_AsInt32(PyObject *obj, int32_t *value);
40+
PyAPI_FUNC(int) PyLong_AsUInt32(PyObject *obj, uint32_t *value);
41+
PyAPI_FUNC(int) PyLong_AsInt64(PyObject *obj, int64_t *value);
42+
PyAPI_FUNC(int) PyLong_AsUInt64(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

+25
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,31 @@ def test_long_getsign(self):
631631

632632
# CRASHES getsign(NULL)
633633

634+
def test_long_asint32(self):
635+
# Test PyLong_AsInt32() and PyLong_FromInt32()
636+
to_int32 = _testlimitedcapi.pylong_asint32
637+
from _testcapi import INT32_MIN, INT32_MAX
638+
self.check_long_asint(to_int32, INT32_MIN, INT32_MAX)
639+
640+
def test_long_asint64(self):
641+
# Test PyLong_AsInt64() and PyLong_FromInt64()
642+
as_int64 = _testlimitedcapi.pylong_asint64
643+
from _testcapi import INT64_MIN, INT64_MAX
644+
self.check_long_asint(as_int64, INT64_MIN, INT64_MAX)
645+
646+
def test_long_asuint32(self):
647+
# Test PyLong_AsUInt32() and PyLong_FromUInt32()
648+
as_uint32 = _testlimitedcapi.pylong_asuint32
649+
from _testcapi import UINT32_MAX
650+
self.check_long_asint(as_uint32, 0, UINT32_MAX,
651+
negative_value_error=ValueError)
652+
653+
def test_long_asuint64(self):
654+
# Test PyLong_AsUInt64() and PyLong_FromUInt64()
655+
as_uint64 = _testlimitedcapi.pylong_asuint64
656+
from _testcapi import UINT64_MAX
657+
self.check_long_asint(as_uint64, 0, UINT64_MAX,
658+
negative_value_error=ValueError)
634659

635660
if __name__ == "__main__":
636661
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_AsInt32`
9+
* :c:func:`PyLong_AsUInt32`
10+
* :c:func:`PyLong_AsInt64`
11+
* :c:func:`PyLong_AsUInt64`
12+
13+
Patch by Victor Stinner.

Misc/stable_abi.toml

+16
Original file line numberDiff line numberDiff line change
@@ -2510,3 +2510,19 @@
25102510
added = '3.14'
25112511
[function.PyIter_NextItem]
25122512
added = '3.14'
2513+
[function.PyLong_FromInt32]
2514+
added = '3.14'
2515+
[function.PyLong_FromUInt32]
2516+
added = '3.14'
2517+
[function.PyLong_AsInt32]
2518+
added = '3.14'
2519+
[function.PyLong_AsUInt32]
2520+
added = '3.14'
2521+
[function.PyLong_FromInt64]
2522+
added = '3.14'
2523+
[function.PyLong_FromUInt64]
2524+
added = '3.14'
2525+
[function.PyLong_AsInt64]
2526+
added = '3.14'
2527+
[function.PyLong_AsUInt64]
2528+
added = '3.14'

Modules/_testcapimodule.c

+6
Original file line numberDiff line numberDiff line change
@@ -4046,6 +4046,12 @@ PyInit__testcapi(void)
40464046

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

40504056
if (PyModule_AddIntMacro(m, Py_single_input)) {
40514057
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_asint32(PyObject *module, PyObject *arg)
763+
{
764+
NULLABLE(arg);
765+
int32_t value;
766+
if (PyLong_AsInt32(arg, &value) < 0) {
767+
return NULL;
768+
}
769+
return PyLong_FromInt32(value);
770+
}
771+
772+
static PyObject *
773+
pylong_asuint32(PyObject *module, PyObject *arg)
774+
{
775+
NULLABLE(arg);
776+
uint32_t value;
777+
if (PyLong_AsUInt32(arg, &value) < 0) {
778+
return NULL;
779+
}
780+
return PyLong_FromUInt32(value);
781+
}
782+
783+
784+
static PyObject *
785+
pylong_asint64(PyObject *module, PyObject *arg)
786+
{
787+
NULLABLE(arg);
788+
int64_t value;
789+
if (PyLong_AsInt64(arg, &value) < 0) {
790+
return NULL;
791+
}
792+
return PyLong_FromInt64(value);
793+
}
794+
795+
static PyObject *
796+
pylong_asuint64(PyObject *module, PyObject *arg)
797+
{
798+
NULLABLE(arg);
799+
uint64_t value;
800+
if (PyLong_AsUInt64(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_asint32", pylong_asint32, METH_O},
835+
{"pylong_asuint32", pylong_asuint32, METH_O},
836+
{"pylong_asint64", pylong_asint64, METH_O},
837+
{"pylong_asuint64", pylong_asuint64, METH_O},
788838
{NULL},
789839
};
790840

0 commit comments

Comments
 (0)