Skip to content

Commit 632524a

Browse files
gh-132987: Support __index__() for "k" and "K" formats in PyArg_Parse (GH-132988)
1 parent e714ead commit 632524a

File tree

12 files changed

+51
-30
lines changed

12 files changed

+51
-30
lines changed

Doc/c-api/arg.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,13 +274,19 @@ small to receive the value.
274274
Convert a Python integer to a C :c:expr:`unsigned long` without
275275
overflow checking.
276276

277+
.. versionchanged:: next
278+
Use :meth:`~object.__index__` if available.
279+
277280
``L`` (:class:`int`) [long long]
278281
Convert a Python integer to a C :c:expr:`long long`.
279282

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

287+
.. versionchanged:: next
288+
Use :meth:`~object.__index__` if available.
289+
284290
``n`` (:class:`int`) [:c:type:`Py_ssize_t`]
285291
Convert a Python integer to a C :c:type:`Py_ssize_t`.
286292

Doc/whatsnew/3.14.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2073,6 +2073,11 @@ New features
20732073
Adding ``?`` after any format unit makes ``None`` be accepted as a value.
20742074
(Contributed by Serhiy Storchaka in :gh:`112068`.)
20752075

2076+
* The ``k`` and ``K`` formats in :c:func:`PyArg_ParseTuple` and
2077+
similar functions now use :meth:`~object.__index__` if available,
2078+
like all other integer formats.
2079+
(Contributed by Serhiy Storchaka in :gh:`112068`.)
2080+
20762081
* Add macros :c:func:`Py_PACK_VERSION` and :c:func:`Py_PACK_FULL_VERSION` for
20772082
bit-packing Python version numbers.
20782083
(Contributed by Petr Viktorin in :gh:`128629`.)

Lib/test/clinic.test.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1410,7 +1410,7 @@ test_unsigned_long_converter(PyObject *module, PyObject *const *args, Py_ssize_t
14101410
if (nargs < 3) {
14111411
goto skip_optional;
14121412
}
1413-
if (!PyLong_Check(args[2])) {
1413+
if (!PyIndex_Check(args[2])) {
14141414
_PyArg_BadArgument("test_unsigned_long_converter", "argument 3", "int", args[2]);
14151415
goto exit;
14161416
}
@@ -1425,7 +1425,7 @@ test_unsigned_long_converter(PyObject *module, PyObject *const *args, Py_ssize_t
14251425
static PyObject *
14261426
test_unsigned_long_converter_impl(PyObject *module, unsigned long a,
14271427
unsigned long b, unsigned long c)
1428-
/*[clinic end generated code: output=540bb0ba2894e1fe input=f450d94cae1ef73b]*/
1428+
/*[clinic end generated code: output=d74eed227d77a31b input=f450d94cae1ef73b]*/
14291429

14301430

14311431
/*[clinic input]
@@ -1525,7 +1525,7 @@ test_unsigned_long_long_converter(PyObject *module, PyObject *const *args, Py_ss
15251525
if (nargs < 3) {
15261526
goto skip_optional;
15271527
}
1528-
if (!PyLong_Check(args[2])) {
1528+
if (!PyIndex_Check(args[2])) {
15291529
_PyArg_BadArgument("test_unsigned_long_long_converter", "argument 3", "int", args[2]);
15301530
goto exit;
15311531
}
@@ -1542,7 +1542,7 @@ test_unsigned_long_long_converter_impl(PyObject *module,
15421542
unsigned long long a,
15431543
unsigned long long b,
15441544
unsigned long long c)
1545-
/*[clinic end generated code: output=3d69994f618b46bb input=a15115dc41866ff4]*/
1545+
/*[clinic end generated code: output=5ca4e4dfb3db644b input=a15115dc41866ff4]*/
15461546

15471547

15481548
/*[clinic input]

Lib/test/test_capi/test_getargs.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -267,12 +267,12 @@ def test_I(self):
267267
def test_k(self):
268268
from _testcapi import getargs_k
269269
# k returns 'unsigned long', no range checking
270-
# it does not accept float, or instances with __int__
271270
self.assertRaises(TypeError, getargs_k, 3.14)
272-
self.assertRaises(TypeError, getargs_k, Index())
271+
self.assertEqual(99, getargs_k(Index()))
273272
self.assertEqual(0, getargs_k(IndexIntSubclass()))
274273
self.assertRaises(TypeError, getargs_k, BadIndex())
275-
self.assertRaises(TypeError, getargs_k, BadIndex2())
274+
with self.assertWarns(DeprecationWarning):
275+
self.assertEqual(1, getargs_k(BadIndex2()))
276276
self.assertEqual(0, getargs_k(BadIndex3()))
277277
self.assertRaises(TypeError, getargs_k, Int())
278278
self.assertEqual(0, getargs_k(IntSubclass()))
@@ -419,10 +419,11 @@ def test_K(self):
419419
from _testcapi import getargs_K
420420
# K return 'unsigned long long', no range checking
421421
self.assertRaises(TypeError, getargs_K, 3.14)
422-
self.assertRaises(TypeError, getargs_K, Index())
422+
self.assertEqual(99, getargs_K(Index()))
423423
self.assertEqual(0, getargs_K(IndexIntSubclass()))
424424
self.assertRaises(TypeError, getargs_K, BadIndex())
425-
self.assertRaises(TypeError, getargs_K, BadIndex2())
425+
with self.assertWarns(DeprecationWarning):
426+
self.assertEqual(1, getargs_K(BadIndex2()))
426427
self.assertEqual(0, getargs_K(BadIndex3()))
427428
self.assertRaises(TypeError, getargs_K, Int())
428429
self.assertEqual(0, getargs_K(IntSubclass()))
@@ -432,6 +433,7 @@ def test_K(self):
432433

433434
self.assertEqual(ULLONG_MAX, getargs_K(ULLONG_MAX))
434435
self.assertEqual(0, getargs_K(0))
436+
self.assertEqual(ULLONG_MAX, getargs_K(ULLONG_MAX))
435437
self.assertEqual(0, getargs_K(ULLONG_MAX+1))
436438

437439
self.assertEqual(42, getargs_K(42))
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
The ``k`` and ``K`` formats in :c:func:`PyArg_Parse` now support the
2+
:meth:`~object.__index__` special method, like all other integer formats.

Modules/clinic/_cursesmodule.c.h

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Modules/clinic/_testclinic.c.h

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Modules/clinic/fcntlmodule.c.h

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Modules/clinic/posixmodule.c.h

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Modules/clinic/signalmodule.c.h

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/getargs.c

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -839,10 +839,13 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
839839
unsigned long *p = va_arg(*p_va, unsigned long *);
840840
HANDLE_NULLABLE;
841841
unsigned long ival;
842-
if (PyLong_Check(arg))
843-
ival = PyLong_AsUnsignedLongMask(arg);
844-
else
842+
if (!PyIndex_Check(arg)) {
845843
return converterr(nullable, "int", arg, msgbuf, bufsize);
844+
}
845+
ival = PyLong_AsUnsignedLongMask(arg);
846+
if (ival == (unsigned long)(long)-1 && PyErr_Occurred()) {
847+
RETURN_ERR_OCCURRED;
848+
}
846849
*p = ival;
847850
break;
848851
}
@@ -862,10 +865,13 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
862865
unsigned long long *p = va_arg(*p_va, unsigned long long *);
863866
HANDLE_NULLABLE;
864867
unsigned long long ival;
865-
if (PyLong_Check(arg))
866-
ival = PyLong_AsUnsignedLongLongMask(arg);
867-
else
868+
if (!PyIndex_Check(arg)) {
868869
return converterr(nullable, "int", arg, msgbuf, bufsize);
870+
}
871+
ival = PyLong_AsUnsignedLongLongMask(arg);
872+
if (ival == (unsigned long long)(long long)-1 && PyErr_Occurred()) {
873+
RETURN_ERR_OCCURRED;
874+
}
869875
*p = ival;
870876
break;
871877
}

Tools/clinic/libclinic/converters.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ def use_converter(self) -> None:
386386
def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None:
387387
if self.format_unit == 'k':
388388
return self.format_code("""
389-
if (!PyLong_Check({argname})) {{{{
389+
if (!PyIndex_Check({argname})) {{{{
390390
{bad_argument}
391391
goto exit;
392392
}}}}
@@ -444,7 +444,7 @@ def use_converter(self) -> None:
444444
def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None:
445445
if self.format_unit == 'K':
446446
return self.format_code("""
447-
if (!PyLong_Check({argname})) {{{{
447+
if (!PyIndex_Check({argname})) {{{{
448448
{bad_argument}
449449
goto exit;
450450
}}}}

0 commit comments

Comments
 (0)