Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-108444: Add PyLong_AsInt() public function #108445

Merged
merged 4 commits into from
Aug 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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:expr:`int` instead of a C :c:expr:`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:expr:`int` instead of a C :c:expr:`long`.
Previously, it was known as 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
30 changes: 30 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,36 @@ 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)

# use __index__(), not __int__()
class MyIndex:
def __index__(self):
return 10
def __int__(self):
return 22
self.assertEqual(PyLong_AsInt(MyIndex()), 10)

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

# invalid type
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add also tests for objects with __index__

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I added a test

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,4 @@
Add :c:func:`PyLong_AsInt` function: similar to :c:func:`PyLong_AsLong`, but
store the result in a C :c:expr:`int` instead of a C :c:expr:`long`.
Previously, it was known as the the private function :c:func:`!_PyLong_AsInt`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Previously, it was known as the the private function :c:func:`!_PyLong_AsInt`
Previously, it was known as the private function :c:func:`!_PyLong_AsInt`

(with an underscore prefix). Patch 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.