Skip to content
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
6 changes: 6 additions & 0 deletions Doc/c-api/arg.rst
Original file line number Diff line number Diff line change
Expand Up @@ -274,13 +274,19 @@ small to receive the value.
Convert a Python integer to a C :c:expr:`unsigned long` without
overflow checking.

.. versionchanged:: next
Use :meth:`~object.__index__` if available.

``L`` (:class:`int`) [long long]
Convert a Python integer to a C :c:expr:`long long`.

``K`` (:class:`int`) [unsigned long long]
Convert a Python integer to a C :c:expr:`unsigned long long`
without overflow checking.

.. versionchanged:: next
Use :meth:`~object.__index__` if available.

``n`` (:class:`int`) [:c:type:`Py_ssize_t`]
Convert a Python integer to a C :c:type:`Py_ssize_t`.

Expand Down
5 changes: 5 additions & 0 deletions Doc/whatsnew/3.14.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2073,6 +2073,11 @@ New features
Adding ``?`` after any format unit makes ``None`` be accepted as a value.
(Contributed by Serhiy Storchaka in :gh:`112068`.)

* The ``k`` and ``K`` formats in :c:func:`PyArg_ParseTuple` and
similar functions now use :meth:`~object.__index__` if available,
like all other integer formats.
(Contributed by Serhiy Storchaka in :gh:`112068`.)

* Add macros :c:func:`Py_PACK_VERSION` and :c:func:`Py_PACK_FULL_VERSION` for
bit-packing Python version numbers.
(Contributed by Petr Viktorin in :gh:`128629`.)
Expand Down
8 changes: 4 additions & 4 deletions Lib/test/clinic.test.c
Original file line number Diff line number Diff line change
Expand Up @@ -1410,7 +1410,7 @@ test_unsigned_long_converter(PyObject *module, PyObject *const *args, Py_ssize_t
if (nargs < 3) {
goto skip_optional;
}
if (!PyLong_Check(args[2])) {
if (!PyIndex_Check(args[2])) {
_PyArg_BadArgument("test_unsigned_long_converter", "argument 3", "int", args[2]);
goto exit;
}
Expand All @@ -1425,7 +1425,7 @@ test_unsigned_long_converter(PyObject *module, PyObject *const *args, Py_ssize_t
static PyObject *
test_unsigned_long_converter_impl(PyObject *module, unsigned long a,
unsigned long b, unsigned long c)
/*[clinic end generated code: output=540bb0ba2894e1fe input=f450d94cae1ef73b]*/
/*[clinic end generated code: output=d74eed227d77a31b input=f450d94cae1ef73b]*/


/*[clinic input]
Expand Down Expand Up @@ -1525,7 +1525,7 @@ test_unsigned_long_long_converter(PyObject *module, PyObject *const *args, Py_ss
if (nargs < 3) {
goto skip_optional;
}
if (!PyLong_Check(args[2])) {
if (!PyIndex_Check(args[2])) {
_PyArg_BadArgument("test_unsigned_long_long_converter", "argument 3", "int", args[2]);
goto exit;
}
Expand All @@ -1542,7 +1542,7 @@ test_unsigned_long_long_converter_impl(PyObject *module,
unsigned long long a,
unsigned long long b,
unsigned long long c)
/*[clinic end generated code: output=3d69994f618b46bb input=a15115dc41866ff4]*/
/*[clinic end generated code: output=5ca4e4dfb3db644b input=a15115dc41866ff4]*/


/*[clinic input]
Expand Down
12 changes: 7 additions & 5 deletions Lib/test/test_capi/test_getargs.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,12 +267,12 @@ def test_I(self):
def test_k(self):
from _testcapi import getargs_k
# k returns 'unsigned long', no range checking
# it does not accept float, or instances with __int__
self.assertRaises(TypeError, getargs_k, 3.14)
self.assertRaises(TypeError, getargs_k, Index())
self.assertEqual(99, getargs_k(Index()))
self.assertEqual(0, getargs_k(IndexIntSubclass()))
self.assertRaises(TypeError, getargs_k, BadIndex())
self.assertRaises(TypeError, getargs_k, BadIndex2())
with self.assertWarns(DeprecationWarning):
self.assertEqual(1, getargs_k(BadIndex2()))
self.assertEqual(0, getargs_k(BadIndex3()))
self.assertRaises(TypeError, getargs_k, Int())
self.assertEqual(0, getargs_k(IntSubclass()))
Expand Down Expand Up @@ -419,10 +419,11 @@ def test_K(self):
from _testcapi import getargs_K
# K return 'unsigned long long', no range checking
self.assertRaises(TypeError, getargs_K, 3.14)
self.assertRaises(TypeError, getargs_K, Index())
self.assertEqual(99, getargs_K(Index()))
self.assertEqual(0, getargs_K(IndexIntSubclass()))
self.assertRaises(TypeError, getargs_K, BadIndex())
self.assertRaises(TypeError, getargs_K, BadIndex2())
with self.assertWarns(DeprecationWarning):
self.assertEqual(1, getargs_K(BadIndex2()))
self.assertEqual(0, getargs_K(BadIndex3()))
self.assertRaises(TypeError, getargs_K, Int())
self.assertEqual(0, getargs_K(IntSubclass()))
Expand All @@ -432,6 +433,7 @@ def test_K(self):

self.assertEqual(ULLONG_MAX, getargs_K(ULLONG_MAX))
self.assertEqual(0, getargs_K(0))
self.assertEqual(ULLONG_MAX, getargs_K(ULLONG_MAX))
self.assertEqual(0, getargs_K(ULLONG_MAX+1))

self.assertEqual(42, getargs_K(42))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
The ``k`` and ``K`` formats in :c:func:`PyArg_Parse` now support the
:meth:`~object.__index__` special method, like all other integer formats.
6 changes: 3 additions & 3 deletions Modules/clinic/_cursesmodule.c.h

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

6 changes: 3 additions & 3 deletions Modules/clinic/_testclinic.c.h

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

4 changes: 2 additions & 2 deletions Modules/clinic/fcntlmodule.c.h

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

6 changes: 3 additions & 3 deletions Modules/clinic/posixmodule.c.h

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

4 changes: 2 additions & 2 deletions Modules/clinic/signalmodule.c.h

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

18 changes: 12 additions & 6 deletions Python/getargs.c
Original file line number Diff line number Diff line change
Expand Up @@ -839,10 +839,13 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
unsigned long *p = va_arg(*p_va, unsigned long *);
HANDLE_NULLABLE;
unsigned long ival;
if (PyLong_Check(arg))
ival = PyLong_AsUnsignedLongMask(arg);
else
if (!PyIndex_Check(arg)) {
return converterr(nullable, "int", arg, msgbuf, bufsize);
}
ival = PyLong_AsUnsignedLongMask(arg);
if (ival == (unsigned long)(long)-1 && PyErr_Occurred()) {
RETURN_ERR_OCCURRED;
}
*p = ival;
break;
}
Expand All @@ -862,10 +865,13 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
unsigned long long *p = va_arg(*p_va, unsigned long long *);
HANDLE_NULLABLE;
unsigned long long ival;
if (PyLong_Check(arg))
ival = PyLong_AsUnsignedLongLongMask(arg);
else
if (!PyIndex_Check(arg)) {
return converterr(nullable, "int", arg, msgbuf, bufsize);
}
ival = PyLong_AsUnsignedLongLongMask(arg);
if (ival == (unsigned long long)(long long)-1 && PyErr_Occurred()) {
RETURN_ERR_OCCURRED;
}
*p = ival;
break;
}
Expand Down
4 changes: 2 additions & 2 deletions Tools/clinic/libclinic/converters.py
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ def use_converter(self) -> None:
def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None:
if self.format_unit == 'k':
return self.format_code("""
if (!PyLong_Check({argname})) {{{{
if (!PyIndex_Check({argname})) {{{{
{bad_argument}
goto exit;
}}}}
Expand Down Expand Up @@ -444,7 +444,7 @@ def use_converter(self) -> None:
def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None:
if self.format_unit == 'K':
return self.format_code("""
if (!PyLong_Check({argname})) {{{{
if (!PyIndex_Check({argname})) {{{{
{bad_argument}
goto exit;
}}}}
Expand Down
Loading