Skip to content

Commit 3a79210

Browse files
committed
gh-108444: Add PyLong_AsInt() public function
* Rename _PyLong_AsInt() to PyLong_AsInt(). * Add documentation. * Add test. * For now, keep _PyLong_AsInt() as an alias to PyLong_AsInt().
1 parent 480a337 commit 3a79210

File tree

13 files changed

+88
-3
lines changed

13 files changed

+88
-3
lines changed

Doc/c-api/long.rst

+8
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,14 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate.
136136
This function will no longer use :meth:`~object.__int__`.
137137
138138
139+
.. c:function:: int PyLong_AsInt(PyObject *obj)
140+
141+
Similar to :c:func:`PyLong_AsLong`, but store the result in a C
142+
:c:expr:`int` instead of a C :c:expr:`long`.
143+
144+
.. versionadded:: 3.13
145+
146+
139147
.. c:function:: long PyLong_AsLongAndOverflow(PyObject *obj, int *overflow)
140148
141149
Return a C :c:expr:`long` representation of *obj*. If *obj* is not an

Doc/data/stable_abi.dat

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

Doc/whatsnew/3.13.rst

+6
Original file line numberDiff line numberDiff line change
@@ -871,6 +871,12 @@ New Features
871871
:term:`shutting down <interpreter shutdown>`.
872872
(Contributed by Victor Stinner in :gh:`108014`.)
873873

874+
* Add :c:func:`PyLong_AsInt` function: similar to :c:func:`PyLong_AsLong`, but
875+
store the result in a C :c:expr:`int` instead of a C :c:expr:`long`.
876+
Previously, it was known as the the private function :c:func:`!_PyLong_AsInt`
877+
(with an underscore prefix).
878+
(Contributed by Victor Stinner in :gh:`108014`.)
879+
874880
Porting to Python 3.13
875881
----------------------
876882

Include/cpython/longobject.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
# error "this header file must not be included directly"
33
#endif
44

5-
PyAPI_FUNC(int) _PyLong_AsInt(PyObject *);
5+
// Alias for backport compatibility
6+
#define _PyLong_AsInt PyLong_AsInt
67

78
PyAPI_FUNC(int) _PyLong_UnsignedShort_Converter(PyObject *, void *);
89
PyAPI_FUNC(int) _PyLong_UnsignedInt_Converter(PyObject *, void *);

Include/longobject.h

+6
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,18 @@ PyAPI_FUNC(PyObject *) PyLong_FromUnsignedLong(unsigned long);
1818
PyAPI_FUNC(PyObject *) PyLong_FromSize_t(size_t);
1919
PyAPI_FUNC(PyObject *) PyLong_FromSsize_t(Py_ssize_t);
2020
PyAPI_FUNC(PyObject *) PyLong_FromDouble(double);
21+
2122
PyAPI_FUNC(long) PyLong_AsLong(PyObject *);
2223
PyAPI_FUNC(long) PyLong_AsLongAndOverflow(PyObject *, int *);
2324
PyAPI_FUNC(Py_ssize_t) PyLong_AsSsize_t(PyObject *);
2425
PyAPI_FUNC(size_t) PyLong_AsSize_t(PyObject *);
2526
PyAPI_FUNC(unsigned long) PyLong_AsUnsignedLong(PyObject *);
2627
PyAPI_FUNC(unsigned long) PyLong_AsUnsignedLongMask(PyObject *);
28+
29+
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030d0000
30+
PyAPI_FUNC(int) PyLong_AsInt(PyObject *);
31+
#endif
32+
2733
PyAPI_FUNC(PyObject *) PyLong_GetInfo(void);
2834

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

Lib/test/test_capi/test_long.py

+22
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,28 @@ def test_compact_known(self):
3434
self.assertEqual(_testcapi.call_long_compact_api(sys.maxsize),
3535
(False, -1))
3636

37+
def test_long_asint(self):
38+
PyLong_AsInt = _testcapi.PyLong_AsInt
39+
INT_MIN = _testcapi.INT_MIN
40+
INT_MAX = _testcapi.INT_MAX
41+
42+
# round trip (object -> int -> object)
43+
for value in (INT_MIN, INT_MAX, -1, 0, 1, 123):
44+
with self.subTest(value=value):
45+
self.assertEqual(PyLong_AsInt(value), value)
46+
47+
# bound checking
48+
with self.assertRaises(OverflowError):
49+
PyLong_AsInt(INT_MIN - 1)
50+
with self.assertRaises(OverflowError):
51+
PyLong_AsInt(INT_MAX + 1)
52+
53+
# invalid type
54+
for value in (1.0, b'2', '3'):
55+
with self.subTest(value=value):
56+
with self.assertRaises(TypeError):
57+
PyLong_AsInt(value)
58+
3759

3860
if __name__ == "__main__":
3961
unittest.main()

Lib/test/test_stable_abi_ctypes.py

+1
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,4 @@
1+
Add :c:func:`PyLong_AsInt` function: similar to :c:func:`PyLong_AsLong`, but
2+
store the result in a C :c:expr:`int` instead of a C :c:expr:`long`.
3+
Previously, it was known as the the private function :c:func:`!_PyLong_AsInt`
4+
(with an underscore prefix). Patch by by Victor Stinner.

Misc/stable_abi.toml

+2
Original file line numberDiff line numberDiff line change
@@ -2450,3 +2450,5 @@
24502450
added = '3.13'
24512451
[function.PyDict_GetItemStringRef]
24522452
added = '3.13'
2453+
[function.PyLong_AsInt]
2454+
added = '3.13'

Modules/_testcapi/clinic/long.c.h

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

Modules/_testcapi/long.c

+25
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ raise_test_long_error(const char* msg)
3737
return raiseTestError("test_long_api", msg);
3838
}
3939

40+
// Test PyLong_FromLong()/PyLong_AsLong()
41+
// and PyLong_FromUnsignedLong()/PyLong_AsUnsignedLong().
42+
4043
#define TESTNAME test_long_api_inner
4144
#define TYPENAME long
4245
#define F_S_TO_PY PyLong_FromLong
@@ -64,6 +67,9 @@ _testcapi_test_long_api_impl(PyObject *module)
6467
#undef F_U_TO_PY
6568
#undef F_PY_TO_U
6669

70+
// Test PyLong_FromLongLong()/PyLong_AsLongLong()
71+
// and PyLong_FromUnsignedLongLong()/PyLong_AsUnsignedLongLong().
72+
6773
static PyObject *
6874
raise_test_longlong_error(const char* msg)
6975
{
@@ -595,6 +601,24 @@ _testcapi_call_long_compact_api(PyObject *module, PyObject *arg)
595601
return Py_BuildValue("in", is_compact, value);
596602
}
597603

604+
/*[clinic input]
605+
_testcapi.PyLong_AsInt
606+
arg: object
607+
/
608+
[clinic start generated code]*/
609+
610+
static PyObject *
611+
_testcapi_PyLong_AsInt(PyObject *module, PyObject *arg)
612+
/*[clinic end generated code: output=0df9f19de5fa575b input=9561b97105493a67]*/
613+
{
614+
assert(!PyErr_Occurred());
615+
int value = PyLong_AsInt(arg);
616+
if (value == -1 && PyErr_Occurred()) {
617+
return NULL;
618+
}
619+
return PyLong_FromLong(value);
620+
}
621+
598622
static PyMethodDef test_methods[] = {
599623
_TESTCAPI_TEST_LONG_AND_OVERFLOW_METHODDEF
600624
_TESTCAPI_TEST_LONG_API_METHODDEF
@@ -605,6 +629,7 @@ static PyMethodDef test_methods[] = {
605629
_TESTCAPI_TEST_LONG_NUMBITS_METHODDEF
606630
_TESTCAPI_TEST_LONGLONG_API_METHODDEF
607631
_TESTCAPI_CALL_LONG_COMPACT_API_METHODDEF
632+
_TESTCAPI_PYLONG_ASINT_METHODDEF
608633
{NULL},
609634
};
610635

Objects/longobject.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -549,7 +549,7 @@ PyLong_AsLong(PyObject *obj)
549549
method. Return -1 and set an error if overflow occurs. */
550550

551551
int
552-
_PyLong_AsInt(PyObject *obj)
552+
PyLong_AsInt(PyObject *obj)
553553
{
554554
int overflow;
555555
long result = PyLong_AsLongAndOverflow(obj, &overflow);

PC/python3dll.c

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

0 commit comments

Comments
 (0)