Skip to content

Commit 669c882

Browse files
authored
Add PyLong_FromUInt64() and PyLong_AsUInt64() (#114)
1 parent 38e2d32 commit 669c882

File tree

4 files changed

+168
-8
lines changed

4 files changed

+168
-8
lines changed

docs/api.rst

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,39 @@ Python 3.14
8989

9090
See `PyUnicodeWriter_Format() documentation <https://docs.python.org/dev/c-api/unicode.html#c.PyUnicodeWriter_Format>`__.
9191

92+
.. c:function:: int PyLong_AsInt32(PyObject *obj, int32_t *pvalue)
93+
94+
See `PyLong_AsInt32() documentation <https://docs.python.org/dev/c-api/long.html#c.PyLong_AsInt32>`__.
95+
96+
.. c:function:: int PyLong_AsInt64(PyObject *obj, int64_t *pvalue)
97+
98+
See `PyLong_AsInt64() documentation <https://docs.python.org/dev/c-api/long.html#c.PyLong_AsInt64>`__.
99+
100+
.. c:function:: int PyLong_AsUInt32(PyObject *obj, uint32_t *pvalue)
101+
102+
See `PyLong_AsUInt32() documentation <https://docs.python.org/dev/c-api/long.html#c.PyLong_AsUInt32>`__.
103+
104+
.. c:function:: int PyLong_AsUInt64(PyObject *obj, uint64_t *pvalue)
105+
106+
See `PyLong_AsUInt64() documentation <https://docs.python.org/dev/c-api/long.html#c.PyLong_AsUInt64>`__.
107+
108+
.. c:function:: PyObject* PyLong_FromInt32(int32_t value)
109+
110+
See `PyLong_FromInt32() documentation <https://docs.python.org/dev/c-api/long.html#c.PyLong_FromInt32>`__.
111+
112+
.. c:function:: PyObject* PyLong_FromInt64(int64_t value)
113+
114+
See `PyLong_FromInt64() documentation <https://docs.python.org/dev/c-api/long.html#c.PyLong_FromInt64>`__.
115+
116+
.. c:function:: PyObject* PyLong_FromUInt32(uint32_t value)
117+
118+
See `PyLong_FromUInt32() documentation <https://docs.python.org/dev/c-api/long.html#c.PyLong_FromUInt32>`__.
119+
120+
.. c:function:: PyObject* PyLong_FromUInt64(uint64_t value)
121+
122+
See `PyLong_FromUInt64() documentation <https://docs.python.org/dev/c-api/long.html#c.PyLong_FromUInt64>`__.
123+
124+
92125
Not supported:
93126

94127
* ``PyConfig_Get()``
@@ -108,14 +141,6 @@ Not supported:
108141
* ``PyInitConfig_SetInt()``
109142
* ``PyInitConfig_SetStr()``
110143
* ``PyInitConfig_SetStrList()``
111-
* ``PyLong_AsInt32()``
112-
* ``PyLong_AsInt64()``
113-
* ``PyLong_AsUInt32()``
114-
* ``PyLong_AsUInt64()``
115-
* ``PyLong_FromInt32()``
116-
* ``PyLong_FromInt64()``
117-
* ``PyLong_FromUInt32()``
118-
* ``PyLong_FromUInt64()``
119144
* ``PyType_GetBaseByToken()``
120145
* ``PyUnicodeWriter_DecodeUTF8Stateful()``
121146
* ``Py_InitializeFromInitConfig()``

docs/changelog.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@ Changelog
55

66
* ``PyBytes_Join()``
77
* ``PyIter_NextItem()``
8+
* ``PyLong_AsInt32()``
9+
* ``PyLong_AsInt64()``
10+
* ``PyLong_AsUInt32()``
11+
* ``PyLong_AsUInt64()``
12+
* ``PyLong_FromInt32()``
13+
* ``PyLong_FromInt64()``
14+
* ``PyLong_FromUInt32()``
15+
* ``PyLong_FromUInt64()``
816
* ``PyUnicode_Equal()``
917
* ``Py_HashBuffer()``
1018

pythoncapi_compat.h

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,13 @@ extern "C" {
4545
# define _PyObject_CAST(op) _Py_CAST(PyObject*, op)
4646
#endif
4747

48+
#ifndef Py_BUILD_ASSERT
49+
# define Py_BUILD_ASSERT(cond) \
50+
do { \
51+
(void)sizeof(char [1 - 2 * !(cond)]); \
52+
} while(0)
53+
#endif
54+
4855

4956
// bpo-42262 added Py_NewRef() to Python 3.10.0a3
5057
#if PY_VERSION_HEX < 0x030A00A3 && !defined(Py_NewRef)
@@ -1605,6 +1612,84 @@ static inline int PyIter_NextItem(PyObject *iter, PyObject **item)
16051612
#endif
16061613

16071614

1615+
#if PY_VERSION_HEX < 0x030E00A0
1616+
static inline PyObject* PyLong_FromInt32(int32_t value)
1617+
{
1618+
Py_BUILD_ASSERT(sizeof(long) >= 4);
1619+
return PyLong_FromLong(value);
1620+
}
1621+
1622+
static inline PyObject* PyLong_FromInt64(int64_t value)
1623+
{
1624+
Py_BUILD_ASSERT(sizeof(long long) >= 8);
1625+
return PyLong_FromLongLong(value);
1626+
}
1627+
1628+
static inline PyObject* PyLong_FromUInt32(uint32_t value)
1629+
{
1630+
Py_BUILD_ASSERT(sizeof(unsigned long) >= 4);
1631+
return PyLong_FromUnsignedLong(value);
1632+
}
1633+
1634+
static inline PyObject* PyLong_FromUInt64(uint64_t value)
1635+
{
1636+
Py_BUILD_ASSERT(sizeof(unsigned long long) >= 8);
1637+
return PyLong_FromUnsignedLongLong(value);
1638+
}
1639+
1640+
static inline int PyLong_AsInt32(PyObject *obj, int32_t *pvalue)
1641+
{
1642+
Py_BUILD_ASSERT(sizeof(int) == 4);
1643+
int value = PyLong_AsInt(obj);
1644+
if (value == -1 && PyErr_Occurred()) {
1645+
return -1;
1646+
}
1647+
*pvalue = (int32_t)value;
1648+
return 0;
1649+
}
1650+
1651+
static inline int PyLong_AsInt64(PyObject *obj, int64_t *pvalue)
1652+
{
1653+
Py_BUILD_ASSERT(sizeof(long long) == 8);
1654+
long long value = PyLong_AsLongLong(obj);
1655+
if (value == -1 && PyErr_Occurred()) {
1656+
return -1;
1657+
}
1658+
*pvalue = (int64_t)value;
1659+
return 0;
1660+
}
1661+
1662+
static inline int PyLong_AsUInt32(PyObject *obj, uint32_t *pvalue)
1663+
{
1664+
Py_BUILD_ASSERT(sizeof(long) >= 4);
1665+
unsigned long value = PyLong_AsUnsignedLong(obj);
1666+
if (value == (unsigned long)-1 && PyErr_Occurred()) {
1667+
return -1;
1668+
}
1669+
#if SIZEOF_LONG > 4
1670+
if ((unsigned long)UINT32_MAX < value) {
1671+
PyErr_SetString(PyExc_OverflowError,
1672+
"Python int too large to convert to C uint32_t");
1673+
return -1;
1674+
}
1675+
#endif
1676+
*pvalue = (uint32_t)value;
1677+
return 0;
1678+
}
1679+
1680+
static inline int PyLong_AsUInt64(PyObject *obj, uint64_t *pvalue)
1681+
{
1682+
Py_BUILD_ASSERT(sizeof(long long) == 8);
1683+
unsigned long long value = PyLong_AsUnsignedLongLong(obj);
1684+
if (value == (unsigned long long)-1 && PyErr_Occurred()) {
1685+
return -1;
1686+
}
1687+
*pvalue = (uint64_t)value;
1688+
return 0;
1689+
}
1690+
#endif
1691+
1692+
16081693
#ifdef __cplusplus
16091694
}
16101695
#endif

tests/test_pythoncapi_compat_cext.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1971,6 +1971,47 @@ test_iter(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
19711971
}
19721972

19731973

1974+
static PyObject *
1975+
test_long_stdint(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
1976+
{
1977+
PyObject *obj;
1978+
1979+
// Test PyLong_FromInt32() and PyLong_AsInt32()
1980+
obj = PyLong_FromInt32(INT32_C(-0x12345678));
1981+
assert(obj != NULL);
1982+
int32_t i32;
1983+
assert(PyLong_AsInt32(obj, &i32) == 0);
1984+
assert(i32 == INT32_C(-0x12345678));
1985+
Py_DECREF(obj);
1986+
1987+
// Test PyLong_FromUInt32() and PyLong_AsUInt32()
1988+
obj = PyLong_FromUInt32(UINT32_C(0xDEADBEEF));
1989+
assert(obj != NULL);
1990+
uint32_t u32;
1991+
assert(PyLong_AsUInt32(obj, &u32) == 0);
1992+
assert(u32 == UINT32_C(0xDEADBEEF));
1993+
Py_DECREF(obj);
1994+
1995+
// Test PyLong_FromInt64() and PyLong_AsInt64()
1996+
obj = PyLong_FromInt64(INT64_C(-0x12345678DEADBEEF));
1997+
assert(obj != NULL);
1998+
int64_t i64;
1999+
assert(PyLong_AsInt64(obj, &i64) == 0);
2000+
assert(i64 == INT64_C(-0x12345678DEADBEEF));
2001+
Py_DECREF(obj);
2002+
2003+
// Test PyLong_FromUInt64() and PyLong_AsUInt64()
2004+
obj = PyLong_FromUInt64(UINT64_C(0xDEADBEEF12345678));
2005+
assert(obj != NULL);
2006+
uint64_t u64;
2007+
assert(PyLong_AsUInt64(obj, &u64) == 0);
2008+
assert(u64 == UINT64_C(0xDEADBEEF12345678));
2009+
Py_DECREF(obj);
2010+
2011+
Py_RETURN_NONE;
2012+
}
2013+
2014+
19742015
static struct PyMethodDef methods[] = {
19752016
{"test_object", test_object, METH_NOARGS, _Py_NULL},
19762017
{"test_py_is", test_py_is, METH_NOARGS, _Py_NULL},
@@ -2016,6 +2057,7 @@ static struct PyMethodDef methods[] = {
20162057
#endif
20172058
{"test_bytes", test_bytes, METH_NOARGS, _Py_NULL},
20182059
{"test_iter", test_iter, METH_NOARGS, _Py_NULL},
2060+
{"test_long_stdint", test_long_stdint, METH_NOARGS, _Py_NULL},
20192061
{_Py_NULL, _Py_NULL, 0, _Py_NULL}
20202062
};
20212063

0 commit comments

Comments
 (0)