From fbc78738ff279c311fcb5ff1352780d139e26273 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 17 Apr 2025 12:38:30 +0300 Subject: [PATCH 1/9] gh-132629: Deprecate acception out of range values for unsigned integers in PyArg_Parse For unsigned integer formats in the PyArg_Parse* funcions, accepting Python integers with value that is larger than the maximal value the corresponding C type or less than the minimal value for the corresponding signed integer type is now deprecated. --- Doc/c-api/arg.rst | 27 ++++-- Doc/whatsnew/3.14.rst | 7 ++ Lib/test/clinic.test.c | 93 +++++++++++++++---- Lib/test/test_capi/test_getargs.py | 70 +++++++++++--- Lib/test/test_clinic.py | 55 ++++++++--- ...-04-17-12-37-27.gh-issue-132629.01ArwX.rst | 4 + Modules/clinic/_cursesmodule.c.h | 34 ++++++- Modules/clinic/_testclinic.c.h | 85 ++++++++++++++--- Modules/clinic/_zoneinfo.c.h | 17 +++- Modules/clinic/binascii.c.h | 38 ++++++-- Modules/clinic/fcntlmodule.c.h | 54 +++++------ Modules/clinic/posixmodule.c.h | 52 +++++++++-- Modules/clinic/selectmodule.c.h | 38 ++++++-- Modules/clinic/signalmodule.c.h | 18 +++- Modules/clinic/zlibmodule.c.h | 38 ++++++-- Modules/fcntlmodule.c | 3 - PC/clinic/msvcrtmodule.c.h | 20 +++- Python/getargs.c | 91 +++++++++++++----- Tools/clinic/libclinic/converters.py | 91 ++++++++++++++---- 19 files changed, 657 insertions(+), 178 deletions(-) create mode 100644 Misc/NEWS.d/next/C_API/2025-04-17-12-37-27.gh-issue-132629.01ArwX.rst diff --git a/Doc/c-api/arg.rst b/Doc/c-api/arg.rst index 81b093a3510914..af43f36dc3e4de 100644 --- a/Doc/c-api/arg.rst +++ b/Doc/c-api/arg.rst @@ -241,9 +241,11 @@ the Python object to the required type. For signed integer formats, :exc:`OverflowError` is raised if the value is out of range for the C type. -For unsigned integer formats, no range checking is done --- the +For unsigned integer formats, the most significant bits are silently truncated when the receiving field is too -small to receive the value. +small to receive the value, and :exc:`DeprecationWarning` is emitted when +the value is larger than the maximal value the C type or less than the +minimal value for the corresponding signed integer type. ``b`` (:class:`int`) [unsigned char] Convert a nonnegative Python integer to an unsigned tiny integer, stored in a C @@ -252,34 +254,31 @@ small to receive the value. ``B`` (:class:`int`) [unsigned char] Convert a Python integer to a tiny integer without overflow checking, stored in a C :c:expr:`unsigned char`. + Convert a Python integer to a C :c:expr:`unsigned char`. ``h`` (:class:`int`) [short int] Convert a Python integer to a C :c:expr:`short int`. ``H`` (:class:`int`) [unsigned short int] - Convert a Python integer to a C :c:expr:`unsigned short int`, without overflow - checking. + Convert a Python integer to a C :c:expr:`unsigned short int`. ``i`` (:class:`int`) [int] Convert a Python integer to a plain C :c:expr:`int`. ``I`` (:class:`int`) [unsigned int] - Convert a Python integer to a C :c:expr:`unsigned int`, without overflow - checking. + Convert a Python integer to a C :c:expr:`unsigned int`. ``l`` (:class:`int`) [long int] Convert a Python integer to a C :c:expr:`long int`. ``k`` (:class:`int`) [unsigned long] - Convert a Python integer to a C :c:expr:`unsigned long` without - overflow checking. + Convert a Python integer to a C :c:expr:`unsigned long`. ``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. + Convert a Python integer to a C :c:expr:`unsigned long long`. ``n`` (:class:`int`) [:c:type:`Py_ssize_t`] Convert a Python integer to a C :c:type:`Py_ssize_t`. @@ -304,6 +303,14 @@ small to receive the value. ``D`` (:class:`complex`) [Py_complex] Convert a Python complex number to a C :c:type:`Py_complex` structure. +.. deprecated:: next + + For unsigned integer formats ``B``, ``H``, ``I``, ``k`` and ``K``, + :exc:`DeprecationWarning` is emitted when the value is larger than + the maximal value the C type or less than the minimal value for + the corresponding signed integer type. + + Other objects ------------- diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index c50d1669fef84c..357f901e230eed 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -2000,6 +2000,13 @@ Deprecated or a :term:`borrowed reference`. (Contributed by Serhiy Storchaka in :gh:`50333`.) +* For unsigned integer formats in :c:func:`PyArg_ParseTuple`, + accepting Python integers with value that is larger than + the maximal value the corresponding C type or less than + the minimal value for the corresponding signed integer type + is now deprecated. + (Contributed by Serhiy Storchaka in :gh:`132629`.) + * The previously undocumented function :c:func:`PySequence_In` is :term:`soft deprecated`. Use :c:func:`PySequence_Contains` instead. (Contributed by Yuki Kobayashi in :gh:`127896`.) diff --git a/Lib/test/clinic.test.c b/Lib/test/clinic.test.c index 8bc6d511e6c4a8..2a2054986dbe92 100644 --- a/Lib/test/clinic.test.c +++ b/Lib/test/clinic.test.c @@ -1020,12 +1020,19 @@ test_unsigned_char_converter(PyObject *module, PyObject *const *args, Py_ssize_t goto skip_optional; } { - unsigned long ival = PyLong_AsUnsignedLongMask(args[2]); - if (ival == (unsigned long)-1 && PyErr_Occurred()) { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[2], &c, sizeof(unsigned char), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { goto exit; } - else { - c = (unsigned char) ival; + if ((size_t)_bytes > sizeof(unsigned char)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } } } skip_optional: @@ -1038,7 +1045,7 @@ test_unsigned_char_converter(PyObject *module, PyObject *const *args, Py_ssize_t static PyObject * test_unsigned_char_converter_impl(PyObject *module, unsigned char a, unsigned char b, unsigned char c) -/*[clinic end generated code: output=45920dbedc22eb55 input=021414060993e289]*/ +/*[clinic end generated code: output=49eda9faaf53372a input=021414060993e289]*/ /*[clinic input] @@ -1151,9 +1158,21 @@ test_unsigned_short_converter(PyObject *module, PyObject *const *args, Py_ssize_ if (nargs < 3) { goto skip_optional; } - c = (unsigned short)PyLong_AsUnsignedLongMask(args[2]); - if (c == (unsigned short)-1 && PyErr_Occurred()) { - goto exit; + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[2], &c, sizeof(unsigned short), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned short)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } } skip_optional: return_value = test_unsigned_short_converter_impl(module, a, b, c); @@ -1165,7 +1184,7 @@ test_unsigned_short_converter(PyObject *module, PyObject *const *args, Py_ssize_ static PyObject * test_unsigned_short_converter_impl(PyObject *module, unsigned short a, unsigned short b, unsigned short c) -/*[clinic end generated code: output=e6e990df729114fc input=cdfd8eff3d9176b4]*/ +/*[clinic end generated code: output=f591c7797e150f49 input=cdfd8eff3d9176b4]*/ /*[clinic input] @@ -1298,9 +1317,21 @@ test_unsigned_int_converter(PyObject *module, PyObject *const *args, Py_ssize_t if (nargs < 3) { goto skip_optional; } - c = (unsigned int)PyLong_AsUnsignedLongMask(args[2]); - if (c == (unsigned int)-1 && PyErr_Occurred()) { - goto exit; + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[2], &c, sizeof(unsigned int), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned int)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } } skip_optional: return_value = test_unsigned_int_converter_impl(module, a, b, c); @@ -1312,7 +1343,7 @@ test_unsigned_int_converter(PyObject *module, PyObject *const *args, Py_ssize_t static PyObject * test_unsigned_int_converter_impl(PyObject *module, unsigned int a, unsigned int b, unsigned int c) -/*[clinic end generated code: output=f9cdbe410ccc98a3 input=5533534828b62fc0]*/ +/*[clinic end generated code: output=50a413f1cc82dc11 input=5533534828b62fc0]*/ /*[clinic input] @@ -1414,7 +1445,21 @@ test_unsigned_long_converter(PyObject *module, PyObject *const *args, Py_ssize_t _PyArg_BadArgument("test_unsigned_long_converter", "argument 3", "int", args[2]); goto exit; } - c = PyLong_AsUnsignedLongMask(args[2]); + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[2], &c, sizeof(unsigned long), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned long)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } + } skip_optional: return_value = test_unsigned_long_converter_impl(module, a, b, c); @@ -1425,7 +1470,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=a7c8b071bc71cf81 input=f450d94cae1ef73b]*/ /*[clinic input] @@ -1529,7 +1574,21 @@ test_unsigned_long_long_converter(PyObject *module, PyObject *const *args, Py_ss _PyArg_BadArgument("test_unsigned_long_long_converter", "argument 3", "int", args[2]); goto exit; } - c = PyLong_AsUnsignedLongLongMask(args[2]); + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[2], &c, sizeof(unsigned long long), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned long long)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } + } skip_optional: return_value = test_unsigned_long_long_converter_impl(module, a, b, c); @@ -1542,7 +1601,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=bf30fe0bb51cb037 input=a15115dc41866ff4]*/ /*[clinic input] diff --git a/Lib/test/test_capi/test_getargs.py b/Lib/test/test_capi/test_getargs.py index b9cad8d2600e56..53d383011c6005 100644 --- a/Lib/test/test_capi/test_getargs.py +++ b/Lib/test/test_capi/test_getargs.py @@ -48,8 +48,8 @@ LARGE = 0x7FFFFFFF VERY_LARGE = 0xFF0000121212121212121242 -from _testcapi import UCHAR_MAX, USHRT_MAX, UINT_MAX, ULONG_MAX, INT_MAX, \ - INT_MIN, LONG_MIN, LONG_MAX, PY_SSIZE_T_MIN, PY_SSIZE_T_MAX, \ +from _testcapi import UCHAR_MAX, USHRT_MAX, UINT_MAX, ULONG_MAX, ULLONG_MAX, INT_MAX, \ + INT_MIN, LONG_MIN, LONG_MAX, LLONG_MIN, LLONG_MAX, PY_SSIZE_T_MIN, PY_SSIZE_T_MAX, \ SHRT_MIN, SHRT_MAX, FLT_MIN, FLT_MAX, DBL_MIN, DBL_MAX DBL_MAX_EXP = sys.float_info.max_exp @@ -57,9 +57,8 @@ NAN = float('nan') # fake, they are not defined in Python's header files -LLONG_MAX = 2**63-1 -LLONG_MIN = -2**63 -ULLONG_MAX = 2**64-1 +SCHAR_MAX = UCHAR_MAX // 2 +SCHAR_MIN = SCHAR_MAX - UCHAR_MAX NULL = None @@ -209,10 +208,23 @@ def test_B(self): self.assertEqual(UCHAR_MAX, getargs_B(-1)) self.assertEqual(0, getargs_B(0)) self.assertEqual(UCHAR_MAX, getargs_B(UCHAR_MAX)) - self.assertEqual(0, getargs_B(UCHAR_MAX+1)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(0, getargs_B(UCHAR_MAX+1)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(1, getargs_B(-UCHAR_MAX)) + self.assertEqual(SCHAR_MAX+1, getargs_B(SCHAR_MIN)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(SCHAR_MAX, getargs_B(SCHAR_MIN-1)) + + self.assertEqual(128, getargs_B(-2**7)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(127, getargs_B(-2**7-1)) self.assertEqual(42, getargs_B(42)) - self.assertEqual(UCHAR_MAX & VERY_LARGE, getargs_B(VERY_LARGE)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(UCHAR_MAX & VERY_LARGE, getargs_B(VERY_LARGE)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(UCHAR_MAX & -VERY_LARGE, getargs_B(-VERY_LARGE)) def test_H(self): from _testcapi import getargs_H @@ -233,11 +245,18 @@ def test_H(self): self.assertEqual(USHRT_MAX, getargs_H(-1)) self.assertEqual(0, getargs_H(0)) self.assertEqual(USHRT_MAX, getargs_H(USHRT_MAX)) - self.assertEqual(0, getargs_H(USHRT_MAX+1)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(0, getargs_H(USHRT_MAX+1)) + self.assertEqual(SHRT_MAX+1, getargs_H(SHRT_MIN)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(SHRT_MAX, getargs_H(SHRT_MIN-1)) self.assertEqual(42, getargs_H(42)) - self.assertEqual(VERY_LARGE & USHRT_MAX, getargs_H(VERY_LARGE)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(USHRT_MAX & VERY_LARGE, getargs_H(VERY_LARGE)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(USHRT_MAX & -VERY_LARGE, getargs_H(-VERY_LARGE)) def test_I(self): from _testcapi import getargs_I @@ -258,11 +277,18 @@ def test_I(self): self.assertEqual(UINT_MAX, getargs_I(-1)) self.assertEqual(0, getargs_I(0)) self.assertEqual(UINT_MAX, getargs_I(UINT_MAX)) - self.assertEqual(0, getargs_I(UINT_MAX+1)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(0, getargs_I(UINT_MAX+1)) + self.assertEqual(INT_MAX+1, getargs_I(INT_MIN)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(INT_MAX, getargs_I(INT_MIN-1)) self.assertEqual(42, getargs_I(42)) - self.assertEqual(VERY_LARGE & UINT_MAX, getargs_I(VERY_LARGE)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(UINT_MAX & VERY_LARGE, getargs_I(VERY_LARGE)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(UINT_MAX & -VERY_LARGE, getargs_I(-VERY_LARGE)) def test_k(self): from _testcapi import getargs_k @@ -283,11 +309,18 @@ def test_k(self): self.assertEqual(ULONG_MAX, getargs_k(-1)) self.assertEqual(0, getargs_k(0)) self.assertEqual(ULONG_MAX, getargs_k(ULONG_MAX)) - self.assertEqual(0, getargs_k(ULONG_MAX+1)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(0, getargs_k(ULONG_MAX+1)) + self.assertEqual(LONG_MAX+1, getargs_k(LONG_MIN)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(LONG_MAX, getargs_k(LONG_MIN-1)) self.assertEqual(42, getargs_k(42)) - self.assertEqual(VERY_LARGE & ULONG_MAX, getargs_k(VERY_LARGE)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(ULONG_MAX & VERY_LARGE, getargs_k(VERY_LARGE)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(ULONG_MAX & -VERY_LARGE, getargs_k(-VERY_LARGE)) class Signed_TestCase(unittest.TestCase): def test_h(self): @@ -432,11 +465,18 @@ def test_K(self): self.assertEqual(ULLONG_MAX, getargs_K(ULLONG_MAX)) self.assertEqual(0, getargs_K(0)) - self.assertEqual(0, getargs_K(ULLONG_MAX+1)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(0, getargs_K(ULLONG_MAX+1)) + self.assertEqual(LLONG_MAX+1, getargs_K(LLONG_MIN)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(LLONG_MAX, getargs_K(LLONG_MIN-1)) self.assertEqual(42, getargs_K(42)) - self.assertEqual(VERY_LARGE & ULLONG_MAX, getargs_K(VERY_LARGE)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(ULLONG_MAX & VERY_LARGE, getargs_K(VERY_LARGE)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(ULLONG_MAX & -VERY_LARGE, getargs_K(-VERY_LARGE)) class Float_TestCase(unittest.TestCase, FloatsAreIdenticalMixin): diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index 199050d0d61438..b60fed0ccb4873 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -3046,6 +3046,8 @@ def test_char_converter(self): def test_unsigned_char_converter(self): from _testcapi import UCHAR_MAX + SCHAR_MAX = UCHAR_MAX // 2 + SCHAR_MIN = SCHAR_MAX - UCHAR_MAX with self.assertRaises(OverflowError): ac_tester.unsigned_char_converter(-1) with self.assertRaises(OverflowError): @@ -3055,8 +3057,13 @@ def test_unsigned_char_converter(self): with self.assertRaises(TypeError): ac_tester.unsigned_char_converter([]) self.assertEqual(ac_tester.unsigned_char_converter(), (12, 34, 56)) - self.assertEqual(ac_tester.unsigned_char_converter(0, 0, UCHAR_MAX + 1), (0, 0, 0)) - self.assertEqual(ac_tester.unsigned_char_converter(0, 0, (UCHAR_MAX + 1) * 3 + 123), (0, 0, 123)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(ac_tester.unsigned_char_converter(0, 0, UCHAR_MAX + 1), (0, 0, 0)) + self.assertEqual(ac_tester.unsigned_char_converter(0, 0, SCHAR_MIN), (0, 0, SCHAR_MAX + 1)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(ac_tester.unsigned_char_converter(0, 0, SCHAR_MIN - 1), (0, 0, SCHAR_MAX)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(ac_tester.unsigned_char_converter(0, 0, (UCHAR_MAX + 1) * 3 + 123), (0, 0, 123)) def test_short_converter(self): from _testcapi import SHRT_MIN, SHRT_MAX @@ -3070,7 +3077,7 @@ def test_short_converter(self): self.assertEqual(ac_tester.short_converter(4321), (4321,)) def test_unsigned_short_converter(self): - from _testcapi import USHRT_MAX + from _testcapi import SHRT_MIN, SHRT_MAX, USHRT_MAX with self.assertRaises(ValueError): ac_tester.unsigned_short_converter(-1) with self.assertRaises(OverflowError): @@ -3080,8 +3087,13 @@ def test_unsigned_short_converter(self): with self.assertRaises(TypeError): ac_tester.unsigned_short_converter([]) self.assertEqual(ac_tester.unsigned_short_converter(), (12, 34, 56)) - self.assertEqual(ac_tester.unsigned_short_converter(0, 0, USHRT_MAX + 1), (0, 0, 0)) - self.assertEqual(ac_tester.unsigned_short_converter(0, 0, (USHRT_MAX + 1) * 3 + 123), (0, 0, 123)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(ac_tester.unsigned_short_converter(0, 0, USHRT_MAX + 1), (0, 0, 0)) + self.assertEqual(ac_tester.unsigned_short_converter(0, 0, SHRT_MIN), (0, 0, SHRT_MAX + 1)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(ac_tester.unsigned_short_converter(0, 0, SHRT_MIN - 1), (0, 0, SHRT_MAX)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(ac_tester.unsigned_short_converter(0, 0, (USHRT_MAX + 1) * 3 + 123), (0, 0, 123)) def test_int_converter(self): from _testcapi import INT_MIN, INT_MAX @@ -3097,7 +3109,7 @@ def test_int_converter(self): self.assertEqual(ac_tester.int_converter(1, 2, '3'), (1, 2, ord('3'))) def test_unsigned_int_converter(self): - from _testcapi import UINT_MAX + from _testcapi import INT_MIN, INT_MAX, UINT_MAX with self.assertRaises(ValueError): ac_tester.unsigned_int_converter(-1) with self.assertRaises(OverflowError): @@ -3107,8 +3119,13 @@ def test_unsigned_int_converter(self): with self.assertRaises(TypeError): ac_tester.unsigned_int_converter([]) self.assertEqual(ac_tester.unsigned_int_converter(), (12, 34, 56)) - self.assertEqual(ac_tester.unsigned_int_converter(0, 0, UINT_MAX + 1), (0, 0, 0)) - self.assertEqual(ac_tester.unsigned_int_converter(0, 0, (UINT_MAX + 1) * 3 + 123), (0, 0, 123)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(ac_tester.unsigned_int_converter(0, 0, UINT_MAX + 1), (0, 0, 0)) + self.assertEqual(ac_tester.unsigned_int_converter(0, 0, INT_MIN), (0, 0, INT_MAX + 1)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(ac_tester.unsigned_int_converter(0, 0, INT_MIN - 1), (0, 0, INT_MAX)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(ac_tester.unsigned_int_converter(0, 0, (UINT_MAX + 1) * 3 + 123), (0, 0, 123)) def test_long_converter(self): from _testcapi import LONG_MIN, LONG_MAX @@ -3122,7 +3139,7 @@ def test_long_converter(self): self.assertEqual(ac_tester.long_converter(-1234), (-1234,)) def test_unsigned_long_converter(self): - from _testcapi import ULONG_MAX + from _testcapi import LONG_MIN, LONG_MAX, ULONG_MAX with self.assertRaises(ValueError): ac_tester.unsigned_long_converter(-1) with self.assertRaises(OverflowError): @@ -3132,8 +3149,13 @@ def test_unsigned_long_converter(self): with self.assertRaises(TypeError): ac_tester.unsigned_long_converter([]) self.assertEqual(ac_tester.unsigned_long_converter(), (12, 34, 56)) - self.assertEqual(ac_tester.unsigned_long_converter(0, 0, ULONG_MAX + 1), (0, 0, 0)) - self.assertEqual(ac_tester.unsigned_long_converter(0, 0, (ULONG_MAX + 1) * 3 + 123), (0, 0, 123)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(ac_tester.unsigned_long_converter(0, 0, ULONG_MAX + 1), (0, 0, 0)) + self.assertEqual(ac_tester.unsigned_long_converter(0, 0, LONG_MIN), (0, 0, LONG_MAX + 1)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(ac_tester.unsigned_long_converter(0, 0, LONG_MIN - 1), (0, 0, LONG_MAX)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(ac_tester.unsigned_long_converter(0, 0, (ULONG_MAX + 1) * 3 + 123), (0, 0, 123)) def test_long_long_converter(self): from _testcapi import LLONG_MIN, LLONG_MAX @@ -3147,7 +3169,7 @@ def test_long_long_converter(self): self.assertEqual(ac_tester.long_long_converter(-1234), (-1234,)) def test_unsigned_long_long_converter(self): - from _testcapi import ULLONG_MAX + from _testcapi import LLONG_MIN, LLONG_MAX, ULLONG_MAX with self.assertRaises(ValueError): ac_tester.unsigned_long_long_converter(-1) with self.assertRaises(OverflowError): @@ -3157,8 +3179,13 @@ def test_unsigned_long_long_converter(self): with self.assertRaises(TypeError): ac_tester.unsigned_long_long_converter([]) self.assertEqual(ac_tester.unsigned_long_long_converter(), (12, 34, 56)) - self.assertEqual(ac_tester.unsigned_long_long_converter(0, 0, ULLONG_MAX + 1), (0, 0, 0)) - self.assertEqual(ac_tester.unsigned_long_long_converter(0, 0, (ULLONG_MAX + 1) * 3 + 123), (0, 0, 123)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(ac_tester.unsigned_long_long_converter(0, 0, ULLONG_MAX + 1), (0, 0, 0)) + self.assertEqual(ac_tester.unsigned_long_long_converter(0, 0, LLONG_MIN), (0, 0, LLONG_MAX + 1)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(ac_tester.unsigned_long_long_converter(0, 0, LLONG_MIN - 1), (0, 0, LLONG_MAX)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(ac_tester.unsigned_long_long_converter(0, 0, (ULLONG_MAX + 1) * 3 + 123), (0, 0, 123)) def test_py_ssize_t_converter(self): from _testcapi import PY_SSIZE_T_MIN, PY_SSIZE_T_MAX diff --git a/Misc/NEWS.d/next/C_API/2025-04-17-12-37-27.gh-issue-132629.01ArwX.rst b/Misc/NEWS.d/next/C_API/2025-04-17-12-37-27.gh-issue-132629.01ArwX.rst new file mode 100644 index 00000000000000..a722c3c74d236c --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2025-04-17-12-37-27.gh-issue-132629.01ArwX.rst @@ -0,0 +1,4 @@ +For unsigned integer formats in :c:func:`PyArg_ParseTuple`, accepting Python +integers with value that is larger than the maximal value the corresponding +C type or less than the minimal value for the corresponding signed integer +type is now deprecated. diff --git a/Modules/clinic/_cursesmodule.c.h b/Modules/clinic/_cursesmodule.c.h index 0346cc88782941..7ab0d7c33c5e1a 100644 --- a/Modules/clinic/_cursesmodule.c.h +++ b/Modules/clinic/_cursesmodule.c.h @@ -2392,7 +2392,21 @@ _curses_ungetmouse(PyObject *module, PyObject *const *args, Py_ssize_t nargs) _PyArg_BadArgument("ungetmouse", "argument 5", "int", args[4]); goto exit; } - bstate = PyLong_AsUnsignedLongMask(args[4]); + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[4], &bstate, sizeof(unsigned long), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned long)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } + } return_value = _curses_ungetmouse_impl(module, id, x, y, z, bstate); exit: @@ -3158,7 +3172,21 @@ _curses_mousemask(PyObject *module, PyObject *arg) _PyArg_BadArgument("mousemask", "argument", "int", arg); goto exit; } - newmask = PyLong_AsUnsignedLongMask(arg); + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(arg, &newmask, sizeof(unsigned long), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned long)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } + } return_value = _curses_mousemask_impl(module, newmask); exit: @@ -4394,4 +4422,4 @@ _curses_has_extended_color_support(PyObject *module, PyObject *Py_UNUSED(ignored #ifndef _CURSES_USE_DEFAULT_COLORS_METHODDEF #define _CURSES_USE_DEFAULT_COLORS_METHODDEF #endif /* !defined(_CURSES_USE_DEFAULT_COLORS_METHODDEF) */ -/*[clinic end generated code: output=acae2eb9cf75e76d input=a9049054013a1b77]*/ +/*[clinic end generated code: output=d773308c84e58a64 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_testclinic.c.h b/Modules/clinic/_testclinic.c.h index f378e95cd6b2d8..59469a03db38f1 100644 --- a/Modules/clinic/_testclinic.c.h +++ b/Modules/clinic/_testclinic.c.h @@ -688,12 +688,19 @@ unsigned_char_converter(PyObject *module, PyObject *const *args, Py_ssize_t narg goto skip_optional; } { - unsigned long ival = PyLong_AsUnsignedLongMask(args[2]); - if (ival == (unsigned long)-1 && PyErr_Occurred()) { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[2], &c, sizeof(unsigned char), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { goto exit; } - else { - c = (unsigned char) ival; + if ((size_t)_bytes > sizeof(unsigned char)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } } } skip_optional: @@ -790,9 +797,21 @@ unsigned_short_converter(PyObject *module, PyObject *const *args, Py_ssize_t nar if (nargs < 3) { goto skip_optional; } - c = (unsigned short)PyLong_AsUnsignedLongMask(args[2]); - if (c == (unsigned short)-1 && PyErr_Occurred()) { - goto exit; + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[2], &c, sizeof(unsigned short), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned short)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } } skip_optional: return_value = unsigned_short_converter_impl(module, a, b, c); @@ -897,9 +916,21 @@ unsigned_int_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs if (nargs < 3) { goto skip_optional; } - c = (unsigned int)PyLong_AsUnsignedLongMask(args[2]); - if (c == (unsigned int)-1 && PyErr_Occurred()) { - goto exit; + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[2], &c, sizeof(unsigned int), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned int)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } } skip_optional: return_value = unsigned_int_converter_impl(module, a, b, c); @@ -984,7 +1015,21 @@ unsigned_long_converter(PyObject *module, PyObject *const *args, Py_ssize_t narg _PyArg_BadArgument("unsigned_long_converter", "argument 3", "int", args[2]); goto exit; } - c = PyLong_AsUnsignedLongMask(args[2]); + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[2], &c, sizeof(unsigned long), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned long)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } + } skip_optional: return_value = unsigned_long_converter_impl(module, a, b, c); @@ -1068,7 +1113,21 @@ unsigned_long_long_converter(PyObject *module, PyObject *const *args, Py_ssize_t _PyArg_BadArgument("unsigned_long_long_converter", "argument 3", "int", args[2]); goto exit; } - c = PyLong_AsUnsignedLongLongMask(args[2]); + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[2], &c, sizeof(unsigned long long), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned long long)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } + } skip_optional: return_value = unsigned_long_long_converter_impl(module, a, b, c); @@ -4379,4 +4438,4 @@ posonly_poskw_varpos_array_no_fastcall(PyTypeObject *type, PyObject *args, PyObj exit: return return_value; } -/*[clinic end generated code: output=a3726ee0a94090d1 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=b9f0435ad977f558 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_zoneinfo.c.h b/Modules/clinic/_zoneinfo.c.h index 09ac157cbfd135..19564a6c13f246 100644 --- a/Modules/clinic/_zoneinfo.c.h +++ b/Modules/clinic/_zoneinfo.c.h @@ -434,12 +434,19 @@ zoneinfo_ZoneInfo__unpickle(PyObject *type, PyTypeObject *cls, PyObject *const * } key = args[0]; { - unsigned long ival = PyLong_AsUnsignedLongMask(args[1]); - if (ival == (unsigned long)-1 && PyErr_Occurred()) { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[1], &from_cache, sizeof(unsigned char), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { goto exit; } - else { - from_cache = (unsigned char) ival; + if ((size_t)_bytes > sizeof(unsigned char)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } } } return_value = zoneinfo_ZoneInfo__unpickle_impl((PyTypeObject *)type, cls, key, from_cache); @@ -447,4 +454,4 @@ zoneinfo_ZoneInfo__unpickle(PyObject *type, PyTypeObject *cls, PyObject *const * exit: return return_value; } -/*[clinic end generated code: output=8e9e204f390261b9 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=c6df04d7b400bd7f input=a9049054013a1b77]*/ diff --git a/Modules/clinic/binascii.c.h b/Modules/clinic/binascii.c.h index 602e42a4c1aaa4..ce29e0d11a45cd 100644 --- a/Modules/clinic/binascii.c.h +++ b/Modules/clinic/binascii.c.h @@ -292,9 +292,21 @@ binascii_crc_hqx(PyObject *module, PyObject *const *args, Py_ssize_t nargs) if (PyObject_GetBuffer(args[0], &data, PyBUF_SIMPLE) != 0) { goto exit; } - crc = (unsigned int)PyLong_AsUnsignedLongMask(args[1]); - if (crc == (unsigned int)-1 && PyErr_Occurred()) { - goto exit; + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[1], &crc, sizeof(unsigned int), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned int)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } } return_value = binascii_crc_hqx_impl(module, &data, crc); @@ -336,9 +348,21 @@ binascii_crc32(PyObject *module, PyObject *const *args, Py_ssize_t nargs) if (nargs < 2) { goto skip_optional; } - crc = (unsigned int)PyLong_AsUnsignedLongMask(args[1]); - if (crc == (unsigned int)-1 && PyErr_Occurred()) { - goto exit; + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[1], &crc, sizeof(unsigned int), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned int)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } } skip_optional: _return_value = binascii_crc32_impl(module, &data, crc); @@ -788,4 +812,4 @@ binascii_b2a_qp(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj return return_value; } -/*[clinic end generated code: output=adb855a2797c3cad input=a9049054013a1b77]*/ +/*[clinic end generated code: output=fba6a71e0d7d092f input=a9049054013a1b77]*/ diff --git a/Modules/clinic/fcntlmodule.c.h b/Modules/clinic/fcntlmodule.c.h index 53b139e09afdf1..8afde556baca0f 100644 --- a/Modules/clinic/fcntlmodule.c.h +++ b/Modules/clinic/fcntlmodule.c.h @@ -2,6 +2,8 @@ preserve [clinic start generated code]*/ +#include "pycore_modsupport.h" // _PyArg_CheckPositional() + PyDoc_STRVAR(fcntl_fcntl__doc__, "fcntl($module, fd, cmd, arg=0, /)\n" "--\n" @@ -19,7 +21,7 @@ PyDoc_STRVAR(fcntl_fcntl__doc__, "corresponding to the return value of the fcntl call in the C code."); #define FCNTL_FCNTL_METHODDEF \ - {"fcntl", (PyCFunction)(void(*)(void))fcntl_fcntl, METH_FASTCALL, fcntl_fcntl__doc__}, + {"fcntl", _PyCFunction_CAST(fcntl_fcntl), METH_FASTCALL, fcntl_fcntl__doc__}, static PyObject * fcntl_fcntl_impl(PyObject *module, int fd, int code, PyObject *arg); @@ -32,12 +34,7 @@ fcntl_fcntl(PyObject *module, PyObject *const *args, Py_ssize_t nargs) int code; PyObject *arg = NULL; - if (nargs < 2) { - PyErr_Format(PyExc_TypeError, "fcntl expected at least 2 arguments, got %zd", nargs); - goto exit; - } - if (nargs > 3) { - PyErr_Format(PyExc_TypeError, "fcntl expected at most 3 arguments, got %zd", nargs); + if (!_PyArg_CheckPositional("fcntl", nargs, 2, 3)) { goto exit; } fd = PyObject_AsFileDescriptor(args[0]); @@ -93,7 +90,7 @@ PyDoc_STRVAR(fcntl_ioctl__doc__, "code."); #define FCNTL_IOCTL_METHODDEF \ - {"ioctl", (PyCFunction)(void(*)(void))fcntl_ioctl, METH_FASTCALL, fcntl_ioctl__doc__}, + {"ioctl", _PyCFunction_CAST(fcntl_ioctl), METH_FASTCALL, fcntl_ioctl__doc__}, static PyObject * fcntl_ioctl_impl(PyObject *module, int fd, unsigned long code, @@ -108,12 +105,7 @@ fcntl_ioctl(PyObject *module, PyObject *const *args, Py_ssize_t nargs) PyObject *ob_arg = NULL; int mutate_arg = 1; - if (nargs < 2) { - PyErr_Format(PyExc_TypeError, "ioctl expected at least 2 arguments, got %zd", nargs); - goto exit; - } - if (nargs > 4) { - PyErr_Format(PyExc_TypeError, "ioctl expected at most 4 arguments, got %zd", nargs); + if (!_PyArg_CheckPositional("ioctl", nargs, 2, 4)) { goto exit; } fd = PyObject_AsFileDescriptor(args[0]); @@ -121,10 +113,24 @@ fcntl_ioctl(PyObject *module, PyObject *const *args, Py_ssize_t nargs) goto exit; } if (!PyLong_Check(args[1])) { - PyErr_Format(PyExc_TypeError, "ioctl() argument 2 must be int, not %T", args[1]); + _PyArg_BadArgument("ioctl", "argument 2", "int", args[1]); goto exit; } - code = PyLong_AsUnsignedLongMask(args[1]); + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[1], &code, sizeof(unsigned long), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned long)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } + } if (nargs < 3) { goto skip_optional; } @@ -153,7 +159,7 @@ PyDoc_STRVAR(fcntl_flock__doc__, "function is emulated using fcntl())."); #define FCNTL_FLOCK_METHODDEF \ - {"flock", (PyCFunction)(void(*)(void))fcntl_flock, METH_FASTCALL, fcntl_flock__doc__}, + {"flock", _PyCFunction_CAST(fcntl_flock), METH_FASTCALL, fcntl_flock__doc__}, static PyObject * fcntl_flock_impl(PyObject *module, int fd, int code); @@ -165,8 +171,7 @@ fcntl_flock(PyObject *module, PyObject *const *args, Py_ssize_t nargs) int fd; int code; - if (nargs != 2) { - PyErr_Format(PyExc_TypeError, "flock expected 2 arguments, got %zd", nargs); + if (!_PyArg_CheckPositional("flock", nargs, 2, 2)) { goto exit; } fd = PyObject_AsFileDescriptor(args[0]); @@ -211,7 +216,7 @@ PyDoc_STRVAR(fcntl_lockf__doc__, " 2 - relative to the end of the file (SEEK_END)"); #define FCNTL_LOCKF_METHODDEF \ - {"lockf", (PyCFunction)(void(*)(void))fcntl_lockf, METH_FASTCALL, fcntl_lockf__doc__}, + {"lockf", _PyCFunction_CAST(fcntl_lockf), METH_FASTCALL, fcntl_lockf__doc__}, static PyObject * fcntl_lockf_impl(PyObject *module, int fd, int code, PyObject *lenobj, @@ -227,12 +232,7 @@ fcntl_lockf(PyObject *module, PyObject *const *args, Py_ssize_t nargs) PyObject *startobj = NULL; int whence = 0; - if (nargs < 2) { - PyErr_Format(PyExc_TypeError, "lockf expected at least 2 arguments, got %zd", nargs); - goto exit; - } - if (nargs > 5) { - PyErr_Format(PyExc_TypeError, "lockf expected at most 5 arguments, got %zd", nargs); + if (!_PyArg_CheckPositional("lockf", nargs, 2, 5)) { goto exit; } fd = PyObject_AsFileDescriptor(args[0]); @@ -264,4 +264,4 @@ fcntl_lockf(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=45a56f53fd17ff3c input=a9049054013a1b77]*/ +/*[clinic end generated code: output=dd541b9ef6b49d90 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index d03f68ab8fb9aa..0e773006124b98 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -844,7 +844,21 @@ os_chflags(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject * _PyArg_BadArgument("chflags", "argument 'flags'", "int", args[1]); goto exit; } - flags = PyLong_AsUnsignedLongMask(args[1]); + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[1], &flags, sizeof(unsigned long), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned long)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } + } if (!noptargs) { goto skip_optional_pos; } @@ -928,7 +942,21 @@ os_lchflags(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject _PyArg_BadArgument("lchflags", "argument 'flags'", "int", args[1]); goto exit; } - flags = PyLong_AsUnsignedLongMask(args[1]); + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[1], &flags, sizeof(unsigned long), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned long)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } + } return_value = os_lchflags_impl(module, &path, flags); exit: @@ -11373,9 +11401,21 @@ os_memfd_create(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj if (!noptargs) { goto skip_optional_pos; } - flags = (unsigned int)PyLong_AsUnsignedLongMask(args[1]); - if (flags == (unsigned int)-1 && PyErr_Occurred()) { - goto exit; + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[1], &flags, sizeof(unsigned int), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned int)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } } skip_optional_pos: return_value = os_memfd_create_impl(module, name, flags); @@ -13398,4 +13438,4 @@ os__emscripten_debugger(PyObject *module, PyObject *Py_UNUSED(ignored)) #ifndef OS__EMSCRIPTEN_DEBUGGER_METHODDEF #define OS__EMSCRIPTEN_DEBUGGER_METHODDEF #endif /* !defined(OS__EMSCRIPTEN_DEBUGGER_METHODDEF) */ -/*[clinic end generated code: output=35dd8edb53b50537 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=85cc5dee5ca0cdb9 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/selectmodule.c.h b/Modules/clinic/selectmodule.c.h index 253ad8c9e78f00..c0a5f678ad038d 100644 --- a/Modules/clinic/selectmodule.c.h +++ b/Modules/clinic/selectmodule.c.h @@ -783,9 +783,21 @@ select_epoll_register(PyObject *self, PyObject *const *args, Py_ssize_t nargs, P if (!noptargs) { goto skip_optional_pos; } - eventmask = (unsigned int)PyLong_AsUnsignedLongMask(args[1]); - if (eventmask == (unsigned int)-1 && PyErr_Occurred()) { - goto exit; + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[1], &eventmask, sizeof(unsigned int), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned int)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } } skip_optional_pos: return_value = select_epoll_register_impl((pyEpoll_Object *)self, fd, eventmask); @@ -860,9 +872,21 @@ select_epoll_modify(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyO if (fd < 0) { goto exit; } - eventmask = (unsigned int)PyLong_AsUnsignedLongMask(args[1]); - if (eventmask == (unsigned int)-1 && PyErr_Occurred()) { - goto exit; + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[1], &eventmask, sizeof(unsigned int), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned int)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } } return_value = select_epoll_modify_impl((pyEpoll_Object *)self, fd, eventmask); @@ -1375,4 +1399,4 @@ select_kqueue_control(PyObject *self, PyObject *const *args, Py_ssize_t nargs) #ifndef SELECT_KQUEUE_CONTROL_METHODDEF #define SELECT_KQUEUE_CONTROL_METHODDEF #endif /* !defined(SELECT_KQUEUE_CONTROL_METHODDEF) */ -/*[clinic end generated code: output=6fc20d78802511d1 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=2a66dd831f22c696 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/signalmodule.c.h b/Modules/clinic/signalmodule.c.h index 3ae8be84ccbf9c..96f294c681ab2f 100644 --- a/Modules/clinic/signalmodule.c.h +++ b/Modules/clinic/signalmodule.c.h @@ -660,7 +660,21 @@ signal_pthread_kill(PyObject *module, PyObject *const *args, Py_ssize_t nargs) _PyArg_BadArgument("pthread_kill", "argument 1", "int", args[0]); goto exit; } - thread_id = PyLong_AsUnsignedLongMask(args[0]); + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[0], &thread_id, sizeof(unsigned long), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned long)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } + } signalnum = PyLong_AsInt(args[1]); if (signalnum == -1 && PyErr_Occurred()) { goto exit; @@ -779,4 +793,4 @@ signal_pidfd_send_signal(PyObject *module, PyObject *const *args, Py_ssize_t nar #ifndef SIGNAL_PIDFD_SEND_SIGNAL_METHODDEF #define SIGNAL_PIDFD_SEND_SIGNAL_METHODDEF #endif /* !defined(SIGNAL_PIDFD_SEND_SIGNAL_METHODDEF) */ -/*[clinic end generated code: output=a8b1ac2fc44a007e input=a9049054013a1b77]*/ +/*[clinic end generated code: output=28652b77931b1f7f input=a9049054013a1b77]*/ diff --git a/Modules/clinic/zlibmodule.c.h b/Modules/clinic/zlibmodule.c.h index 2710f65a840db9..fe0735176d608d 100644 --- a/Modules/clinic/zlibmodule.c.h +++ b/Modules/clinic/zlibmodule.c.h @@ -1028,9 +1028,21 @@ zlib_adler32(PyObject *module, PyObject *const *args, Py_ssize_t nargs) if (nargs < 2) { goto skip_optional; } - value = (unsigned int)PyLong_AsUnsignedLongMask(args[1]); - if (value == (unsigned int)-1 && PyErr_Occurred()) { - goto exit; + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[1], &value, sizeof(unsigned int), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned int)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } } skip_optional: return_value = zlib_adler32_impl(module, &data, value); @@ -1078,9 +1090,21 @@ zlib_crc32(PyObject *module, PyObject *const *args, Py_ssize_t nargs) if (nargs < 2) { goto skip_optional; } - value = (unsigned int)PyLong_AsUnsignedLongMask(args[1]); - if (value == (unsigned int)-1 && PyErr_Occurred()) { - goto exit; + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[1], &value, sizeof(unsigned int), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned int)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } } skip_optional: _return_value = zlib_crc32_impl(module, &data, value); @@ -1121,4 +1145,4 @@ zlib_crc32(PyObject *module, PyObject *const *args, Py_ssize_t nargs) #ifndef ZLIB_DECOMPRESS___DEEPCOPY___METHODDEF #define ZLIB_DECOMPRESS___DEEPCOPY___METHODDEF #endif /* !defined(ZLIB_DECOMPRESS___DEEPCOPY___METHODDEF) */ -/*[clinic end generated code: output=33938c7613a8c1c7 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=8d7226784a8b2379 input=a9049054013a1b77]*/ diff --git a/Modules/fcntlmodule.c b/Modules/fcntlmodule.c index 90ebfd7e99a777..60329b858a50fe 100644 --- a/Modules/fcntlmodule.c +++ b/Modules/fcntlmodule.c @@ -2,9 +2,6 @@ // Need limited C API version 3.13 for PyLong_AsInt() #include "pyconfig.h" // Py_GIL_DISABLED -#ifndef Py_GIL_DISABLED -# define Py_LIMITED_API 0x030d0000 -#endif #include "Python.h" diff --git a/PC/clinic/msvcrtmodule.c.h b/PC/clinic/msvcrtmodule.c.h index a77d0855af293f..647aadfa46bbed 100644 --- a/PC/clinic/msvcrtmodule.c.h +++ b/PC/clinic/msvcrtmodule.c.h @@ -690,9 +690,21 @@ msvcrt_SetErrorMode(PyObject *module, PyObject *arg) PyObject *return_value = NULL; unsigned int mode; - mode = (unsigned int)PyLong_AsUnsignedLongMask(arg); - if (mode == (unsigned int)-1 && PyErr_Occurred()) { - goto exit; + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(arg, &mode, sizeof(unsigned int), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned int)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } } return_value = msvcrt_SetErrorMode_impl(module, mode); @@ -731,4 +743,4 @@ msvcrt_SetErrorMode(PyObject *module, PyObject *arg) #ifndef MSVCRT_GETERRORMODE_METHODDEF #define MSVCRT_GETERRORMODE_METHODDEF #endif /* !defined(MSVCRT_GETERRORMODE_METHODDEF) */ -/*[clinic end generated code: output=692c6f52bb9193ce input=a9049054013a1b77]*/ +/*[clinic end generated code: output=f67eaf745685429d input=a9049054013a1b77]*/ diff --git a/Python/getargs.c b/Python/getargs.c index 16d5e52742d129..2cc4179d2c1613 100644 --- a/Python/getargs.c +++ b/Python/getargs.c @@ -734,11 +734,20 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags, values allowed */ unsigned char *p = va_arg(*p_va, unsigned char *); HANDLE_NULLABLE; - unsigned long ival = PyLong_AsUnsignedLongMask(arg); - if (ival == (unsigned long)-1 && PyErr_Occurred()) + Py_ssize_t bytes = PyLong_AsNativeBytes(arg, p, sizeof(unsigned char), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (bytes < 0) { RETURN_ERR_OCCURRED; - else - *p = (unsigned char) ival; + } + if ((size_t)bytes > sizeof(unsigned char)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + RETURN_ERR_OCCURRED; + } + } break; } @@ -767,11 +776,20 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags, unsigned allowed */ unsigned short *p = va_arg(*p_va, unsigned short *); HANDLE_NULLABLE; - unsigned long ival = PyLong_AsUnsignedLongMask(arg); - if (ival == (unsigned long)-1 && PyErr_Occurred()) + Py_ssize_t bytes = PyLong_AsNativeBytes(arg, p, sizeof(unsigned short), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (bytes < 0) { RETURN_ERR_OCCURRED; - else - *p = (unsigned short) ival; + } + if ((size_t)bytes > sizeof(unsigned short)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + RETURN_ERR_OCCURRED; + } + } break; } @@ -800,11 +818,20 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags, unsigned allowed */ unsigned int *p = va_arg(*p_va, unsigned int *); HANDLE_NULLABLE; - unsigned long ival = PyLong_AsUnsignedLongMask(arg); - if (ival == (unsigned long)-1 && PyErr_Occurred()) + Py_ssize_t bytes = PyLong_AsNativeBytes(arg, p, sizeof(unsigned int), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (bytes < 0) { RETURN_ERR_OCCURRED; - else - *p = (unsigned int) ival; + } + if ((size_t)bytes > sizeof(unsigned int)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + RETURN_ERR_OCCURRED; + } + } break; } @@ -838,12 +865,22 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags, case 'k': { /* long sized bitfield */ 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); - *p = ival; + } + Py_ssize_t bytes = PyLong_AsNativeBytes(arg, p, sizeof(unsigned long), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (bytes < 0) { + RETURN_ERR_OCCURRED; + } + if ((size_t)bytes > sizeof(unsigned long)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + RETURN_ERR_OCCURRED; + } + } break; } @@ -861,12 +898,22 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags, case 'K': { /* long long sized bitfield */ 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); - *p = ival; + } + Py_ssize_t bytes = PyLong_AsNativeBytes(arg, p, sizeof(unsigned long long), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (bytes < 0) { + RETURN_ERR_OCCURRED; + } + if ((size_t)bytes > sizeof(unsigned long long)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + RETURN_ERR_OCCURRED; + } + } break; } diff --git a/Tools/clinic/libclinic/converters.py b/Tools/clinic/libclinic/converters.py index 871d7542ba0763..d28bf3c7315a74 100644 --- a/Tools/clinic/libclinic/converters.py +++ b/Tools/clinic/libclinic/converters.py @@ -160,12 +160,19 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st elif self.format_unit == 'B': return self.format_code(""" {{{{ - unsigned long ival = PyLong_AsUnsignedLongMask({argname}); - if (ival == (unsigned long)-1 && PyErr_Occurred()) {{{{ + Py_ssize_t _bytes = PyLong_AsNativeBytes({argname}, &{paramname}, sizeof(unsigned char), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) {{{{ goto exit; }}}} - else {{{{ - {paramname} = (unsigned char) ival; + if ((size_t)_bytes > sizeof(unsigned char)) {{{{ + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + {{{{ + goto exit; + }}}} }}}} }}}} """, @@ -229,9 +236,21 @@ def use_converter(self) -> None: def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: if self.format_unit == 'H': return self.format_code(""" - {paramname} = (unsigned short)PyLong_AsUnsignedLongMask({argname}); - if ({paramname} == (unsigned short)-1 && PyErr_Occurred()) {{{{ - goto exit; + {{{{ + Py_ssize_t _bytes = PyLong_AsNativeBytes({argname}, &{paramname}, sizeof(unsigned short), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) {{{{ + goto exit; + }}}} + if ((size_t)_bytes > sizeof(unsigned short)) {{{{ + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + {{{{ + goto exit; + }}}} + }}}} }}}} """, argname=argname) @@ -322,9 +341,21 @@ def use_converter(self) -> None: def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: if self.format_unit == 'I': return self.format_code(""" - {paramname} = (unsigned int)PyLong_AsUnsignedLongMask({argname}); - if ({paramname} == (unsigned int)-1 && PyErr_Occurred()) {{{{ - goto exit; + {{{{ + Py_ssize_t _bytes = PyLong_AsNativeBytes({argname}, &{paramname}, sizeof(unsigned int), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) {{{{ + goto exit; + }}}} + if ((size_t)_bytes > sizeof(unsigned int)) {{{{ + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + {{{{ + goto exit; + }}}} + }}}} }}}} """, argname=argname) @@ -389,7 +420,21 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st {bad_argument} goto exit; }}}} - {paramname} = PyLong_AsUnsignedLongMask({argname}); + {{{{ + Py_ssize_t _bytes = PyLong_AsNativeBytes({argname}, &{paramname}, sizeof(unsigned long), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) {{{{ + goto exit; + }}}} + if ((size_t)_bytes > sizeof(unsigned long)) {{{{ + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + {{{{ + goto exit; + }}}} + }}}} + }}}} """, argname=argname, bad_argument=self.bad_argument(displayname, 'int', limited_capi=limited_capi), @@ -398,10 +443,10 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st return super().parse_arg(argname, displayname, limited_capi=limited_capi) # NOTE: Raises OverflowError for negative integer. return self.format_code(""" - {paramname} = PyLong_AsUnsignedLong({argname}); - if ({paramname} == (unsigned long)-1 && PyErr_Occurred()) {{{{ - goto exit; - }}}} +- {paramname} = PyLong_AsUnsignedLong({argname}); +- if ({paramname} == (unsigned long)-1 && PyErr_Occurred()) {{{{ +- goto exit; +- }}}} """, argname=argname) @@ -447,7 +492,21 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st {bad_argument} goto exit; }}}} - {paramname} = PyLong_AsUnsignedLongLongMask({argname}); + {{{{ + Py_ssize_t _bytes = PyLong_AsNativeBytes({argname}, &{paramname}, sizeof(unsigned long long), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) {{{{ + goto exit; + }}}} + if ((size_t)_bytes > sizeof(unsigned long long)) {{{{ + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + {{{{ + goto exit; + }}}} + }}}} + }}}} """, argname=argname, bad_argument=self.bad_argument(displayname, 'int', limited_capi=limited_capi), From ddd9ee5123d2ff49dcf5ffd6c4a74154f0ca498b Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 17 Apr 2025 15:07:12 +0300 Subject: [PATCH 2/9] Fix compilation. --- Modules/fcntlmodule.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Modules/fcntlmodule.c b/Modules/fcntlmodule.c index 60329b858a50fe..b3cf88daa86f78 100644 --- a/Modules/fcntlmodule.c +++ b/Modules/fcntlmodule.c @@ -2,6 +2,9 @@ // Need limited C API version 3.13 for PyLong_AsInt() #include "pyconfig.h" // Py_GIL_DISABLED +#ifndef Py_BUILD_CORE_BUILTIN +# define Py_BUILD_CORE_MODULE 1 +#endif #include "Python.h" From 478d396391bf01996459200ee99ec71ef2a29773 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 23 Apr 2025 19:01:44 +0300 Subject: [PATCH 3/9] Use the limited C API again. --- Modules/clinic/fcntlmodule.c.h | 38 +++++++++++++++++++++++----------- Modules/fcntlmodule.c | 4 ++-- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/Modules/clinic/fcntlmodule.c.h b/Modules/clinic/fcntlmodule.c.h index 8afde556baca0f..8eb73ab05a8dfe 100644 --- a/Modules/clinic/fcntlmodule.c.h +++ b/Modules/clinic/fcntlmodule.c.h @@ -2,8 +2,6 @@ preserve [clinic start generated code]*/ -#include "pycore_modsupport.h" // _PyArg_CheckPositional() - PyDoc_STRVAR(fcntl_fcntl__doc__, "fcntl($module, fd, cmd, arg=0, /)\n" "--\n" @@ -21,7 +19,7 @@ PyDoc_STRVAR(fcntl_fcntl__doc__, "corresponding to the return value of the fcntl call in the C code."); #define FCNTL_FCNTL_METHODDEF \ - {"fcntl", _PyCFunction_CAST(fcntl_fcntl), METH_FASTCALL, fcntl_fcntl__doc__}, + {"fcntl", (PyCFunction)(void(*)(void))fcntl_fcntl, METH_FASTCALL, fcntl_fcntl__doc__}, static PyObject * fcntl_fcntl_impl(PyObject *module, int fd, int code, PyObject *arg); @@ -34,7 +32,12 @@ fcntl_fcntl(PyObject *module, PyObject *const *args, Py_ssize_t nargs) int code; PyObject *arg = NULL; - if (!_PyArg_CheckPositional("fcntl", nargs, 2, 3)) { + if (nargs < 2) { + PyErr_Format(PyExc_TypeError, "fcntl expected at least 2 arguments, got %zd", nargs); + goto exit; + } + if (nargs > 3) { + PyErr_Format(PyExc_TypeError, "fcntl expected at most 3 arguments, got %zd", nargs); goto exit; } fd = PyObject_AsFileDescriptor(args[0]); @@ -90,7 +93,7 @@ PyDoc_STRVAR(fcntl_ioctl__doc__, "code."); #define FCNTL_IOCTL_METHODDEF \ - {"ioctl", _PyCFunction_CAST(fcntl_ioctl), METH_FASTCALL, fcntl_ioctl__doc__}, + {"ioctl", (PyCFunction)(void(*)(void))fcntl_ioctl, METH_FASTCALL, fcntl_ioctl__doc__}, static PyObject * fcntl_ioctl_impl(PyObject *module, int fd, unsigned long code, @@ -105,7 +108,12 @@ fcntl_ioctl(PyObject *module, PyObject *const *args, Py_ssize_t nargs) PyObject *ob_arg = NULL; int mutate_arg = 1; - if (!_PyArg_CheckPositional("ioctl", nargs, 2, 4)) { + if (nargs < 2) { + PyErr_Format(PyExc_TypeError, "ioctl expected at least 2 arguments, got %zd", nargs); + goto exit; + } + if (nargs > 4) { + PyErr_Format(PyExc_TypeError, "ioctl expected at most 4 arguments, got %zd", nargs); goto exit; } fd = PyObject_AsFileDescriptor(args[0]); @@ -113,7 +121,7 @@ fcntl_ioctl(PyObject *module, PyObject *const *args, Py_ssize_t nargs) goto exit; } if (!PyLong_Check(args[1])) { - _PyArg_BadArgument("ioctl", "argument 2", "int", args[1]); + PyErr_Format(PyExc_TypeError, "ioctl() argument 2 must be int, not %T", args[1]); goto exit; } { @@ -159,7 +167,7 @@ PyDoc_STRVAR(fcntl_flock__doc__, "function is emulated using fcntl())."); #define FCNTL_FLOCK_METHODDEF \ - {"flock", _PyCFunction_CAST(fcntl_flock), METH_FASTCALL, fcntl_flock__doc__}, + {"flock", (PyCFunction)(void(*)(void))fcntl_flock, METH_FASTCALL, fcntl_flock__doc__}, static PyObject * fcntl_flock_impl(PyObject *module, int fd, int code); @@ -171,7 +179,8 @@ fcntl_flock(PyObject *module, PyObject *const *args, Py_ssize_t nargs) int fd; int code; - if (!_PyArg_CheckPositional("flock", nargs, 2, 2)) { + if (nargs != 2) { + PyErr_Format(PyExc_TypeError, "flock expected 2 arguments, got %zd", nargs); goto exit; } fd = PyObject_AsFileDescriptor(args[0]); @@ -216,7 +225,7 @@ PyDoc_STRVAR(fcntl_lockf__doc__, " 2 - relative to the end of the file (SEEK_END)"); #define FCNTL_LOCKF_METHODDEF \ - {"lockf", _PyCFunction_CAST(fcntl_lockf), METH_FASTCALL, fcntl_lockf__doc__}, + {"lockf", (PyCFunction)(void(*)(void))fcntl_lockf, METH_FASTCALL, fcntl_lockf__doc__}, static PyObject * fcntl_lockf_impl(PyObject *module, int fd, int code, PyObject *lenobj, @@ -232,7 +241,12 @@ fcntl_lockf(PyObject *module, PyObject *const *args, Py_ssize_t nargs) PyObject *startobj = NULL; int whence = 0; - if (!_PyArg_CheckPositional("lockf", nargs, 2, 5)) { + if (nargs < 2) { + PyErr_Format(PyExc_TypeError, "lockf expected at least 2 arguments, got %zd", nargs); + goto exit; + } + if (nargs > 5) { + PyErr_Format(PyExc_TypeError, "lockf expected at most 5 arguments, got %zd", nargs); goto exit; } fd = PyObject_AsFileDescriptor(args[0]); @@ -264,4 +278,4 @@ fcntl_lockf(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=dd541b9ef6b49d90 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=19e74f78a84faa81 input=a9049054013a1b77]*/ diff --git a/Modules/fcntlmodule.c b/Modules/fcntlmodule.c index b3cf88daa86f78..904427722751e3 100644 --- a/Modules/fcntlmodule.c +++ b/Modules/fcntlmodule.c @@ -2,8 +2,8 @@ // Need limited C API version 3.13 for PyLong_AsInt() #include "pyconfig.h" // Py_GIL_DISABLED -#ifndef Py_BUILD_CORE_BUILTIN -# define Py_BUILD_CORE_MODULE 1 +#ifndef Py_GIL_DISABLED +# define Py_LIMITED_API 0x030e0000 #endif #include "Python.h" From be4f26beb7bdab07985753dc3177c9628d7178a6 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 29 Apr 2025 21:35:54 +0300 Subject: [PATCH 4/9] Fix merge error. --- Lib/test/clinic.test.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/Lib/test/clinic.test.c b/Lib/test/clinic.test.c index bb1bf99c36721c..dc5b4b27a07f99 100644 --- a/Lib/test/clinic.test.c +++ b/Lib/test/clinic.test.c @@ -1448,6 +1448,7 @@ test_unsigned_long_converter(PyObject *module, PyObject *const *args, Py_ssize_t { Py_ssize_t _bytes = PyLong_AsNativeBytes(args[2], &c, sizeof(unsigned long), Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | Py_ASNATIVEBYTES_UNSIGNED_BUFFER); if (_bytes < 0) { goto exit; @@ -1470,10 +1471,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=f2c4399c71720c4d input=f450d94cae1ef73b]*/ -======= -/*[clinic end generated code: output=d74eed227d77a31b input=f450d94cae1ef73b]*/ ->>>>>>> main +/*[clinic end generated code: output=1bbf5620093cc914 input=f450d94cae1ef73b]*/ /*[clinic input] @@ -1580,6 +1578,7 @@ test_unsigned_long_long_converter(PyObject *module, PyObject *const *args, Py_ss { Py_ssize_t _bytes = PyLong_AsNativeBytes(args[2], &c, sizeof(unsigned long long), Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | Py_ASNATIVEBYTES_UNSIGNED_BUFFER); if (_bytes < 0) { goto exit; @@ -1604,10 +1603,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=ad412e0d5dd94ac4 input=a15115dc41866ff4]*/ -======= -/*[clinic end generated code: output=5ca4e4dfb3db644b input=a15115dc41866ff4]*/ ->>>>>>> main +/*[clinic end generated code: output=582a6623dc845824 input=a15115dc41866ff4]*/ /*[clinic input] From bb2ef60118b0d3e4e2b2eed21c7ecdd5f9076647 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 13 May 2025 17:34:36 +0300 Subject: [PATCH 5/9] Move the What's New entry. --- Doc/whatsnew/3.14.rst | 7 ------- Doc/whatsnew/3.15.rst | 7 ++++++- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 6f6fdb0176770b..11361289874c9d 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -2743,13 +2743,6 @@ Deprecated or a :term:`borrowed reference`. (Contributed by Serhiy Storchaka in :gh:`50333`.) -* For unsigned integer formats in :c:func:`PyArg_ParseTuple`, - accepting Python integers with value that is larger than - the maximal value the corresponding C type or less than - the minimal value for the corresponding signed integer type - is now deprecated. - (Contributed by Serhiy Storchaka in :gh:`132629`.) - * The previously undocumented function :c:func:`PySequence_In` is :term:`soft deprecated`. Use :c:func:`PySequence_Contains` instead. (Contributed by Yuki Kobayashi in :gh:`127896`.) diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 8cf5238e6cc49a..3e77d7f92a78ae 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -180,7 +180,12 @@ Porting to Python 3.15 Deprecated C APIs ----------------- -* TODO +* For unsigned integer formats in :c:func:`PyArg_ParseTuple`, + accepting Python integers with value that is larger than + the maximal value the corresponding C type or less than + the minimal value for the corresponding signed integer type + is now deprecated. + (Contributed by Serhiy Storchaka in :gh:`132629`.) .. Add C API deprecations above alphabetically, not here at the end. From 23b4c083afb0deb555b185daef75d121cf9e9bcb Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 13 May 2025 18:20:17 +0300 Subject: [PATCH 6/9] Factorize the Argument Clinic code. --- Tools/clinic/libclinic/converters.py | 164 +++++++-------------------- 1 file changed, 39 insertions(+), 125 deletions(-) diff --git a/Tools/clinic/libclinic/converters.py b/Tools/clinic/libclinic/converters.py index b7bf642d6fb4b2..6e89e8de7cccf1 100644 --- a/Tools/clinic/libclinic/converters.py +++ b/Tools/clinic/libclinic/converters.py @@ -18,6 +18,7 @@ class BaseUnsignedIntConverter(CConverter): + bitwise = False def use_converter(self) -> None: if self.converter: @@ -25,6 +26,38 @@ def use_converter(self) -> None: f'{self.converter}()') def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: + if self.bitwise: + result = self.format_code(""" + {{{{ + Py_ssize_t _bytes = PyLong_AsNativeBytes({argname}, &{paramname}, sizeof({type}), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) {{{{ + goto exit; + }}}} + if ((size_t)_bytes > sizeof({type})) {{{{ + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + {{{{ + goto exit; + }}}} + }}}} + }}}} + """, + argname=argname, + type=self.type, + bad_argument=self.bad_argument(displayname, 'int', limited_capi=limited_capi)) + if self.format_unit in ('k', 'K'): + result = self.format_code(""" + if (!PyIndex_Check({argname})) {{{{ + {bad_argument} + goto exit; + }}}}""", + argname=argname, + bad_argument=self.bad_argument(displayname, 'int', limited_capi=limited_capi)) + result + return result + if not limited_capi: return super().parse_arg(argname, displayname, limited_capi=limited_capi) return self.format_code(""" @@ -172,13 +205,14 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st @add_legacy_c_converter('B', bitwise=True) -class unsigned_char_converter(CConverter): +class unsigned_char_converter(BaseUnsignedIntConverter): type = 'unsigned char' default_type = int format_unit = 'b' c_ignored_default = "'\0'" def converter_init(self, *, bitwise: bool = False) -> None: + self.bitwise = bitwise if bitwise: self.format_unit = 'B' @@ -206,26 +240,6 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st }}}} """, argname=argname) - elif self.format_unit == 'B': - return self.format_code(""" - {{{{ - Py_ssize_t _bytes = PyLong_AsNativeBytes({argname}, &{paramname}, sizeof(unsigned char), - Py_ASNATIVEBYTES_NATIVE_ENDIAN | - Py_ASNATIVEBYTES_ALLOW_INDEX | - Py_ASNATIVEBYTES_UNSIGNED_BUFFER); - if (_bytes < 0) {{{{ - goto exit; - }}}} - if ((size_t)_bytes > sizeof(unsigned char)) {{{{ - if (PyErr_WarnEx(PyExc_DeprecationWarning, - "integer value out of range", 1) < 0) - {{{{ - goto exit; - }}}} - }}}} - }}}} - """, - argname=argname) return super().parse_arg(argname, displayname, limited_capi=limited_capi) @@ -272,34 +286,12 @@ class unsigned_short_converter(BaseUnsignedIntConverter): c_ignored_default = "0" def converter_init(self, *, bitwise: bool = False) -> None: + self.bitwise = bitwise if bitwise: self.format_unit = 'H' else: self.converter = '_PyLong_UnsignedShort_Converter' - def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: - if self.format_unit == 'H': - return self.format_code(""" - {{{{ - Py_ssize_t _bytes = PyLong_AsNativeBytes({argname}, &{paramname}, sizeof(unsigned short), - Py_ASNATIVEBYTES_NATIVE_ENDIAN | - Py_ASNATIVEBYTES_ALLOW_INDEX | - Py_ASNATIVEBYTES_UNSIGNED_BUFFER); - if (_bytes < 0) {{{{ - goto exit; - }}}} - if ((size_t)_bytes > sizeof(unsigned short)) {{{{ - if (PyErr_WarnEx(PyExc_DeprecationWarning, - "integer value out of range", 1) < 0) - {{{{ - goto exit; - }}}} - }}}} - }}}} - """, - argname=argname) - return super().parse_arg(argname, displayname, limited_capi=limited_capi) - @add_legacy_c_converter('C', accept={str}) class int_converter(CConverter): @@ -355,34 +347,12 @@ class unsigned_int_converter(BaseUnsignedIntConverter): c_ignored_default = "0" def converter_init(self, *, bitwise: bool = False) -> None: + self.bitwise = bitwise if bitwise: self.format_unit = 'I' else: self.converter = '_PyLong_UnsignedInt_Converter' - def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: - if self.format_unit == 'I': - return self.format_code(""" - {{{{ - Py_ssize_t _bytes = PyLong_AsNativeBytes({argname}, &{paramname}, sizeof(unsigned int), - Py_ASNATIVEBYTES_NATIVE_ENDIAN | - Py_ASNATIVEBYTES_ALLOW_INDEX | - Py_ASNATIVEBYTES_UNSIGNED_BUFFER); - if (_bytes < 0) {{{{ - goto exit; - }}}} - if ((size_t)_bytes > sizeof(unsigned int)) {{{{ - if (PyErr_WarnEx(PyExc_DeprecationWarning, - "integer value out of range", 1) < 0) - {{{{ - goto exit; - }}}} - }}}} - }}}} - """, - argname=argname) - return super().parse_arg(argname, displayname, limited_capi=limited_capi) - class long_converter(CConverter): type = 'long' @@ -408,40 +378,12 @@ class unsigned_long_converter(BaseUnsignedIntConverter): c_ignored_default = "0" def converter_init(self, *, bitwise: bool = False) -> None: + self.bitwise = bitwise if bitwise: self.format_unit = 'k' else: self.converter = '_PyLong_UnsignedLong_Converter' - def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: - if self.format_unit == 'k': - return self.format_code(""" - if (!PyIndex_Check({argname})) {{{{ - {bad_argument} - goto exit; - }}}} - {{{{ - Py_ssize_t _bytes = PyLong_AsNativeBytes({argname}, &{paramname}, sizeof(unsigned long), - Py_ASNATIVEBYTES_NATIVE_ENDIAN | - Py_ASNATIVEBYTES_ALLOW_INDEX | - Py_ASNATIVEBYTES_UNSIGNED_BUFFER); - if (_bytes < 0) {{{{ - goto exit; - }}}} - if ((size_t)_bytes > sizeof(unsigned long)) {{{{ - if (PyErr_WarnEx(PyExc_DeprecationWarning, - "integer value out of range", 1) < 0) - {{{{ - goto exit; - }}}} - }}}} - }}}} - """, - argname=argname, - bad_argument=self.bad_argument(displayname, 'int', limited_capi=limited_capi), - ) - return super().parse_arg(argname, displayname, limited_capi=limited_capi) - class long_long_converter(CConverter): type = 'long long' @@ -467,40 +409,12 @@ class unsigned_long_long_converter(BaseUnsignedIntConverter): c_ignored_default = "0" def converter_init(self, *, bitwise: bool = False) -> None: + self.bitwise = bitwise if bitwise: self.format_unit = 'K' else: self.converter = '_PyLong_UnsignedLongLong_Converter' - def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: - if self.format_unit == 'K': - return self.format_code(""" - if (!PyIndex_Check({argname})) {{{{ - {bad_argument} - goto exit; - }}}} - {{{{ - Py_ssize_t _bytes = PyLong_AsNativeBytes({argname}, &{paramname}, sizeof(unsigned long long), - Py_ASNATIVEBYTES_NATIVE_ENDIAN | - Py_ASNATIVEBYTES_ALLOW_INDEX | - Py_ASNATIVEBYTES_UNSIGNED_BUFFER); - if (_bytes < 0) {{{{ - goto exit; - }}}} - if ((size_t)_bytes > sizeof(unsigned long long)) {{{{ - if (PyErr_WarnEx(PyExc_DeprecationWarning, - "integer value out of range", 1) < 0) - {{{{ - goto exit; - }}}} - }}}} - }}}} - """, - argname=argname, - bad_argument=self.bad_argument(displayname, 'int', limited_capi=limited_capi), - ) - return super().parse_arg(argname, displayname, limited_capi=limited_capi) - class Py_ssize_t_converter(CConverter): type = 'Py_ssize_t' From d3c476523f76dce7c96604310f459e5dcec913a3 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 23 May 2025 20:12:43 +0300 Subject: [PATCH 7/9] Apply suggestions from code review Co-authored-by: Victor Stinner --- Doc/c-api/arg.rst | 4 ++-- .../next/C_API/2025-04-17-12-37-27.gh-issue-132629.01ArwX.rst | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/c-api/arg.rst b/Doc/c-api/arg.rst index c89c4931f11090..83571f796dd2aa 100644 --- a/Doc/c-api/arg.rst +++ b/Doc/c-api/arg.rst @@ -244,7 +244,7 @@ is out of range for the C type. For unsigned integer formats, the most significant bits are silently truncated when the receiving field is too small to receive the value, and :exc:`DeprecationWarning` is emitted when -the value is larger than the maximal value the C type or less than the +the value is larger than the maximal value for the corresponding C type or less than the minimal value for the corresponding signed integer type. ``b`` (:class:`int`) [unsigned char] @@ -313,7 +313,7 @@ minimal value for the corresponding signed integer type. For unsigned integer formats ``B``, ``H``, ``I``, ``k`` and ``K``, :exc:`DeprecationWarning` is emitted when the value is larger than - the maximal value the C type or less than the minimal value for + the maximal value for the corresponding C type or less than the minimal value for the corresponding signed integer type. diff --git a/Misc/NEWS.d/next/C_API/2025-04-17-12-37-27.gh-issue-132629.01ArwX.rst b/Misc/NEWS.d/next/C_API/2025-04-17-12-37-27.gh-issue-132629.01ArwX.rst index a722c3c74d236c..08c6d1ac402d25 100644 --- a/Misc/NEWS.d/next/C_API/2025-04-17-12-37-27.gh-issue-132629.01ArwX.rst +++ b/Misc/NEWS.d/next/C_API/2025-04-17-12-37-27.gh-issue-132629.01ArwX.rst @@ -1,4 +1,4 @@ For unsigned integer formats in :c:func:`PyArg_ParseTuple`, accepting Python -integers with value that is larger than the maximal value the corresponding +integers with value that is larger than the maximal value for the corresponding C type or less than the minimal value for the corresponding signed integer type is now deprecated. From f51a0528b0d425b54e8011d5380c8a3079acfcab Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 23 May 2025 20:23:55 +0300 Subject: [PATCH 8/9] Update documentation. --- Doc/c-api/arg.rst | 8 ++++---- Doc/whatsnew/3.15.rst | 7 +++---- .../C_API/2025-04-17-12-37-27.gh-issue-132629.01ArwX.rst | 6 +++--- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/Doc/c-api/arg.rst b/Doc/c-api/arg.rst index 83571f796dd2aa..3a3ffac8b5e24d 100644 --- a/Doc/c-api/arg.rst +++ b/Doc/c-api/arg.rst @@ -244,8 +244,8 @@ is out of range for the C type. For unsigned integer formats, the most significant bits are silently truncated when the receiving field is too small to receive the value, and :exc:`DeprecationWarning` is emitted when -the value is larger than the maximal value for the corresponding C type or less than the -minimal value for the corresponding signed integer type. +the value is larger than the maximal value for the C type or less than +the minimal value for the corresponding signed integer type of the same size. ``b`` (:class:`int`) [unsigned char] Convert a nonnegative Python integer to an unsigned tiny integer, stored in a C @@ -313,8 +313,8 @@ minimal value for the corresponding signed integer type. For unsigned integer formats ``B``, ``H``, ``I``, ``k`` and ``K``, :exc:`DeprecationWarning` is emitted when the value is larger than - the maximal value for the corresponding C type or less than the minimal value for - the corresponding signed integer type. + the maximal value for the C type or less than the minimal value for + the corresponding signed integer type of the same size. Other objects diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index a3faea681fc6d6..be14ae3224a521 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -216,10 +216,9 @@ Deprecated C APIs ----------------- * For unsigned integer formats in :c:func:`PyArg_ParseTuple`, - accepting Python integers with value that is larger than - the maximal value the corresponding C type or less than - the minimal value for the corresponding signed integer type - is now deprecated. + accepting Python integers with value that is larger than the maximal value + for the C type or less than the minimal value for the corresponding + signed integer type of the same size is now deprecated. (Contributed by Serhiy Storchaka in :gh:`132629`.) .. Add C API deprecations above alphabetically, not here at the end. diff --git a/Misc/NEWS.d/next/C_API/2025-04-17-12-37-27.gh-issue-132629.01ArwX.rst b/Misc/NEWS.d/next/C_API/2025-04-17-12-37-27.gh-issue-132629.01ArwX.rst index 08c6d1ac402d25..38b7a0a493e39f 100644 --- a/Misc/NEWS.d/next/C_API/2025-04-17-12-37-27.gh-issue-132629.01ArwX.rst +++ b/Misc/NEWS.d/next/C_API/2025-04-17-12-37-27.gh-issue-132629.01ArwX.rst @@ -1,4 +1,4 @@ For unsigned integer formats in :c:func:`PyArg_ParseTuple`, accepting Python -integers with value that is larger than the maximal value for the corresponding -C type or less than the minimal value for the corresponding signed integer -type is now deprecated. +integers with value that is larger than the maximal value for the C type or +less than the minimal value for the corresponding signed integer type +of the same size is now deprecated. From 5ee148086acda190affff32f447d847eff2a49c0 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 13 Jul 2025 11:15:55 +0300 Subject: [PATCH 9/9] Update Modules/fcntlmodule.c Co-authored-by: Victor Stinner --- Modules/fcntlmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/fcntlmodule.c b/Modules/fcntlmodule.c index bf8bd91c20a051..524eb54b984ca8 100644 --- a/Modules/fcntlmodule.c +++ b/Modules/fcntlmodule.c @@ -1,6 +1,6 @@ /* fcntl module */ -// Need limited C API version 3.13 for PyLong_AsInt() +// Need limited C API version 3.14 for PyLong_AsNativeBytes() in AC code #include "pyconfig.h" // Py_GIL_DISABLED #ifndef Py_GIL_DISABLED # define Py_LIMITED_API 0x030e0000