Skip to content

Commit

Permalink
pythongh-108444: Add PyLong_AsInt() public function
Browse files Browse the repository at this point in the history
* Rename _PyLong_AsInt() to PyLong_AsInt().
* Add documentation.
* Add test.
* For now, keep _PyLong_AsInt() as an alias to PyLong_AsInt().
  • Loading branch information
vstinner committed Aug 24, 2023
1 parent 480a337 commit c8c7686
Show file tree
Hide file tree
Showing 13 changed files with 89 additions and 3 deletions.
8 changes: 8 additions & 0 deletions Doc/c-api/long.rst
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,14 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate.
This function will no longer use :meth:`~object.__int__`.
.. c:function:: int PyLong_AsInt(PyObject *obj)
Similar to :c:func:`PyLong_AsLong`, but store the result in a C
:c:type:`int` instead of a C :c:type:`long`.
.. versionadded:: 3.13
.. c:function:: long PyLong_AsLongAndOverflow(PyObject *obj, int *overflow)
Return a C :c:expr:`long` representation of *obj*. If *obj* is not an
Expand Down
1 change: 1 addition & 0 deletions Doc/data/stable_abi.dat

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions Doc/whatsnew/3.13.rst
Original file line number Diff line number Diff line change
Expand Up @@ -871,6 +871,12 @@ New Features
:term:`shutting down <interpreter shutdown>`.
(Contributed by Victor Stinner in :gh:`108014`.)

* Add :c:func:`PyLong_AsInt` function: similar to :c:func:`PyLong_AsLong`, but
store the result in a C :c:type:`int` instead of a C :c:type:`long`.
Previously, it was known as the the private function :c:func:`!_PyLong_AsInt`
(with an underscore prefix).
(Contributed by Victor Stinner in :gh:`108014`.)

Porting to Python 3.13
----------------------

Expand Down
3 changes: 2 additions & 1 deletion Include/cpython/longobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
# error "this header file must not be included directly"
#endif

PyAPI_FUNC(int) _PyLong_AsInt(PyObject *);
// Alias for backport compatibility
#define _PyLong_AsInt PyLong_AsInt

PyAPI_FUNC(int) _PyLong_UnsignedShort_Converter(PyObject *, void *);
PyAPI_FUNC(int) _PyLong_UnsignedInt_Converter(PyObject *, void *);
Expand Down
6 changes: 6 additions & 0 deletions Include/longobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,18 @@ PyAPI_FUNC(PyObject *) PyLong_FromUnsignedLong(unsigned long);
PyAPI_FUNC(PyObject *) PyLong_FromSize_t(size_t);
PyAPI_FUNC(PyObject *) PyLong_FromSsize_t(Py_ssize_t);
PyAPI_FUNC(PyObject *) PyLong_FromDouble(double);

PyAPI_FUNC(long) PyLong_AsLong(PyObject *);
PyAPI_FUNC(long) PyLong_AsLongAndOverflow(PyObject *, int *);
PyAPI_FUNC(Py_ssize_t) PyLong_AsSsize_t(PyObject *);
PyAPI_FUNC(size_t) PyLong_AsSize_t(PyObject *);
PyAPI_FUNC(unsigned long) PyLong_AsUnsignedLong(PyObject *);
PyAPI_FUNC(unsigned long) PyLong_AsUnsignedLongMask(PyObject *);

#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030d0000
PyAPI_FUNC(int) PyLong_AsInt(PyObject *);
#endif

PyAPI_FUNC(PyObject *) PyLong_GetInfo(void);

/* It may be useful in the future. I've added it in the PyInt -> PyLong
Expand Down
22 changes: 22 additions & 0 deletions Lib/test/test_capi/test_long.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,28 @@ def test_compact_known(self):
self.assertEqual(_testcapi.call_long_compact_api(sys.maxsize),
(False, -1))

def test_long_asint(self):
PyLong_AsInt = _testcapi.PyLong_AsInt
INT_MIN = _testcapi.INT_MIN
INT_MAX = _testcapi.INT_MAX

# round trip (object -> int -> object)
for value in (INT_MIN, INT_MAX, -1, 0, 1, 123):
with self.subTest(value=value):
self.assertEqual(PyLong_AsInt(value), value)

# bound checking
with self.assertRaises(OverflowError):
PyLong_AsInt(INT_MIN - 1)
with self.assertRaises(OverflowError):
PyLong_AsInt(INT_MAX + 1)

# invalid type
for value in (1.0, b'2', '3'):
with self.subTest(value=value):
with self.assertRaises(TypeError):
PyLong_AsInt(value)


if __name__ == "__main__":
unittest.main()
1 change: 1 addition & 0 deletions Lib/test/test_stable_abi_ctypes.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Add :c:func:`PyLong_AsInt` function: similar to :c:func:`PyLong_AsLong`, but
store the result in a C :c:type:`int` instead of a C :c:type:`long`.
Previously, it was known as the the private function
:c:func:`!_PyLong_AsInt` (with an underscore prefix). Patch by by Victor
Stinner.
2 changes: 2 additions & 0 deletions Misc/stable_abi.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2450,3 +2450,5 @@
added = '3.13'
[function.PyDict_GetItemStringRef]
added = '3.13'
[function.PyLong_AsInt]
added = '3.13'
10 changes: 9 additions & 1 deletion Modules/_testcapi/clinic/long.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions Modules/_testcapi/long.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ raise_test_long_error(const char* msg)
return raiseTestError("test_long_api", msg);
}

// Test PyLong_FromLong()/PyLong_AsLong()
// and PyLong_FromUnsignedLong()/PyLong_AsUnsignedLong().

#define TESTNAME test_long_api_inner
#define TYPENAME long
#define F_S_TO_PY PyLong_FromLong
Expand Down Expand Up @@ -64,6 +67,9 @@ _testcapi_test_long_api_impl(PyObject *module)
#undef F_U_TO_PY
#undef F_PY_TO_U

// Test PyLong_FromLongLong()/PyLong_AsLongLong()
// and PyLong_FromUnsignedLongLong()/PyLong_AsUnsignedLongLong().

static PyObject *
raise_test_longlong_error(const char* msg)
{
Expand Down Expand Up @@ -595,6 +601,24 @@ _testcapi_call_long_compact_api(PyObject *module, PyObject *arg)
return Py_BuildValue("in", is_compact, value);
}

/*[clinic input]
_testcapi.PyLong_AsInt
arg: object
/
[clinic start generated code]*/

static PyObject *
_testcapi_PyLong_AsInt(PyObject *module, PyObject *arg)
/*[clinic end generated code: output=0df9f19de5fa575b input=9561b97105493a67]*/
{
assert(!PyErr_Occurred());
int value = PyLong_AsInt(arg);
if (value == -1 && PyErr_Occurred()) {
return NULL;
}
return PyLong_FromLong(value);
}

static PyMethodDef test_methods[] = {
_TESTCAPI_TEST_LONG_AND_OVERFLOW_METHODDEF
_TESTCAPI_TEST_LONG_API_METHODDEF
Expand All @@ -605,6 +629,7 @@ static PyMethodDef test_methods[] = {
_TESTCAPI_TEST_LONG_NUMBITS_METHODDEF
_TESTCAPI_TEST_LONGLONG_API_METHODDEF
_TESTCAPI_CALL_LONG_COMPACT_API_METHODDEF
_TESTCAPI_PYLONG_ASINT_METHODDEF
{NULL},
};

Expand Down
2 changes: 1 addition & 1 deletion Objects/longobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -549,7 +549,7 @@ PyLong_AsLong(PyObject *obj)
method. Return -1 and set an error if overflow occurs. */

int
_PyLong_AsInt(PyObject *obj)
PyLong_AsInt(PyObject *obj)
{
int overflow;
long result = PyLong_AsLongAndOverflow(obj, &overflow);
Expand Down
1 change: 1 addition & 0 deletions PC/python3dll.c

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit c8c7686

Please sign in to comment.