From 88c5c586708dcff369c49edae947d487a80f0346 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Sun, 14 May 2023 14:45:54 -0400 Subject: [PATCH 01/13] gh-104337: Clarify random.gammavariate doc entry (#104410) * gh-104337: Clarify random.gammavariate doc entry * Fix parameter markup. --- Doc/library/random.rst | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Doc/library/random.rst b/Doc/library/random.rst index c192919ac62e54..76ae97a8be7e63 100644 --- a/Doc/library/random.rst +++ b/Doc/library/random.rst @@ -334,8 +334,10 @@ be found in any statistics text. .. function:: gammavariate(alpha, beta) - Gamma distribution. (*Not* the gamma function!) Conditions on the - parameters are ``alpha > 0`` and ``beta > 0``. + Gamma distribution. (*Not* the gamma function!) The shape and + scale parameters, *alpha* and *beta*, must have positive values. + (Calling conventions vary and some sources define 'beta' + as the inverse of the scale). The probability distribution function is:: @@ -346,7 +348,8 @@ be found in any statistics text. .. function:: gauss(mu=0.0, sigma=1.0) - Normal distribution, also called the Gaussian distribution. *mu* is the mean, + Normal distribution, also called the Gaussian distribution. + *mu* is the mean, and *sigma* is the standard deviation. This is slightly faster than the :func:`normalvariate` function defined below. From fb8739f0b6291fb048a94d6312f59ba4d10a20ca Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Sun, 14 May 2023 19:58:13 +0100 Subject: [PATCH 02/13] gh-98539: Make _SSLTransportProtocol.abort() safe to call when closed (#104474) --- Lib/asyncio/sslproto.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/asyncio/sslproto.py b/Lib/asyncio/sslproto.py index bbf9cad6bc7f86..488e17d8bccd5b 100644 --- a/Lib/asyncio/sslproto.py +++ b/Lib/asyncio/sslproto.py @@ -244,7 +244,8 @@ def abort(self): called with None as its argument. """ self._closed = True - self._ssl_protocol._abort() + if self._ssl_protocol is not None: + self._ssl_protocol._abort() def _force_close(self, exc): self._closed = True From 2cd1c87d2a23ffd00730b5d1648304593530326c Mon Sep 17 00:00:00 2001 From: Kirill Podoprigora Date: Mon, 15 May 2023 00:05:35 +0300 Subject: [PATCH 03/13] gh-104456: Fix ref leak in _ctypes.COMError (#104457) --- Modules/_ctypes/_ctypes.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index f6cda45eaeac27..534ef8c1d6cf8f 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -5476,11 +5476,17 @@ comerror_init(PyObject *self, PyObject *args, PyObject *kwds) return 0; } +static int +comerror_clear(PyObject *self) +{ + return ((PyTypeObject *)PyExc_BaseException)->tp_clear(self); +} + static int comerror_traverse(PyObject *self, visitproc visit, void *arg) { Py_VISIT(Py_TYPE(self)); - return 0; + return ((PyTypeObject *)PyExc_BaseException)->tp_traverse(self, visit, arg); } static void @@ -5488,6 +5494,7 @@ comerror_dealloc(PyObject *self) { PyTypeObject *tp = Py_TYPE(self); PyObject_GC_UnTrack(self); + (void)comerror_clear(self); tp->tp_free(self); Py_DECREF(tp); } @@ -5497,6 +5504,7 @@ static PyType_Slot comerror_slots[] = { {Py_tp_init, comerror_init}, {Py_tp_traverse, comerror_traverse}, {Py_tp_dealloc, comerror_dealloc}, + {Py_tp_clear, comerror_clear}, {0, NULL}, }; From 48b3617de491f00a3bf978b355074cc8e228d61b Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Mon, 15 May 2023 12:44:00 +0900 Subject: [PATCH 04/13] gh-104469 Convert _testcapi/float.c to use AC (gh-104470) --- Modules/_testcapi/clinic/float.c.h | 88 ++++++++++++++++++++++++++++++ Modules/_testcapi/float.c | 54 +++++++++++------- 2 files changed, 123 insertions(+), 19 deletions(-) create mode 100644 Modules/_testcapi/clinic/float.c.h diff --git a/Modules/_testcapi/clinic/float.c.h b/Modules/_testcapi/clinic/float.c.h new file mode 100644 index 00000000000000..c1dff2a9ac5789 --- /dev/null +++ b/Modules/_testcapi/clinic/float.c.h @@ -0,0 +1,88 @@ +/*[clinic input] +preserve +[clinic start generated code]*/ + +#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) +# include "pycore_gc.h" // PyGC_Head +# include "pycore_runtime.h" // _Py_ID() +#endif + + +PyDoc_STRVAR(_testcapi_float_pack__doc__, +"float_pack($module, size, d, le, /)\n" +"--\n" +"\n" +"Test PyFloat_Pack2(), PyFloat_Pack4() and PyFloat_Pack8()"); + +#define _TESTCAPI_FLOAT_PACK_METHODDEF \ + {"float_pack", _PyCFunction_CAST(_testcapi_float_pack), METH_FASTCALL, _testcapi_float_pack__doc__}, + +static PyObject * +_testcapi_float_pack_impl(PyObject *module, int size, double d, int le); + +static PyObject * +_testcapi_float_pack(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + int size; + double d; + int le; + + if (!_PyArg_CheckPositional("float_pack", nargs, 3, 3)) { + goto exit; + } + size = _PyLong_AsInt(args[0]); + if (size == -1 && PyErr_Occurred()) { + goto exit; + } + if (PyFloat_CheckExact(args[1])) { + d = PyFloat_AS_DOUBLE(args[1]); + } + else + { + d = PyFloat_AsDouble(args[1]); + if (d == -1.0 && PyErr_Occurred()) { + goto exit; + } + } + le = _PyLong_AsInt(args[2]); + if (le == -1 && PyErr_Occurred()) { + goto exit; + } + return_value = _testcapi_float_pack_impl(module, size, d, le); + +exit: + return return_value; +} + +PyDoc_STRVAR(_testcapi_float_unpack__doc__, +"float_unpack($module, data, le, /)\n" +"--\n" +"\n" +"Test PyFloat_Unpack2(), PyFloat_Unpack4() and PyFloat_Unpack8()"); + +#define _TESTCAPI_FLOAT_UNPACK_METHODDEF \ + {"float_unpack", _PyCFunction_CAST(_testcapi_float_unpack), METH_FASTCALL, _testcapi_float_unpack__doc__}, + +static PyObject * +_testcapi_float_unpack_impl(PyObject *module, const char *data, + Py_ssize_t data_length, int le); + +static PyObject * +_testcapi_float_unpack(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + const char *data; + Py_ssize_t data_length; + int le; + + if (!_PyArg_ParseStack(args, nargs, "y#i:float_unpack", + &data, &data_length, &le)) { + goto exit; + } + return_value = _testcapi_float_unpack_impl(module, data, data_length, le); + +exit: + return return_value; +} +/*[clinic end generated code: output=083e5df26cd5fbeb input=a9049054013a1b77]*/ diff --git a/Modules/_testcapi/float.c b/Modules/_testcapi/float.c index 26d99d990e0042..33cbda83a81af7 100644 --- a/Modules/_testcapi/float.c +++ b/Modules/_testcapi/float.c @@ -1,18 +1,29 @@ #define PY_SSIZE_T_CLEAN #include "parts.h" +#include "clinic/float.c.h" -// Test PyFloat_Pack2(), PyFloat_Pack4() and PyFloat_Pack8() +/*[clinic input] +module _testcapi +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=6361033e795369fc]*/ + +/*[clinic input] +_testcapi.float_pack + + size: int + d: double + le: int + / + +Test PyFloat_Pack2(), PyFloat_Pack4() and PyFloat_Pack8() +[clinic start generated code]*/ + static PyObject * -test_float_pack(PyObject *self, PyObject *args) +_testcapi_float_pack_impl(PyObject *module, int size, double d, int le) +/*[clinic end generated code: output=7899bd98f8b6cb04 input=52c9115121999c98]*/ { - int size; - double d; - int le; - if (!PyArg_ParseTuple(args, "idi", &size, &d, &le)) { - return NULL; - } switch (size) { case 2: @@ -47,19 +58,24 @@ test_float_pack(PyObject *self, PyObject *args) } -// Test PyFloat_Unpack2(), PyFloat_Unpack4() and PyFloat_Unpack8() +/*[clinic input] +_testcapi.float_unpack + + data: str(accept={robuffer}, zeroes=True) + le: int + / + +Test PyFloat_Unpack2(), PyFloat_Unpack4() and PyFloat_Unpack8() +[clinic start generated code]*/ + static PyObject * -test_float_unpack(PyObject *self, PyObject *args) +_testcapi_float_unpack_impl(PyObject *module, const char *data, + Py_ssize_t data_length, int le) +/*[clinic end generated code: output=617059f889ddbfe4 input=c095e4bb75a696cd]*/ { assert(!PyErr_Occurred()); - const char *data; - Py_ssize_t size; - int le; - if (!PyArg_ParseTuple(args, "y#i", &data, &size, &le)) { - return NULL; - } double d; - switch (size) + switch (data_length) { case 2: d = PyFloat_Unpack2(data, le); @@ -82,8 +98,8 @@ test_float_unpack(PyObject *self, PyObject *args) } static PyMethodDef test_methods[] = { - {"float_pack", test_float_pack, METH_VARARGS, NULL}, - {"float_unpack", test_float_unpack, METH_VARARGS, NULL}, + _TESTCAPI_FLOAT_PACK_METHODDEF + _TESTCAPI_FLOAT_UNPACK_METHODDEF {NULL}, }; From 27d8ecd7f3325a40a967d2d6b6b36b21d5328753 Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Sun, 14 May 2023 21:29:44 -0700 Subject: [PATCH 05/13] gh-101282: move BOLT config after PGO (gh-104493) --- configure | 360 +++++++++++++++++++++++++-------------------------- configure.ac | 124 +++++++++--------- 2 files changed, 242 insertions(+), 242 deletions(-) diff --git a/configure b/configure index c9ea72cf6efacf..4d3520aee43def 100755 --- a/configure +++ b/configure @@ -883,6 +883,11 @@ CFLAGS_NODIST BASECFLAGS CFLAGS_ALIASING OPT +MERGE_FDATA +LLVM_BOLT +ac_ct_READELF +READELF +PREBOLT_RULE LLVM_PROF_FOUND LLVM_PROFDATA LLVM_PROF_ERR @@ -890,11 +895,6 @@ LLVM_PROF_FILE LLVM_PROF_MERGER PGO_PROF_USE_FLAG PGO_PROF_GEN_FLAG -MERGE_FDATA -LLVM_BOLT -ac_ct_READELF -READELF -PREBOLT_RULE LLVM_AR_FOUND LLVM_AR PROFILE_TASK @@ -7903,7 +7903,181 @@ fi LDFLAGS_NODIST="$LDFLAGS_NODIST $LTOFLAGS" fi -# Enable bolt flags +# Enable PGO flags. + + + + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}llvm-profdata", so it can be a program name with args. +set dummy ${ac_tool_prefix}llvm-profdata; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_LLVM_PROFDATA+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $LLVM_PROFDATA in + [\\/]* | ?:[\\/]*) + ac_cv_path_LLVM_PROFDATA="$LLVM_PROFDATA" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in ${llvm_path} +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_LLVM_PROFDATA="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +LLVM_PROFDATA=$ac_cv_path_LLVM_PROFDATA +if test -n "$LLVM_PROFDATA"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LLVM_PROFDATA" >&5 +$as_echo "$LLVM_PROFDATA" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_path_LLVM_PROFDATA"; then + ac_pt_LLVM_PROFDATA=$LLVM_PROFDATA + # Extract the first word of "llvm-profdata", so it can be a program name with args. +set dummy llvm-profdata; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_ac_pt_LLVM_PROFDATA+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $ac_pt_LLVM_PROFDATA in + [\\/]* | ?:[\\/]*) + ac_cv_path_ac_pt_LLVM_PROFDATA="$ac_pt_LLVM_PROFDATA" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in ${llvm_path} +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_ac_pt_LLVM_PROFDATA="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +ac_pt_LLVM_PROFDATA=$ac_cv_path_ac_pt_LLVM_PROFDATA +if test -n "$ac_pt_LLVM_PROFDATA"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_LLVM_PROFDATA" >&5 +$as_echo "$ac_pt_LLVM_PROFDATA" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_pt_LLVM_PROFDATA" = x; then + LLVM_PROFDATA="''" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + LLVM_PROFDATA=$ac_pt_LLVM_PROFDATA + fi +else + LLVM_PROFDATA="$ac_cv_path_LLVM_PROFDATA" +fi + + +if test -n "${LLVM_PROFDATA}" -a -x "${LLVM_PROFDATA}" +then + LLVM_PROF_FOUND="found" +else + LLVM_PROF_FOUND="not-found" +fi +if test "$ac_sys_system" = "Darwin" -a "${LLVM_PROF_FOUND}" = "not-found" +then + found_llvm_profdata=`/usr/bin/xcrun -find llvm-profdata 2>/dev/null` + if test -n "${found_llvm_profdata}" + then + # llvm-profdata isn't directly in $PATH in some cases. + # https://apple.stackexchange.com/questions/197053/ + LLVM_PROFDATA='/usr/bin/xcrun llvm-profdata' + LLVM_PROF_FOUND=found + { $as_echo "$as_me:${as_lineno-$LINENO}: llvm-profdata found via xcrun: ${LLVM_PROFDATA}" >&5 +$as_echo "$as_me: llvm-profdata found via xcrun: ${LLVM_PROFDATA}" >&6;} + fi +fi +LLVM_PROF_ERR=no +case $CC in + *clang*) + # Any changes made here should be reflected in the GCC+Darwin case below + PGO_PROF_GEN_FLAG="-fprofile-instr-generate" + PGO_PROF_USE_FLAG="-fprofile-instr-use=code.profclangd" + LLVM_PROF_MERGER="${LLVM_PROFDATA} merge -output=code.profclangd *.profclangr" + LLVM_PROF_FILE="LLVM_PROFILE_FILE=\"code-%p.profclangr\"" + if test $LLVM_PROF_FOUND = not-found + then + LLVM_PROF_ERR=yes + if test "${REQUIRE_PGO}" = "yes" + then + as_fn_error $? "llvm-profdata is required for a --enable-optimizations build but could not be found." "$LINENO" 5 + fi + fi + ;; + *gcc*) + case $ac_sys_system in + Darwin*) + PGO_PROF_GEN_FLAG="-fprofile-instr-generate" + PGO_PROF_USE_FLAG="-fprofile-instr-use=code.profclangd" + LLVM_PROF_MERGER="${LLVM_PROFDATA} merge -output=code.profclangd *.profclangr" + LLVM_PROF_FILE="LLVM_PROFILE_FILE=\"code-%p.profclangr\"" + if test "${LLVM_PROF_FOUND}" = "not-found" + then + LLVM_PROF_ERR=yes + if test "${REQUIRE_PGO}" = "yes" + then + as_fn_error $? "llvm-profdata is required for a --enable-optimizations build but could not be found." "$LINENO" 5 + fi + fi + ;; + *) + PGO_PROF_GEN_FLAG="-fprofile-generate" + PGO_PROF_USE_FLAG="-fprofile-use -fprofile-correction" + LLVM_PROF_MERGER="true" + LLVM_PROF_FILE="" + ;; + esac + ;; + *icc*) + PGO_PROF_GEN_FLAG="-prof-gen" + PGO_PROF_USE_FLAG="-prof-use" + LLVM_PROF_MERGER="true" + LLVM_PROF_FILE="" + ;; +esac + +# BOLT optimization. Always configured after PGO since it always runs after PGO. Py_BOLT='false' { $as_echo "$as_me:${as_lineno-$LINENO}: checking for --enable-bolt" >&5 $as_echo_n "checking for --enable-bolt... " >&6; } @@ -8300,180 +8474,6 @@ $as_echo "\"Found merge-fdata\"" >&6; } fi fi -# Enable PGO flags. - - - - - - -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}llvm-profdata", so it can be a program name with args. -set dummy ${ac_tool_prefix}llvm-profdata; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_LLVM_PROFDATA+:} false; then : - $as_echo_n "(cached) " >&6 -else - case $LLVM_PROFDATA in - [\\/]* | ?:[\\/]*) - ac_cv_path_LLVM_PROFDATA="$LLVM_PROFDATA" # Let the user override the test with a path. - ;; - *) - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in ${llvm_path} -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_path_LLVM_PROFDATA="$as_dir/$ac_word$ac_exec_ext" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - - ;; -esac -fi -LLVM_PROFDATA=$ac_cv_path_LLVM_PROFDATA -if test -n "$LLVM_PROFDATA"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LLVM_PROFDATA" >&5 -$as_echo "$LLVM_PROFDATA" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$ac_cv_path_LLVM_PROFDATA"; then - ac_pt_LLVM_PROFDATA=$LLVM_PROFDATA - # Extract the first word of "llvm-profdata", so it can be a program name with args. -set dummy llvm-profdata; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_ac_pt_LLVM_PROFDATA+:} false; then : - $as_echo_n "(cached) " >&6 -else - case $ac_pt_LLVM_PROFDATA in - [\\/]* | ?:[\\/]*) - ac_cv_path_ac_pt_LLVM_PROFDATA="$ac_pt_LLVM_PROFDATA" # Let the user override the test with a path. - ;; - *) - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in ${llvm_path} -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_path_ac_pt_LLVM_PROFDATA="$as_dir/$ac_word$ac_exec_ext" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - - ;; -esac -fi -ac_pt_LLVM_PROFDATA=$ac_cv_path_ac_pt_LLVM_PROFDATA -if test -n "$ac_pt_LLVM_PROFDATA"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_LLVM_PROFDATA" >&5 -$as_echo "$ac_pt_LLVM_PROFDATA" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - if test "x$ac_pt_LLVM_PROFDATA" = x; then - LLVM_PROFDATA="''" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - LLVM_PROFDATA=$ac_pt_LLVM_PROFDATA - fi -else - LLVM_PROFDATA="$ac_cv_path_LLVM_PROFDATA" -fi - - -if test -n "${LLVM_PROFDATA}" -a -x "${LLVM_PROFDATA}" -then - LLVM_PROF_FOUND="found" -else - LLVM_PROF_FOUND="not-found" -fi -if test "$ac_sys_system" = "Darwin" -a "${LLVM_PROF_FOUND}" = "not-found" -then - found_llvm_profdata=`/usr/bin/xcrun -find llvm-profdata 2>/dev/null` - if test -n "${found_llvm_profdata}" - then - # llvm-profdata isn't directly in $PATH in some cases. - # https://apple.stackexchange.com/questions/197053/ - LLVM_PROFDATA='/usr/bin/xcrun llvm-profdata' - LLVM_PROF_FOUND=found - { $as_echo "$as_me:${as_lineno-$LINENO}: llvm-profdata found via xcrun: ${LLVM_PROFDATA}" >&5 -$as_echo "$as_me: llvm-profdata found via xcrun: ${LLVM_PROFDATA}" >&6;} - fi -fi -LLVM_PROF_ERR=no -case $CC in - *clang*) - # Any changes made here should be reflected in the GCC+Darwin case below - PGO_PROF_GEN_FLAG="-fprofile-instr-generate" - PGO_PROF_USE_FLAG="-fprofile-instr-use=code.profclangd" - LLVM_PROF_MERGER="${LLVM_PROFDATA} merge -output=code.profclangd *.profclangr" - LLVM_PROF_FILE="LLVM_PROFILE_FILE=\"code-%p.profclangr\"" - if test $LLVM_PROF_FOUND = not-found - then - LLVM_PROF_ERR=yes - if test "${REQUIRE_PGO}" = "yes" - then - as_fn_error $? "llvm-profdata is required for a --enable-optimizations build but could not be found." "$LINENO" 5 - fi - fi - ;; - *gcc*) - case $ac_sys_system in - Darwin*) - PGO_PROF_GEN_FLAG="-fprofile-instr-generate" - PGO_PROF_USE_FLAG="-fprofile-instr-use=code.profclangd" - LLVM_PROF_MERGER="${LLVM_PROFDATA} merge -output=code.profclangd *.profclangr" - LLVM_PROF_FILE="LLVM_PROFILE_FILE=\"code-%p.profclangr\"" - if test "${LLVM_PROF_FOUND}" = "not-found" - then - LLVM_PROF_ERR=yes - if test "${REQUIRE_PGO}" = "yes" - then - as_fn_error $? "llvm-profdata is required for a --enable-optimizations build but could not be found." "$LINENO" 5 - fi - fi - ;; - *) - PGO_PROF_GEN_FLAG="-fprofile-generate" - PGO_PROF_USE_FLAG="-fprofile-use -fprofile-correction" - LLVM_PROF_MERGER="true" - LLVM_PROF_FILE="" - ;; - esac - ;; - *icc*) - PGO_PROF_GEN_FLAG="-prof-gen" - PGO_PROF_USE_FLAG="-prof-use" - LLVM_PROF_MERGER="true" - LLVM_PROF_FILE="" - ;; -esac - # XXX Shouldn't the code above that fiddles with BASECFLAGS and OPT be # merged with this chunk of code? diff --git a/configure.ac b/configure.ac index 10672bd3761da8..0c63cde0363e2e 100644 --- a/configure.ac +++ b/configure.ac @@ -1929,68 +1929,6 @@ if test "$Py_LTO" = 'true' ; then LDFLAGS_NODIST="$LDFLAGS_NODIST $LTOFLAGS" fi -# Enable bolt flags -Py_BOLT='false' -AC_MSG_CHECKING(for --enable-bolt) -AC_ARG_ENABLE(bolt, AS_HELP_STRING( - [--enable-bolt], - [enable usage of the llvm-bolt post-link optimizer (default is no)]), -[ -if test "$enableval" != no -then - Py_BOLT='true' - AC_MSG_RESULT(yes); -else - Py_BOLT='false' - AC_MSG_RESULT(no); -fi], -[AC_MSG_RESULT(no)]) - -AC_SUBST(PREBOLT_RULE) -if test "$Py_BOLT" = 'true' ; then - PREBOLT_RULE="${DEF_MAKE_ALL_RULE}" - DEF_MAKE_ALL_RULE="bolt-opt" - DEF_MAKE_RULE="build_all" - - AC_SUBST(READELF) - AC_CHECK_TOOLS(READELF, [readelf], "notfound") - if test "$READELF" == "notfound" - then - AC_MSG_ERROR([readelf is required for a --enable-bolt build but could not be found.]) - fi - - # -fno-reorder-blocks-and-partition is required for bolt to work. - # Possibly GCC only. - AX_CHECK_COMPILE_FLAG([-fno-reorder-blocks-and-partition],[ - CFLAGS_NODIST="$CFLAGS_NODIST -fno-reorder-blocks-and-partition" - ]) - - # These flags are required for bolt to work: - LDFLAGS_NODIST="$LDFLAGS_NODIST -Wl,--emit-relocs" - - # These flags are required to get good performance from bolt: - CFLAGS_NODIST="$CFLAGS_NODIST -fno-pie" - # We want to add these no-pie flags to linking executables but not shared libraries: - LINKCC="$LINKCC -fno-pie -no-pie" - AC_SUBST(LLVM_BOLT) - AC_PATH_TOOL(LLVM_BOLT, llvm-bolt, '', ${llvm_path}) - if test -n "${LLVM_BOLT}" -a -x "${LLVM_BOLT}" - then - AC_MSG_RESULT("Found llvm-bolt") - else - AC_MSG_ERROR([llvm-bolt is required for a --enable-bolt build but could not be found.]) - fi - - AC_SUBST(MERGE_FDATA) - AC_PATH_TOOL(MERGE_FDATA, merge-fdata, '', ${llvm_path}) - if test -n "${MERGE_FDATA}" -a -x "${MERGE_FDATA}" - then - AC_MSG_RESULT("Found merge-fdata") - else - AC_MSG_ERROR([merge-fdata is required for a --enable-bolt build but could not be found.]) - fi -fi - # Enable PGO flags. AC_SUBST(PGO_PROF_GEN_FLAG) AC_SUBST(PGO_PROF_USE_FLAG) @@ -2067,6 +2005,68 @@ case $CC in ;; esac +# BOLT optimization. Always configured after PGO since it always runs after PGO. +Py_BOLT='false' +AC_MSG_CHECKING(for --enable-bolt) +AC_ARG_ENABLE(bolt, AS_HELP_STRING( + [--enable-bolt], + [enable usage of the llvm-bolt post-link optimizer (default is no)]), +[ +if test "$enableval" != no +then + Py_BOLT='true' + AC_MSG_RESULT(yes); +else + Py_BOLT='false' + AC_MSG_RESULT(no); +fi], +[AC_MSG_RESULT(no)]) + +AC_SUBST(PREBOLT_RULE) +if test "$Py_BOLT" = 'true' ; then + PREBOLT_RULE="${DEF_MAKE_ALL_RULE}" + DEF_MAKE_ALL_RULE="bolt-opt" + DEF_MAKE_RULE="build_all" + + AC_SUBST(READELF) + AC_CHECK_TOOLS(READELF, [readelf], "notfound") + if test "$READELF" == "notfound" + then + AC_MSG_ERROR([readelf is required for a --enable-bolt build but could not be found.]) + fi + + # -fno-reorder-blocks-and-partition is required for bolt to work. + # Possibly GCC only. + AX_CHECK_COMPILE_FLAG([-fno-reorder-blocks-and-partition],[ + CFLAGS_NODIST="$CFLAGS_NODIST -fno-reorder-blocks-and-partition" + ]) + + # These flags are required for bolt to work: + LDFLAGS_NODIST="$LDFLAGS_NODIST -Wl,--emit-relocs" + + # These flags are required to get good performance from bolt: + CFLAGS_NODIST="$CFLAGS_NODIST -fno-pie" + # We want to add these no-pie flags to linking executables but not shared libraries: + LINKCC="$LINKCC -fno-pie -no-pie" + AC_SUBST(LLVM_BOLT) + AC_PATH_TOOL(LLVM_BOLT, llvm-bolt, '', ${llvm_path}) + if test -n "${LLVM_BOLT}" -a -x "${LLVM_BOLT}" + then + AC_MSG_RESULT("Found llvm-bolt") + else + AC_MSG_ERROR([llvm-bolt is required for a --enable-bolt build but could not be found.]) + fi + + AC_SUBST(MERGE_FDATA) + AC_PATH_TOOL(MERGE_FDATA, merge-fdata, '', ${llvm_path}) + if test -n "${MERGE_FDATA}" -a -x "${MERGE_FDATA}" + then + AC_MSG_RESULT("Found merge-fdata") + else + AC_MSG_ERROR([merge-fdata is required for a --enable-bolt build but could not be found.]) + fi +fi + # XXX Shouldn't the code above that fiddles with BASECFLAGS and OPT be # merged with this chunk of code? From 146106a0f1cc61815fa33f0d3f808a3e3e3275be Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Mon, 15 May 2023 10:02:44 +0200 Subject: [PATCH 06/13] gh-104487: PYTHON_FOR_REGEN must be minimum Python 3.10 (#104488) Also include Python 3.12 in the list of accepted versions. --- Doc/whatsnew/3.12.rst | 2 ++ .../next/Build/2023-05-15-09-34-08.gh-issue-99017.nToOQu.rst | 1 + configure | 2 +- configure.ac | 2 +- 4 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Build/2023-05-15-09-34-08.gh-issue-99017.nToOQu.rst diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index dc1178811e75cc..3e55b3fa0f4734 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -1204,6 +1204,8 @@ Build Changes (Contributed by Zhang Na in :gh:`90656`.) +* ``PYTHON_FOR_REGEN`` now require Python 3.10 or newer. + C API Changes ============= diff --git a/Misc/NEWS.d/next/Build/2023-05-15-09-34-08.gh-issue-99017.nToOQu.rst b/Misc/NEWS.d/next/Build/2023-05-15-09-34-08.gh-issue-99017.nToOQu.rst new file mode 100644 index 00000000000000..a3517ac0204b1d --- /dev/null +++ b/Misc/NEWS.d/next/Build/2023-05-15-09-34-08.gh-issue-99017.nToOQu.rst @@ -0,0 +1 @@ +``PYTHON_FOR_REGEN`` now require Python 3.10 or newer. diff --git a/configure b/configure index 4d3520aee43def..7aad4fe89e3cbf 100755 --- a/configure +++ b/configure @@ -3359,7 +3359,7 @@ fi -for ac_prog in python$PACKAGE_VERSION python3.11 python3.10 python3.9 python3.8 python3.7 python3.6 python3 python +for ac_prog in python$PACKAGE_VERSION python3.12 python3.11 python3.10 python3 python do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 diff --git a/configure.ac b/configure.ac index 0c63cde0363e2e..115998e0753b26 100644 --- a/configure.ac +++ b/configure.ac @@ -202,7 +202,7 @@ AC_SUBST([FREEZE_MODULE_DEPS]) AC_SUBST([PYTHON_FOR_BUILD_DEPS]) AC_CHECK_PROGS([PYTHON_FOR_REGEN], - [python$PACKAGE_VERSION python3.11 python3.10 python3.9 python3.8 python3.7 python3.6 python3 python], + [python$PACKAGE_VERSION python3.12 python3.11 python3.10 python3 python], [python3]) AC_SUBST(PYTHON_FOR_REGEN) From b15a1a6ac6ea0d7792036e639e90f0e51400c2ee Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Mon, 15 May 2023 09:12:52 +0100 Subject: [PATCH 07/13] gh-67056: document that registering/unregistering an atexit func from within an atexit func is undefined (#104473) --- Doc/library/atexit.rst | 3 +++ .../2023-05-14-12-11-28.gh-issue-67056.nVC2Rf.rst | 2 ++ 2 files changed, 5 insertions(+) create mode 100644 Misc/NEWS.d/next/Documentation/2023-05-14-12-11-28.gh-issue-67056.nVC2Rf.rst diff --git a/Doc/library/atexit.rst b/Doc/library/atexit.rst index f7f038107d11fe..a2bd85b31c9a8d 100644 --- a/Doc/library/atexit.rst +++ b/Doc/library/atexit.rst @@ -20,6 +20,9 @@ at interpreter termination time they will be run in the order ``C``, ``B``, program is killed by a signal not handled by Python, when a Python fatal internal error is detected, or when :func:`os._exit` is called. +**Note:** The effect of registering or unregistering functions from within +a cleanup function is undefined. + .. versionchanged:: 3.7 When used with C-API subinterpreters, registered functions are local to the interpreter they were registered in. diff --git a/Misc/NEWS.d/next/Documentation/2023-05-14-12-11-28.gh-issue-67056.nVC2Rf.rst b/Misc/NEWS.d/next/Documentation/2023-05-14-12-11-28.gh-issue-67056.nVC2Rf.rst new file mode 100644 index 00000000000000..2c6ef17810723d --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2023-05-14-12-11-28.gh-issue-67056.nVC2Rf.rst @@ -0,0 +1,2 @@ +Document that the effect of registering or unregistering an :mod:`atexit` +cleanup function from within a registered cleanup function is undefined. From a6bcc8fb92ffb75bb1907cc568ba9fff516979c3 Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Mon, 15 May 2023 01:48:34 -0700 Subject: [PATCH 08/13] gh-104490: Consistently define phony make targets (#104491) By convention make targets that don't refer to a file have a dependency on the fake .PHONY target/file. This ensures that these targets are always evaluated because there is no rule to create a .PHONY file and that will force make to think the rule is out of date and needs to be rebuilt. This commit consistently associates virtual targets with .PHONY by declaring the .PHONY dependency immediately above the make rule. This should avoid race conditions and avoidable rebuilds across multiple make invocations. --- Makefile.pre.in | 97 +++++++++++++++---- ...-05-14-19-00-19.gh-issue-104490.1tA4AF.rst | 1 + 2 files changed, 78 insertions(+), 20 deletions(-) create mode 100644 Misc/NEWS.d/next/Build/2023-05-14-19-00-19.gh-issue-104490.1tA4AF.rst diff --git a/Makefile.pre.in b/Makefile.pre.in index 52cafabd1ab999..7c44b7be5dbe67 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -603,12 +603,21 @@ LIBHACL_SHA2_HEADERS= \ # Default target all: @DEF_MAKE_ALL_RULE@ + +# First target in Makefile is implicit default. So .PHONY needs to come after +# all. +.PHONY: all + +.PHONY: build_all build_all: check-clean-src $(BUILDPYTHON) platform sharedmods \ gdbhooks Programs/_testembed scripts checksharedmods rundsymutil + +.PHONY: build_wasm build_wasm: check-clean-src $(BUILDPYTHON) platform sharedmods \ python-config checksharedmods # Check that the source is clean when building out of source. +.PHONY: check-clean-src check-clean-src: @if test -n "$(VPATH)" -a \( \ -f "$(srcdir)/Programs/python.o" \ @@ -652,23 +661,28 @@ profile-run-stamp: # to record its completion and avoid re-running it. touch $@ +.PHONY: build_all_generate_profile build_all_generate_profile: $(MAKE) @DEF_MAKE_RULE@ CFLAGS_NODIST="$(CFLAGS_NODIST) $(PGO_PROF_GEN_FLAG)" LDFLAGS_NODIST="$(LDFLAGS_NODIST) $(PGO_PROF_GEN_FLAG)" LIBS="$(LIBS)" +.PHONY: run_profile_task run_profile_task: @ # FIXME: can't run for a cross build $(LLVM_PROF_FILE) $(RUNSHARED) ./$(BUILDPYTHON) $(PROFILE_TASK) || true +.PHONY: build_all_merge_profile build_all_merge_profile: $(LLVM_PROF_MERGER) # Compile Python binary with profile guided optimization. # To force re-running of the profile task, remove the profile-run-stamp file. +.PHONY: profile-opt profile-opt: profile-run-stamp @echo "Rebuilding with profile guided optimizations:" -rm -f profile-clean-stamp $(MAKE) @DEF_MAKE_RULE@ CFLAGS_NODIST="$(CFLAGS_NODIST) $(PGO_PROF_USE_FLAG)" LDFLAGS_NODIST="$(LDFLAGS_NODIST)" +.PHONY: bolt-opt bolt-opt: @PREBOLT_RULE@ rm -f *.fdata @if $(READELF) -p .note.bolt_info $(BUILDPYTHON) | grep BOLT > /dev/null; then\ @@ -685,12 +699,13 @@ bolt-opt: @PREBOLT_RULE@ # Compile and run with gcov -.PHONY=coverage coverage-lcov coverage-report +.PHONY: coverage coverage: @echo "Building with support for coverage checking:" $(MAKE) clean $(MAKE) @DEF_MAKE_RULE@ CFLAGS="$(CFLAGS) -O0 -pg --coverage" LDFLAGS="$(LDFLAGS) --coverage" +.PHONY: coverage-lcov coverage-lcov: @echo "Creating Coverage HTML report with LCOV:" @rm -f $(COVERAGE_INFO) @@ -722,6 +737,7 @@ coverage-lcov: @echo # Force regeneration of parser and frozen modules +.PHONY: coverage-report coverage-report: regen-token regen-frozen @ # build with coverage info $(MAKE) coverage @@ -731,7 +747,7 @@ coverage-report: regen-token regen-frozen $(MAKE) coverage-lcov # Run "Argument Clinic" over all source files -.PHONY=clinic +.PHONY: clinic clinic: check-clean-src $(srcdir)/Modules/_blake2/blake2s_impl.c $(PYTHON_FOR_REGEN) $(srcdir)/Tools/clinic/clinic.py --make --srcdir $(srcdir) $(PYTHON_FOR_REGEN) $(srcdir)/Tools/build/generate_global_objects.py @@ -796,6 +812,7 @@ Modules/python.exp: $(LIBRARY) # # Distributors are likely to want to install this somewhere else e.g. relative # to the stripped DWARF data for the shared library. +.PHONY: gdbhooks gdbhooks: $(BUILDPYTHON)-gdb.py SRC_GDB_HOOKS=$(srcdir)/Tools/gdb/libpython.py @@ -936,6 +953,7 @@ $(LIBHACL_SHA2_A): $(LIBHACL_SHA2_OBJS) # create relative links from build/lib.platform/egg.so to Modules/egg.so # pybuilddir.txt is created too late. We cannot use it in Makefile # targets. ln --relative is not portable. +.PHONY: sharedmods sharedmods: $(SHAREDMODS) pybuilddir.txt @target=`cat pybuilddir.txt`; \ $(MKDIR_P) $$target; \ @@ -946,9 +964,11 @@ sharedmods: $(SHAREDMODS) pybuilddir.txt done # dependency on BUILDPYTHON ensures that the target is run last +.PHONY: checksharedmods checksharedmods: sharedmods $(PYTHON_FOR_BUILD_DEPS) $(BUILDPYTHON) @$(RUNSHARED) $(PYTHON_FOR_BUILD) $(srcdir)/Tools/build/check_extension_modules.py +.PHONY: rundsymutil rundsymutil: sharedmods $(PYTHON_FOR_BUILD_DEPS) $(BUILDPYTHON) @if [ ! -z $(DSYMUTIL) ] ; then \ echo $(DSYMUTIL_PATH) $(BUILDPYTHON); \ @@ -1252,20 +1272,24 @@ regen-global-objects: $(srcdir)/Tools/build/generate_global_objects.py ############################################################################ # ABI +.PHONY: regen-abidump regen-abidump: all @$(MKDIR_P) $(srcdir)/Doc/data/ abidw "libpython$(LDVERSION).so" --no-architecture --out-file $(srcdir)/Doc/data/python$(LDVERSION).abi.new @$(UPDATE_FILE) --create $(srcdir)/Doc/data/python$(LDVERSION).abi $(srcdir)/Doc/data/python$(LDVERSION).abi.new +.PHONY: check-abidump check-abidump: all abidiff $(srcdir)/Doc/data/python$(LDVERSION).abi "libpython$(LDVERSION).so" --drop-private-types --no-architecture --no-added-syms +.PHONY: regen-limited-abi regen-limited-abi: all $(RUNSHARED) ./$(BUILDPYTHON) $(srcdir)/Tools/build/stable_abi.py --generate-all $(srcdir)/Misc/stable_abi.toml ############################################################################ # Regenerate all generated files +.PHONY: regen-all regen-all: regen-cases regen-opcode regen-opcode-targets regen-typeslots \ regen-token regen-ast regen-keyword regen-sre regen-frozen clinic \ regen-pegen-metaparser regen-pegen regen-test-frozenmain \ @@ -1351,7 +1375,7 @@ regen-pegen: -o $(srcdir)/Parser/parser.new.c $(UPDATE_FILE) $(srcdir)/Parser/parser.c $(srcdir)/Parser/parser.new.c -.PHONY=regen-ast +.PHONY: regen-ast regen-ast: # Regenerate 3 files using using Parser/asdl_c.py: # - Include/internal/pycore_ast.h @@ -1424,6 +1448,7 @@ regen-stdlib-module-names: all Programs/_testembed > $(srcdir)/Python/stdlib_module_names.h.new $(UPDATE_FILE) $(srcdir)/Python/stdlib_module_names.h $(srcdir)/Python/stdlib_module_names.h.new +.PHONY: regen-sre regen-sre: # Regenerate Modules/_sre/sre_constants.h and Modules/_sre/sre_targets.h # from Lib/re/_constants.py using Tools/build/generate_sre_constants.py @@ -1762,15 +1787,15 @@ TESTPYTHON= $(RUNSHARED) $(PYTHON_FOR_BUILD) $(TESTPYTHONOPTS) TESTRUNNER= $(TESTPYTHON) $(srcdir)/Tools/scripts/run_tests.py TESTTIMEOUT= 1200 -.PHONY: test testall testuniversal buildbottest pythoninfo - # Remove "test_python_*" directories of previous failed test jobs. # Pass TESTOPTS options because it can contain --tempdir option. +.PHONY: cleantest cleantest: all $(TESTRUNNER) $(TESTOPTS) --cleanup # Run a basic set of regression tests. # This excludes some tests that are particularly resource-intensive. +.PHONY: test test: all $(TESTRUNNER) $(TESTOPTS) @@ -1781,6 +1806,7 @@ test: all # the bytecode read from a .pyc file had the bug, sometimes the directly # generated bytecode. This is sometimes a very shy bug needing a lot of # sample data. +.PHONY: testall testall: all -find $(srcdir)/Lib -name '*.py[co]' -print | xargs rm -f $(TESTPYTHON) -E $(srcdir)/Lib/compileall.py @@ -1790,6 +1816,7 @@ testall: all # Run the test suite for both architectures in a Universal build on OSX. # Must be run on an Intel box. +.PHONY: testuniversal testuniversal: all @if [ `arch` != 'i386' ]; then \ echo "This can only be used on OSX/i386" ;\ @@ -1801,6 +1828,7 @@ testuniversal: all # Like testall, but with only one pass and without multiple processes. # Run an optional script to include information about the build environment. +.PHONY: buildbottest buildbottest: all -@if which pybuildbot.identify >/dev/null 2>&1; then \ pybuildbot.identify "CC='$(CC)'" "CXX='$(CXX)'"; \ @@ -1808,9 +1836,11 @@ buildbottest: all $(TESTRUNNER) -j 1 -u all -W --slowest --fail-env-changed --timeout=$(TESTTIMEOUT) $(TESTOPTS) # Like testall, but run Python tests with HOSTRUNNER directly. +.PHONY: hostrunnertest hostrunnertest: all $(RUNSHARED) $(HOSTRUNNER) ./$(BUILDPYTHON) -m test -u all $(TESTOPTS) +.PHONY: pythoninfo pythoninfo: all $(RUNSHARED) $(HOSTRUNNER) ./$(BUILDPYTHON) -m test.pythoninfo @@ -1820,14 +1850,17 @@ QUICKTESTOPTS= $(TESTOPTS) -x test_subprocess test_io test_lib2to3 \ test_multiprocessing_forkserver \ test_mailbox test_nntplib test_socket test_poll \ test_select test_zipfile test_concurrent_futures + +.PHONY: quicktest quicktest: all $(TESTRUNNER) $(QUICKTESTOPTS) # SSL tests -.PHONY: multisslcompile multissltest +.PHONY: multisslcompile multisslcompile: all $(RUNSHARED) ./$(BUILDPYTHON) $(srcdir)/Tools/ssl/multissltests.py --steps=modules +.PHONY: multissltest multissltest: all $(RUNSHARED) ./$(BUILDPYTHON) $(srcdir)/Tools/ssl/multissltests.py @@ -1835,6 +1868,7 @@ multissltest: all # prevent race conditions with PGO builds. PGO builds use recursive make, # which can lead to two parallel `./python setup.py build` processes that # step on each others toes. +.PHONY: install install: @FRAMEWORKINSTALLFIRST@ commoninstall bininstall maninstall @FRAMEWORKINSTALLLAST@ if test "x$(ENSUREPIP)" != "xno" ; then \ case $(ENSUREPIP) in \ @@ -1845,6 +1879,7 @@ install: @FRAMEWORKINSTALLFIRST@ commoninstall bininstall maninstall @FRAMEWORKI $$ensurepip --root=$(DESTDIR)/ ; \ fi +.PHONY: altinstall altinstall: commoninstall if test "x$(ENSUREPIP)" != "xno" ; then \ case $(ENSUREPIP) in \ @@ -1855,6 +1890,7 @@ altinstall: commoninstall $$ensurepip --root=$(DESTDIR)/ ; \ fi +.PHONY: commoninstall commoninstall: check-clean-src @FRAMEWORKALTINSTALLFIRST@ \ altbininstall libinstall inclinstall libainstall \ sharedinstall altmaninstall \ @@ -1863,6 +1899,7 @@ commoninstall: check-clean-src @FRAMEWORKALTINSTALLFIRST@ \ # Install shared libraries enabled by Setup DESTDIRS= $(exec_prefix) $(LIBDIR) $(BINLIBDEST) $(DESTSHARED) +.PHONY: sharedinstall sharedinstall: all @for i in $(DESTDIRS); \ do \ @@ -1885,6 +1922,7 @@ sharedinstall: all # Install the interpreter with $(VERSION) affixed # This goes into $(exec_prefix) +.PHONY: altbininstall altbininstall: $(BUILDPYTHON) @FRAMEWORKPYTHONW@ @for i in $(BINDIR) $(LIBDIR); \ do \ @@ -1950,7 +1988,7 @@ altbininstall: $(BUILDPYTHON) @FRAMEWORKPYTHONW@ fi \ fi - +.PHONY: bininstall bininstall: altbininstall if test ! -d $(DESTDIR)$(LIBPC); then \ echo "Creating directory $(LIBPC)"; \ @@ -1991,6 +2029,7 @@ bininstall: altbininstall fi # Install the versioned manual page +.PHONY: altmaninstall altmaninstall: @for i in $(MANDIR) $(MANDIR)/man1; \ do \ @@ -2004,6 +2043,7 @@ altmaninstall: $(DESTDIR)$(MANDIR)/man1/python$(VERSION).1 # Install the unversioned manual page +.PHONY: maninstall maninstall: altmaninstall -rm -f $(DESTDIR)$(MANDIR)/man1/python3.1 (cd $(DESTDIR)$(MANDIR)/man1; $(LN) -s python$(VERSION).1 python3.1) @@ -2162,6 +2202,8 @@ TESTSUBDIRS= idlelib/idle_test \ COMPILEALL_OPTS=-j0 TEST_MODULES=@TEST_MODULES@ + +.PHONY: libinstall libinstall: all $(srcdir)/Modules/xxmodule.c @for i in $(SCRIPTDIR) $(LIBDEST); \ do \ @@ -2282,10 +2324,13 @@ $(SCRIPT_PYDOC): $(srcdir)/Tools/scripts/pydoc3 sed -e "s,/usr/bin/env python3,$(EXENAME)," < $(srcdir)/Tools/scripts/pydoc3 > $@ @chmod +x $@ +.PHONY: scripts scripts: $(SCRIPT_2TO3) $(SCRIPT_IDLE) $(SCRIPT_PYDOC) python-config # Install the include files INCLDIRSTOMAKE=$(INCLUDEDIR) $(CONFINCLUDEDIR) $(INCLUDEPY) $(CONFINCLUDEPY) + +.PHONY: inclinstall inclinstall: @for i in $(INCLDIRSTOMAKE); \ do \ @@ -2329,6 +2374,7 @@ LIBPL= @LIBPL@ # pkgconfig directory LIBPC= $(LIBDIR)/pkgconfig +.PHONY: libainstall libainstall: all scripts @for i in $(LIBDIR) $(LIBPL) $(LIBPC) $(BINDIR); \ do \ @@ -2392,6 +2438,7 @@ libainstall: all scripts # # This target is here for backward compatibility, previous versions of Python # hadn't integrated framework installation in the normal install process. +.PHONY: frameworkinstall frameworkinstall: install # On install, we re-make the framework @@ -2400,8 +2447,10 @@ frameworkinstall: install # automatically set prefix to the location deep down in the framework, so we # only have to cater for the structural bits of the framework. +.PHONY: frameworkinstallframework frameworkinstallframework: frameworkinstallstructure install frameworkinstallmaclib +.PHONY: frameworkinstallstructure frameworkinstallstructure: $(LDLIBRARY) @if test "$(PYTHONFRAMEWORKDIR)" = no-framework; then \ echo Not configured with --enable-framework; \ @@ -2426,6 +2475,7 @@ frameworkinstallstructure: $(LDLIBRARY) # This installs Mac/Lib into the framework # Install a number of symlinks to keep software that expects a normal unix # install (which includes python-config) happy. +.PHONY: frameworkinstallmaclib frameworkinstallmaclib: $(LN) -fs "../../../$(PYTHONFRAMEWORK)" "$(DESTDIR)$(LIBPL)/libpython$(LDVERSION).a" $(LN) -fs "../../../$(PYTHONFRAMEWORK)" "$(DESTDIR)$(LIBPL)/libpython$(LDVERSION).dylib" @@ -2435,25 +2485,30 @@ frameworkinstallmaclib: $(LN) -fs "../$(PYTHONFRAMEWORK)" "$(DESTDIR)$(prefix)/lib/libpython$(VERSION).dylib" # This installs the IDE, the Launcher and other apps into /Applications +.PHONY: frameworkinstallapps frameworkinstallapps: cd Mac && $(MAKE) installapps DESTDIR="$(DESTDIR)" # Build the bootstrap executable that will spawn the interpreter inside # an app bundle within the framework. This allows the interpreter to # run OS X GUI APIs. +.PHONY: frameworkpythonw frameworkpythonw: cd Mac && $(MAKE) pythonw # This installs the python* and other bin symlinks in $prefix/bin or in # a bin directory relative to the framework root +.PHONY: frameworkinstallunixtools frameworkinstallunixtools: cd Mac && $(MAKE) installunixtools DESTDIR="$(DESTDIR)" +.PHONY: frameworkaltinstallunixtools frameworkaltinstallunixtools: cd Mac && $(MAKE) altinstallunixtools DESTDIR="$(DESTDIR)" # This installs the Tools into the applications directory. # It is not part of a normal frameworkinstall +.PHONY: frameworkinstallextras frameworkinstallextras: cd Mac && $(MAKE) installextras DESTDIR="$(DESTDIR)" @@ -2483,11 +2538,13 @@ Python/dtoa.o: Python/dtoa.c $(CC) -c $(PY_CORE_CFLAGS) $(CFLAGS_ALIASING) -o $@ $< # Run reindent on the library +.PHONY: reindent reindent: ./$(BUILDPYTHON) $(srcdir)/Tools/patchcheck/reindent.py -r $(srcdir)/Lib # Rerun configure with the same options as it was run last time, # provided the config.status script exists +.PHONY: recheck recheck: ./config.status --recheck ./config.status @@ -2524,10 +2581,12 @@ TAGS:: # Sanitation targets -- clean leaves libraries, executables and tags # files, which clobber removes as well +.PHONY: pycremoval pycremoval: -find $(srcdir) -depth -name '__pycache__' -exec rm -rf {} ';' -find $(srcdir) -name '*.py[co]' -exec rm -f {} ';' +.PHONY: rmtestturds rmtestturds: -rm -f *BAD *GOOD *SKIPPED -rm -rf OUT @@ -2535,11 +2594,13 @@ rmtestturds: -rm -f *.txt -rm -f gb-18030-2000.xml +.PHONY: docclean docclean: $(MAKE) -C $(srcdir)/Doc clean # like the 'clean' target but retain the profile guided optimization (PGO) # data. The PGO data is only valid if source code remains unchanged. +.PHONY: clean-retain-profile clean-retain-profile: pycremoval find . -name '*.[oa]' -exec rm -f {} ';' find . -name '*.s[ol]' -exec rm -f {} ';' @@ -2563,6 +2624,7 @@ clean-retain-profile: pycremoval -rm -f Include/pydtrace_probes.h -rm -f profile-gen-stamp +.PHONY: profile-removal profile-removal: find . -name '*.gc??' -exec rm -f {} ';' find . -name '*.profclang?' -exec rm -f {} ';' @@ -2571,12 +2633,14 @@ profile-removal: rm -rf $(COVERAGE_REPORT) rm -f profile-run-stamp +.PHONY: clean clean: clean-retain-profile @if test @DEF_MAKE_ALL_RULE@ = profile-opt; then \ rm -f profile-gen-stamp profile-clean-stamp; \ $(MAKE) profile-removal; \ fi +.PHONY: clobber clobber: clean -rm -f $(BUILDPYTHON) $(LIBRARY) $(LDLIBRARY) $(DLLLIBRARY) \ tags TAGS \ @@ -2588,6 +2652,7 @@ clobber: clean # Make things extra clean, before making a distribution: # remove all generated files, even Makefile[.pre] # Keep configure and Python-ast.[ch], it's possible they can't be generated +.PHONY: distclean distclean: clobber docclean for file in $(srcdir)/Lib/test/data/* ; do \ if test "$$file" != "$(srcdir)/Lib/test/data/README"; then rm "$$file"; fi; \ @@ -2608,16 +2673,19 @@ distclean: clobber docclean -exec rm -f {} ';' # Check that all symbols exported by libpython start with "Py" or "_Py" +.PHONY: smelly smelly: all $(RUNSHARED) ./$(BUILDPYTHON) $(srcdir)/Tools/build/smelly.py # Check if any unsupported C global variables have been added. +.PHONY: check-c-globals check-c-globals: $(PYTHON_FOR_REGEN) $(srcdir)/Tools/c-analyzer/check-c-globals.py \ --format summary \ --traceback # Find files with funny names +.PHONY: funny funny: find $(SUBDIRS) $(SUBDIRSTOO) \ -type d \ @@ -2649,9 +2717,11 @@ funny: -o -print # Perform some verification checks on any modified files. +.PHONY: patchcheck patchcheck: all $(RUNSHARED) ./$(BUILDPYTHON) $(srcdir)/Tools/patchcheck/patchcheck.py +.PHONY: check-limited-abi check-limited-abi: all $(RUNSHARED) ./$(BUILDPYTHON) $(srcdir)/Tools/build/stable_abi.py --all $(srcdir)/Misc/stable_abi.toml @@ -2665,19 +2735,6 @@ update-config: Python/thread.o: @THREADHEADERS@ $(srcdir)/Python/condvar.h -# Declare targets that aren't real files -.PHONY: all build_all build_wasm check-clean-src -.PHONY: sharedmods checksharedmods test quicktest rundsymutil -.PHONY: install altinstall sharedinstall bininstall altbininstall -.PHONY: maninstall libinstall inclinstall libainstall -.PHONY: frameworkinstall frameworkinstallframework frameworkinstallstructure -.PHONY: frameworkinstallmaclib frameworkinstallapps frameworkinstallunixtools -.PHONY: frameworkaltinstallunixtools recheck clean clobber distclean -.PHONY: smelly funny patchcheck touch altmaninstall commoninstall -.PHONY: clean-retain-profile profile-removal run_profile_task -.PHONY: build_all_generate_profile build_all_merge_profile -.PHONY: gdbhooks scripts - ########################################################################## # Module dependencies and platform-specific files diff --git a/Misc/NEWS.d/next/Build/2023-05-14-19-00-19.gh-issue-104490.1tA4AF.rst b/Misc/NEWS.d/next/Build/2023-05-14-19-00-19.gh-issue-104490.1tA4AF.rst new file mode 100644 index 00000000000000..1315b5cb3c1969 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2023-05-14-19-00-19.gh-issue-104490.1tA4AF.rst @@ -0,0 +1 @@ +Define ``.PHONY`` / virtual make targets consistently and properly. From 9d41f83c58e6dc2fc6eb4b91f803551850b0adeb Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Mon, 15 May 2023 09:49:28 +0100 Subject: [PATCH 09/13] gh-104050: Run mypy on `clinic.py` in CI (#104421) * Add basic mypy workflow to CI * Make the type check pass --------- Co-authored-by: Erlend E. Aasland Co-authored-by: Nikita Sobolev Co-authored-by: Hugo van Kemenade --- .github/dependabot.yml | 7 ++++++ .github/workflows/mypy.yml | 39 ++++++++++++++++++++++++++++++ Tools/clinic/clinic.py | 40 ++++++++++++++++++++----------- Tools/clinic/cpp.py | 26 ++++++++++++-------- Tools/clinic/mypy.ini | 11 +++++++++ Tools/clinic/requirements-dev.txt | 2 ++ 6 files changed, 101 insertions(+), 24 deletions(-) create mode 100644 .github/workflows/mypy.yml create mode 100644 Tools/clinic/mypy.ini create mode 100644 Tools/clinic/requirements-dev.txt diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 555e246e402bf9..f026b0f5f9454a 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -12,3 +12,10 @@ updates: update-types: - "version-update:semver-minor" - "version-update:semver-patch" + - package-ecosystem: "pip" + directory: "/Tools/clinic/" + schedule: + interval: "monthly" + labels: + - "skip issue" + - "skip news" diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml new file mode 100644 index 00000000000000..1315bb5a966f01 --- /dev/null +++ b/.github/workflows/mypy.yml @@ -0,0 +1,39 @@ +# Workflow to run mypy on select parts of the CPython repo +name: mypy + +on: + push: + branches: + - main + pull_request: + paths: + - "Tools/clinic/**" + - ".github/workflows/mypy.yml" + workflow_dispatch: + +permissions: + contents: read + +env: + PIP_DISABLE_PIP_VERSION_CHECK: 1 + FORCE_COLOR: 1 + TERM: xterm-256color # needed for FORCE_COLOR to work on mypy on Ubuntu, see https://github.com/python/mypy/issues/13817 + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + mypy: + name: Run mypy on Tools/clinic/ + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: "3.x" + cache: pip + cache-dependency-path: Tools/clinic/requirements-dev.txt + - run: pip install -r Tools/clinic/requirements-dev.txt + - run: mypy --config-file Tools/clinic/mypy.ini diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index 19c4cd299f0bbb..4270fb3cc56613 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -7,6 +7,7 @@ import abc import ast +import builtins as bltns import collections import contextlib import copy @@ -26,7 +27,9 @@ import traceback import types +from collections.abc import Callable from types import * +from typing import Any, NamedTuple # TODO: # @@ -78,8 +81,13 @@ def __repr__(self): sig_end_marker = '--' +Appender = Callable[[str], None] +Outputter = Callable[[None], str] -_text_accumulator_nt = collections.namedtuple("_text_accumulator", "text append output") +class _TextAccumulator(NamedTuple): + text: list[str] + append: Appender + output: Outputter def _text_accumulator(): text = [] @@ -87,10 +95,12 @@ def output(): s = ''.join(text) text.clear() return s - return _text_accumulator_nt(text, text.append, output) + return _TextAccumulator(text, text.append, output) -text_accumulator_nt = collections.namedtuple("text_accumulator", "text append") +class TextAccumulator(NamedTuple): + text: list[str] + append: Appender def text_accumulator(): """ @@ -104,7 +114,7 @@ def text_accumulator(): empties the accumulator. """ text, append, output = _text_accumulator() - return text_accumulator_nt(append, output) + return TextAccumulator(append, output) def warn_or_fail(fail=False, *args, filename=None, line_number=None): @@ -1925,8 +1935,10 @@ def dump(self): # maps strings to Language objects. # "languages" maps the name of the language ("C", "Python"). # "extensions" maps the file extension ("c", "py"). +LangDict = dict[str, Callable[[str], Language]] + languages = { 'C': CLanguage, 'Python': PythonLanguage } -extensions = { name: CLanguage for name in "c cc cpp cxx h hh hpp hxx".split() } +extensions: LangDict = { name: CLanguage for name in "c cc cpp cxx h hh hpp hxx".split() } extensions['py'] = PythonLanguage @@ -2558,15 +2570,15 @@ class CConverter(metaclass=CConverterAutoRegister): """ # The C name to use for this variable. - name = None + name: str | None = None # The Python name to use for this variable. - py_name = None + py_name: str | None = None # The C type to use for this variable. # 'type' should be a Python string specifying the type, e.g. "int". # If this is a pointer type, the type string should end with ' *'. - type = None + type: str | None = None # The Python default value for this parameter, as a Python value. # Or the magic value "unspecified" if there is no default. @@ -2577,15 +2589,15 @@ class CConverter(metaclass=CConverterAutoRegister): # If not None, default must be isinstance() of this type. # (You can also specify a tuple of types.) - default_type = None + default_type: bltns.type[Any] | tuple[bltns.type[Any], ...] | None = None # "default" converted into a C value, as a string. # Or None if there is no default. - c_default = None + c_default: str | None = None # "default" converted into a Python value, as a string. # Or None if there is no default. - py_default = None + py_default: str | None = None # The default value used to initialize the C variable when # there is no default, but not specifying a default may @@ -2597,14 +2609,14 @@ class CConverter(metaclass=CConverterAutoRegister): # # This value is specified as a string. # Every non-abstract subclass should supply a valid value. - c_ignored_default = 'NULL' + c_ignored_default: str = 'NULL' # If true, wrap with Py_UNUSED. unused = False # The C converter *function* to be used, if any. # (If this is not None, format_unit must be 'O&'.) - converter = None + converter: str | None = None # Should Argument Clinic add a '&' before the name of # the variable when passing it into the _impl function? @@ -3432,7 +3444,7 @@ class robuffer: pass def str_converter_key(types, encoding, zeroes): return (frozenset(types), bool(encoding), bool(zeroes)) -str_converter_argument_map = {} +str_converter_argument_map: dict[str, str] = {} class str_converter(CConverter): type = 'const char *' diff --git a/Tools/clinic/cpp.py b/Tools/clinic/cpp.py index 77f5f9696a6d84..bc2cc713aac394 100644 --- a/Tools/clinic/cpp.py +++ b/Tools/clinic/cpp.py @@ -1,7 +1,12 @@ import re import sys +from collections.abc import Callable -def negate(condition): + +TokenAndCondition = tuple[str, str] +TokenStack = list[TokenAndCondition] + +def negate(condition: str) -> str: """ Returns a CPP conditional that is the opposite of the conditional passed in. """ @@ -22,17 +27,18 @@ class Monitor: Anyway this implementation seems to work well enough for the CPython sources. """ + is_a_simple_defined: Callable[[str], re.Match[str] | None] is_a_simple_defined = re.compile(r'^defined\s*\(\s*[A-Za-z0-9_]+\s*\)$').match - def __init__(self, filename=None, *, verbose=False): - self.stack = [] + def __init__(self, filename=None, *, verbose: bool = False): + self.stack: TokenStack = [] self.in_comment = False - self.continuation = None + self.continuation: str | None = None self.line_number = 0 self.filename = filename self.verbose = verbose - def __repr__(self): + def __repr__(self) -> str: return ''.join(( '")) - def status(self): + def status(self) -> str: return str(self.line_number).rjust(4) + ": " + self.condition() - def condition(self): + def condition(self) -> str: """ Returns the current preprocessor state, as a single #if condition. """ @@ -62,15 +68,15 @@ def close(self): if self.stack: self.fail("Ended file while still in a preprocessor conditional block!") - def write(self, s): + def write(self, s: str) -> None: for line in s.split("\n"): self.writeline(line) - def writeline(self, line): + def writeline(self, line: str) -> None: self.line_number += 1 line = line.strip() - def pop_stack(): + def pop_stack() -> TokenAndCondition: if not self.stack: self.fail("#" + token + " without matching #if / #ifdef / #ifndef!") return self.stack.pop() diff --git a/Tools/clinic/mypy.ini b/Tools/clinic/mypy.ini new file mode 100644 index 00000000000000..3c5643e789be37 --- /dev/null +++ b/Tools/clinic/mypy.ini @@ -0,0 +1,11 @@ +[mypy] +# make sure clinic can still be run on Python 3.10 +python_version = 3.10 +pretty = True +enable_error_code = ignore-without-code +disallow_any_generics = True +strict_concatenate = True +warn_redundant_casts = True +warn_unused_ignores = True +warn_unused_configs = True +files = Tools/clinic/ diff --git a/Tools/clinic/requirements-dev.txt b/Tools/clinic/requirements-dev.txt new file mode 100644 index 00000000000000..7e0aa37a46d7bc --- /dev/null +++ b/Tools/clinic/requirements-dev.txt @@ -0,0 +1,2 @@ +# Requirements file for external linters and checks we run on Tools/clinic/ in CI +mypy==1.2.0 From 3cba61f111db9b5e8ef35632915309f81fff8c6c Mon Sep 17 00:00:00 2001 From: Christopher Chavez Date: Mon, 15 May 2023 03:54:41 -0500 Subject: [PATCH 10/13] gh-104494: Update certain Tkinter pack/place tests for Tk 8.7 errors (#104495) Co-authored-by: Terry Jan Reedy Co-authored-by: Erlend E. Aasland --- Lib/test/test_tkinter/test_geometry_managers.py | 10 ++++++---- .../2023-05-15-02-22-44.gh-issue-104494.Bkrbfn.rst | 2 ++ 2 files changed, 8 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Tests/2023-05-15-02-22-44.gh-issue-104494.Bkrbfn.rst diff --git a/Lib/test/test_tkinter/test_geometry_managers.py b/Lib/test/test_tkinter/test_geometry_managers.py index 3663048a145ab1..59fe592b492adc 100644 --- a/Lib/test/test_tkinter/test_geometry_managers.py +++ b/Lib/test/test_tkinter/test_geometry_managers.py @@ -108,8 +108,8 @@ def test_pack_configure_in(self): a.pack_configure(in_=c) self.assertEqual(pack.pack_slaves(), [b, c, d]) self.assertEqual(c.pack_slaves(), [a]) - with self.assertRaisesRegex(TclError, - 'can\'t pack %s inside itself' % (a,)): + with self.assertRaisesRegex( + TclError, """can't pack "?%s"? inside itself""" % (a,)): a.pack_configure(in_=a) with self.assertRaisesRegex(TclError, 'bad window path name ".foo"'): a.pack_configure(in_='.foo') @@ -292,8 +292,10 @@ def create2(self): def test_place_configure_in(self): t, f, f2 = self.create2() self.assertEqual(f2.winfo_manager(), '') - with self.assertRaisesRegex(TclError, "can't place %s relative to " - "itself" % re.escape(str(f2))): + with self.assertRaisesRegex( + TclError, + """can't place "?%s"? relative to itself""" + % re.escape(str(f2))): f2.place_configure(in_=f2) self.assertEqual(f2.winfo_manager(), '') with self.assertRaisesRegex(TclError, 'bad window path name'): diff --git a/Misc/NEWS.d/next/Tests/2023-05-15-02-22-44.gh-issue-104494.Bkrbfn.rst b/Misc/NEWS.d/next/Tests/2023-05-15-02-22-44.gh-issue-104494.Bkrbfn.rst new file mode 100644 index 00000000000000..a320c48428b58b --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-05-15-02-22-44.gh-issue-104494.Bkrbfn.rst @@ -0,0 +1,2 @@ +Update ``test_pack_configure_in`` and ``test_place_configure_in`` +for changes to error message formatting in Tk 8.7. From 35bf0916d913b83cf1883b76e67ad8b1c9a25bbe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 May 2023 09:17:48 +0000 Subject: [PATCH 11/13] Bump mypy from 1.2.0 to 1.3.0 in /Tools/clinic (#104501) build(deps-dev): bump mypy from 1.2.0 to 1.3.0 in /Tools/clinic Bumps [mypy](https://github.com/python/mypy) from 1.2.0 to 1.3.0. - [Commits](https://github.com/python/mypy/compare/v1.2.0...v1.3.0) --- updated-dependencies: - dependency-name: mypy dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Tools/clinic/requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tools/clinic/requirements-dev.txt b/Tools/clinic/requirements-dev.txt index 7e0aa37a46d7bc..154934003c31c4 100644 --- a/Tools/clinic/requirements-dev.txt +++ b/Tools/clinic/requirements-dev.txt @@ -1,2 +1,2 @@ # Requirements file for external linters and checks we run on Tools/clinic/ in CI -mypy==1.2.0 +mypy==1.3.0 From 186bf39f5c6003912e4f445430c504db51b9a743 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Mon, 15 May 2023 11:26:27 +0200 Subject: [PATCH 12/13] gh-101819: Isolate `_io` (#101948) Co-authored-by: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com> Co-authored-by: Victor Stinner --- Lib/test/test_io.py | 7 +- ...-03-14-11-20-19.gh-issue-101819.0-h0it.rst | 2 + Modules/_io/_iomodule.c | 138 ++++---------- Modules/_io/_iomodule.h | 15 +- Modules/_io/bufferedio.c | 116 +++++------- Modules/_io/bytesio.c | 2 + Modules/_io/fileio.c | 4 +- Modules/_io/iobase.c | 176 +++++++----------- Modules/_io/stringio.c | 11 +- Modules/_io/textio.c | 80 +++----- Modules/_io/winconsoleio.c | 2 +- Python/pylifecycle.c | 10 - Tools/c-analyzer/cpython/globals-to-fix.tsv | 4 - 13 files changed, 205 insertions(+), 362 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-03-14-11-20-19.gh-issue-101819.0-h0it.rst diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index 9bcc92a40c61df..cc16804fe21829 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -4242,6 +4242,7 @@ def test_warn_on_dealloc_fd(self): def test_pickling(self): # Pickling file objects is forbidden + msg = "cannot pickle" for kwargs in [ {"mode": "w"}, {"mode": "wb"}, @@ -4256,8 +4257,10 @@ def test_pickling(self): if "b" not in kwargs["mode"]: kwargs["encoding"] = "utf-8" for protocol in range(pickle.HIGHEST_PROTOCOL + 1): - with self.open(os_helper.TESTFN, **kwargs) as f: - self.assertRaises(TypeError, pickle.dumps, f, protocol) + with self.subTest(protocol=protocol, kwargs=kwargs): + with self.open(os_helper.TESTFN, **kwargs) as f: + with self.assertRaisesRegex(TypeError, msg): + pickle.dumps(f, protocol) @unittest.skipIf( support.is_emscripten, "fstat() of a pipe fd is not supported" diff --git a/Misc/NEWS.d/next/Library/2023-03-14-11-20-19.gh-issue-101819.0-h0it.rst b/Misc/NEWS.d/next/Library/2023-03-14-11-20-19.gh-issue-101819.0-h0it.rst new file mode 100644 index 00000000000000..4a73bbf32b370e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-03-14-11-20-19.gh-issue-101819.0-h0it.rst @@ -0,0 +1,2 @@ +Isolate the :mod:`io` extension module by applying :pep:`687`. Patch by +Kumar Aditya, Victor Stinner, and Erlend E. Aasland. diff --git a/Modules/_io/_iomodule.c b/Modules/_io/_iomodule.c index 2457cb124036fe..7b06c1bee5a832 100644 --- a/Modules/_io/_iomodule.c +++ b/Modules/_io/_iomodule.c @@ -561,25 +561,9 @@ PyNumber_AsOff_t(PyObject *item, PyObject *err) return result; } -_PyIO_State * -_PyIO_get_module_state(void) -{ - PyObject *mod = PyState_FindModule(&_PyIO_Module); - _PyIO_State *state; - if (mod == NULL || (state = get_io_state(mod)) == NULL) { - PyErr_SetString(PyExc_RuntimeError, - "could not find io module state " - "(interpreter shutdown?)"); - return NULL; - } - return state; -} - static int iomodule_traverse(PyObject *mod, visitproc visit, void *arg) { _PyIO_State *state = get_io_state(mod); - if (!state->initialized) - return 0; Py_VISIT(state->unsupported_operation); Py_VISIT(state->PyIOBase_Type); @@ -606,8 +590,6 @@ iomodule_traverse(PyObject *mod, visitproc visit, void *arg) { static int iomodule_clear(PyObject *mod) { _PyIO_State *state = get_io_state(mod); - if (!state->initialized) - return 0; Py_CLEAR(state->unsupported_operation); Py_CLEAR(state->PyIOBase_Type); @@ -652,115 +634,57 @@ static PyMethodDef module_methods[] = { {NULL, NULL} }; -struct PyModuleDef _PyIO_Module = { - PyModuleDef_HEAD_INIT, - "io", - module_doc, - sizeof(_PyIO_State), - module_methods, - NULL, - iomodule_traverse, - iomodule_clear, - (freefunc)iomodule_free, -}; - - -static PyTypeObject* static_types[] = { - // Base classes - &PyIOBase_Type, - - // PyIOBase_Type subclasses - &PyBufferedIOBase_Type, - &PyRawIOBase_Type, - &PyTextIOBase_Type, -}; - - -PyStatus -_PyIO_InitTypes(PyInterpreterState *interp) -{ - for (size_t i=0; i < Py_ARRAY_LENGTH(static_types); i++) { - PyTypeObject *type = static_types[i]; - if (_PyStaticType_InitBuiltin(interp, type) < 0) { - return _PyStatus_ERR("Can't initialize builtin type"); - } - } - - return _PyStatus_OK(); -} - -void -_PyIO_FiniTypes(PyInterpreterState *interp) -{ - // Deallocate types in the reverse order to deallocate subclasses before - // their base classes. - for (Py_ssize_t i=Py_ARRAY_LENGTH(static_types) - 1; i >= 0; i--) { - PyTypeObject *type = static_types[i]; - _PyStaticType_Dealloc(interp, type); - } -} - #define ADD_TYPE(module, type, spec, base) \ do { \ type = (PyTypeObject *)PyType_FromModuleAndSpec(module, spec, \ (PyObject *)base); \ if (type == NULL) { \ - goto fail; \ + return -1; \ } \ if (PyModule_AddType(module, type) < 0) { \ - goto fail; \ + return -1; \ } \ } while (0) -PyMODINIT_FUNC -PyInit__io(void) +static int +iomodule_exec(PyObject *m) { - PyObject *m = PyModule_Create(&_PyIO_Module); - _PyIO_State *state = NULL; - if (m == NULL) - return NULL; - state = get_io_state(m); - state->initialized = 0; + _PyIO_State *state = get_io_state(m); /* DEFAULT_BUFFER_SIZE */ if (PyModule_AddIntMacro(m, DEFAULT_BUFFER_SIZE) < 0) - goto fail; + return -1; /* UnsupportedOperation inherits from ValueError and OSError */ state->unsupported_operation = PyObject_CallFunction( (PyObject *)&PyType_Type, "s(OO){}", "UnsupportedOperation", PyExc_OSError, PyExc_ValueError); if (state->unsupported_operation == NULL) - goto fail; + return -1; if (PyModule_AddObjectRef(m, "UnsupportedOperation", state->unsupported_operation) < 0) { - goto fail; + return -1; } /* BlockingIOError, for compatibility */ if (PyModule_AddObjectRef(m, "BlockingIOError", (PyObject *) PyExc_BlockingIOError) < 0) { - goto fail; - } - - // Add types - for (size_t i=0; i < Py_ARRAY_LENGTH(static_types); i++) { - PyTypeObject *type = static_types[i]; - if (PyModule_AddType(m, type) < 0) { - goto fail; - } + return -1; } // Base classes - state->PyIOBase_Type = (PyTypeObject *)Py_NewRef(&PyIOBase_Type); ADD_TYPE(m, state->PyIncrementalNewlineDecoder_Type, &nldecoder_spec, NULL); ADD_TYPE(m, state->PyBytesIOBuffer_Type, &bytesiobuf_spec, NULL); + ADD_TYPE(m, state->PyIOBase_Type, &iobase_spec, NULL); // PyIOBase_Type subclasses - state->PyRawIOBase_Type = (PyTypeObject *)Py_NewRef(&PyRawIOBase_Type); - state->PyBufferedIOBase_Type = (PyTypeObject *)Py_NewRef(&PyBufferedIOBase_Type); - state->PyTextIOBase_Type = (PyTypeObject *)Py_NewRef(&PyTextIOBase_Type); + ADD_TYPE(m, state->PyTextIOBase_Type, &textiobase_spec, + state->PyIOBase_Type); + ADD_TYPE(m, state->PyBufferedIOBase_Type, &bufferediobase_spec, + state->PyIOBase_Type); + ADD_TYPE(m, state->PyRawIOBase_Type, &rawiobase_spec, + state->PyIOBase_Type); // PyBufferedIOBase_Type(PyIOBase_Type) subclasses ADD_TYPE(m, state->PyBytesIO_Type, &bytesio_spec, state->PyBufferedIOBase_Type); @@ -775,6 +699,7 @@ PyInit__io(void) // PyRawIOBase_Type(PyIOBase_Type) subclasses ADD_TYPE(m, state->PyFileIO_Type, &fileio_spec, state->PyRawIOBase_Type); + #ifdef HAVE_WINDOWS_CONSOLE_IO ADD_TYPE(m, state->PyWindowsConsoleIO_Type, &winconsoleio_spec, state->PyRawIOBase_Type); @@ -785,11 +710,30 @@ PyInit__io(void) ADD_TYPE(m, state->PyTextIOWrapper_Type, &textiowrapper_spec, state->PyTextIOBase_Type); - state->initialized = 1; +#undef ADD_TYPE + return 0; +} - return m; +static struct PyModuleDef_Slot iomodule_slots[] = { + {Py_mod_exec, iomodule_exec}, + {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {0, NULL}, +}; - fail: - Py_DECREF(m); - return NULL; +struct PyModuleDef _PyIO_Module = { + .m_base = PyModuleDef_HEAD_INIT, + .m_name = "io", + .m_doc = module_doc, + .m_size = sizeof(_PyIO_State), + .m_methods = module_methods, + .m_traverse = iomodule_traverse, + .m_clear = iomodule_clear, + .m_free = iomodule_free, + .m_slots = iomodule_slots, +}; + +PyMODINIT_FUNC +PyInit__io(void) +{ + return PyModuleDef_Init(&_PyIO_Module); } diff --git a/Modules/_io/_iomodule.h b/Modules/_io/_iomodule.h index ae06fecc48b450..afd638a120ba08 100644 --- a/Modules/_io/_iomodule.h +++ b/Modules/_io/_iomodule.h @@ -8,13 +8,8 @@ #include "pycore_typeobject.h" // _PyType_GetModuleState() #include "structmember.h" -/* ABCs */ -extern PyTypeObject PyIOBase_Type; -extern PyTypeObject PyRawIOBase_Type; -extern PyTypeObject PyBufferedIOBase_Type; -extern PyTypeObject PyTextIOBase_Type; - /* Type specs */ +extern PyType_Spec bufferediobase_spec; extern PyType_Spec bufferedrandom_spec; extern PyType_Spec bufferedreader_spec; extern PyType_Spec bufferedrwpair_spec; @@ -22,8 +17,11 @@ extern PyType_Spec bufferedwriter_spec; extern PyType_Spec bytesio_spec; extern PyType_Spec bytesiobuf_spec; extern PyType_Spec fileio_spec; +extern PyType_Spec iobase_spec; extern PyType_Spec nldecoder_spec; +extern PyType_Spec rawiobase_spec; extern PyType_Spec stringio_spec; +extern PyType_Spec textiobase_spec; extern PyType_Spec textiowrapper_spec; #ifdef HAVE_WINDOWS_CONSOLE_IO @@ -168,9 +166,6 @@ struct _io_state { #endif }; -#define IO_MOD_STATE(mod) ((_PyIO_State *)PyModule_GetState(mod)) -#define IO_STATE() _PyIO_get_module_state() - static inline _PyIO_State * get_io_state(PyObject *module) { @@ -195,7 +190,7 @@ find_io_state_by_def(PyTypeObject *type) return get_io_state(mod); } -extern _PyIO_State *_PyIO_get_module_state(void); +extern PyObject *_PyIOBase_cannot_pickle(PyObject *self, PyObject *args); #ifdef HAVE_WINDOWS_CONSOLE_IO extern char _PyIO_get_console_type(PyObject *); diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c index 00e228bca4375b..d5cc047bd8d563 100644 --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -16,14 +16,14 @@ /*[clinic input] module _io -class _io._BufferedIOBase "PyObject *" "&PyBufferedIOBase_Type" -class _io._Buffered "buffered *" "&PyBufferedIOBase_Type" +class _io._BufferedIOBase "PyObject *" "clinic_state()->PyBufferedIOBase_Type" +class _io._Buffered "buffered *" "clinic_state()->PyBufferedIOBase_Type" class _io.BufferedReader "buffered *" "clinic_state()->PyBufferedReader_Type" class _io.BufferedWriter "buffered *" "clinic_state()->PyBufferedWriter_Type" class _io.BufferedRWPair "rwpair *" "clinic_state()->PyBufferedRWPair_Type" class _io.BufferedRandom "buffered *" "clinic_state()->PyBufferedRandom_Type" [clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=abd685b9d94b9888]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=3b3ef9cbbbad4590]*/ /* * BufferedIOBase class, inherits from IOBase. @@ -128,7 +128,7 @@ static PyObject * _io__BufferedIOBase_detach_impl(PyObject *self, PyTypeObject *cls) /*[clinic end generated code: output=b87b135d67cd4448 input=0b61a7b4357c1ea7]*/ { - _PyIO_State *state = IO_STATE(); + _PyIO_State *state = get_io_state_by_cls(cls); return bufferediobase_unsupported(state, "detach"); } @@ -162,7 +162,7 @@ _io__BufferedIOBase_read_impl(PyObject *self, PyTypeObject *cls, PyObject *args) /*[clinic end generated code: output=4521b30940fd7b67 input=390205758adc8510]*/ { - _PyIO_State *state = IO_STATE(); + _PyIO_State *state = get_io_state_by_cls(cls); return bufferediobase_unsupported(state, "read"); } @@ -184,7 +184,7 @@ _io__BufferedIOBase_read1_impl(PyObject *self, PyTypeObject *cls, PyObject *args) /*[clinic end generated code: output=636fd241c21e050a input=ef546a1238c5b41c]*/ { - _PyIO_State *state = IO_STATE(); + _PyIO_State *state = get_io_state_by_cls(cls); return bufferediobase_unsupported(state, "read1"); } @@ -209,7 +209,7 @@ _io__BufferedIOBase_write_impl(PyObject *self, PyTypeObject *cls, PyObject *args) /*[clinic end generated code: output=d51feea4bcac9892 input=f79b72c4dccb3dc2]*/ { - _PyIO_State *state = IO_STATE(); + _PyIO_State *state = get_io_state_by_cls(cls); return bufferediobase_unsupported(state, "write"); } @@ -394,6 +394,15 @@ _enter_buffered_busy(buffered *self) (self->buffer_size * (size / self->buffer_size))) +static int +buffered_clear(buffered *self) +{ + self->ok = 0; + Py_CLEAR(self->raw); + Py_CLEAR(self->dict); + return 0; +} + static void buffered_dealloc(buffered *self) { @@ -405,7 +414,6 @@ buffered_dealloc(buffered *self) self->ok = 0; if (self->weakreflist != NULL) PyObject_ClearWeakRefs((PyObject *)self); - Py_CLEAR(self->raw); if (self->buffer) { PyMem_Free(self->buffer); self->buffer = NULL; @@ -414,7 +422,7 @@ buffered_dealloc(buffered *self) PyThread_free_lock(self->lock); self->lock = NULL; } - Py_CLEAR(self->dict); + (void)buffered_clear(self); tp->tp_free((PyObject *)self); Py_DECREF(tp); } @@ -443,15 +451,6 @@ buffered_traverse(buffered *self, visitproc visit, void *arg) return 0; } -static int -buffered_clear(buffered *self) -{ - self->ok = 0; - Py_CLEAR(self->raw); - Py_CLEAR(self->dict); - return 0; -} - /* Because this can call arbitrary code, it shouldn't be called when the refcount is 0 (that is, not directly from tp_dealloc unless the refcount has been temporarily re-incremented). */ @@ -2220,6 +2219,8 @@ bufferedrwpair_traverse(rwpair *self, visitproc visit, void *arg) { Py_VISIT(Py_TYPE(self)); Py_VISIT(self->dict); + Py_VISIT(self->reader); + Py_VISIT(self->writer); return 0; } @@ -2239,9 +2240,7 @@ bufferedrwpair_dealloc(rwpair *self) _PyObject_GC_UNTRACK(self); if (self->weakreflist != NULL) PyObject_ClearWeakRefs((PyObject *)self); - Py_CLEAR(self->reader); - Py_CLEAR(self->writer); - Py_CLEAR(self->dict); + (void)bufferedrwpair_clear(self); tp->tp_free((PyObject *) self); Py_DECREF(tp); } @@ -2424,6 +2423,12 @@ _io_BufferedRandom___init___impl(buffered *self, PyObject *raw, #include "clinic/bufferedio.c.h" #undef clinic_state +static int +bufferediobase_traverse(PyObject *self, visitproc visit, void *arg) +{ + Py_VISIT(Py_TYPE(self)); + return 0; +} static PyMethodDef bufferediobase_methods[] = { _IO__BUFFEREDIOBASE_DETACH_METHODDEF @@ -2435,57 +2440,19 @@ static PyMethodDef bufferediobase_methods[] = { {NULL, NULL} }; -PyTypeObject PyBufferedIOBase_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_io._BufferedIOBase", /*tp_name*/ - 0, /*tp_basicsize*/ - 0, /*tp_itemsize*/ - 0, /*tp_dealloc*/ - 0, /*tp_vectorcall_offset*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_as_async*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash */ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ - bufferediobase_doc, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - bufferediobase_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - &PyIOBase_Type, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ - 0, /* tp_free */ - 0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - 0, /* tp_del */ - 0, /* tp_version_tag */ - 0, /* tp_finalize */ +static PyType_Slot bufferediobase_slots[] = { + {Py_tp_doc, (void *)bufferediobase_doc}, + {Py_tp_methods, bufferediobase_methods}, + {Py_tp_traverse, bufferediobase_traverse}, + {0, NULL}, }; +PyType_Spec bufferediobase_spec = { + .name = "_io._BufferedIOBase", + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = bufferediobase_slots, +}; static PyMethodDef bufferedreader_methods[] = { /* BufferedIOMixin methods */ @@ -2508,6 +2475,9 @@ static PyMethodDef bufferedreader_methods[] = { _IO__BUFFERED_TELL_METHODDEF _IO__BUFFERED_TRUNCATE_METHODDEF _IO__BUFFERED___SIZEOF___METHODDEF + + {"__reduce__", _PyIOBase_cannot_pickle, METH_VARARGS}, + {"__reduce_ex__", _PyIOBase_cannot_pickle, METH_VARARGS}, {NULL, NULL} }; @@ -2565,6 +2535,9 @@ static PyMethodDef bufferedwriter_methods[] = { _IO__BUFFERED_SEEK_METHODDEF _IO__BUFFERED_TELL_METHODDEF _IO__BUFFERED___SIZEOF___METHODDEF + + {"__reduce__", _PyIOBase_cannot_pickle, METH_VARARGS}, + {"__reduce_ex__", _PyIOBase_cannot_pickle, METH_VARARGS}, {NULL, NULL} }; @@ -2680,6 +2653,9 @@ static PyMethodDef bufferedrandom_methods[] = { _IO__BUFFERED_PEEK_METHODDEF _IO_BUFFEREDWRITER_WRITE_METHODDEF _IO__BUFFERED___SIZEOF___METHODDEF + + {"__reduce__", _PyIOBase_cannot_pickle, METH_VARARGS}, + {"__reduce_ex__", _PyIOBase_cannot_pickle, METH_VARARGS}, {NULL, NULL} }; diff --git a/Modules/_io/bytesio.c b/Modules/_io/bytesio.c index 3fddfc2ed0bc9c..80773058693259 100644 --- a/Modules/_io/bytesio.c +++ b/Modules/_io/bytesio.c @@ -979,6 +979,7 @@ bytesio_traverse(bytesio *self, visitproc visit, void *arg) { Py_VISIT(Py_TYPE(self)); Py_VISIT(self->dict); + Py_VISIT(self->buf); return 0; } @@ -986,6 +987,7 @@ static int bytesio_clear(bytesio *self) { Py_CLEAR(self->dict); + Py_CLEAR(self->buf); return 0; } diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c index 473f0a8a6befcb..30944fc56bf70e 100644 --- a/Modules/_io/fileio.c +++ b/Modules/_io/fileio.c @@ -536,7 +536,7 @@ fileio_dealloc(fileio *self) _PyObject_GC_UNTRACK(self); if (self->weakreflist != NULL) PyObject_ClearWeakRefs((PyObject *) self); - Py_CLEAR(self->dict); + (void)fileio_clear(self); tp->tp_free((PyObject *)self); Py_DECREF(tp); } @@ -1166,6 +1166,8 @@ static PyMethodDef fileio_methods[] = { _IO_FILEIO_FILENO_METHODDEF _IO_FILEIO_ISATTY_METHODDEF {"_dealloc_warn", (PyCFunction)fileio_dealloc_warn, METH_O, NULL}, + {"__reduce__", _PyIOBase_cannot_pickle, METH_VARARGS}, + {"__reduce_ex__", _PyIOBase_cannot_pickle, METH_VARARGS}, {NULL, NULL} /* sentinel */ }; diff --git a/Modules/_io/iobase.c b/Modules/_io/iobase.c index 26f2a3155bda62..764c5fb3320072 100644 --- a/Modules/_io/iobase.c +++ b/Modules/_io/iobase.c @@ -17,10 +17,10 @@ /*[clinic input] module _io -class _io._IOBase "PyObject *" "&PyIOBase_Type" -class _io._RawIOBase "PyObject *" "&PyRawIOBase_Type" +class _io._IOBase "PyObject *" "clinic_state()->PyIOBase_Type" +class _io._RawIOBase "PyObject *" "clinic_state()->PyRawIOBase_Type" [clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=d29a4d076c2b211c]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=9006b7802ab8ea85]*/ /* * IOBase class, an abstract class @@ -101,7 +101,7 @@ static PyObject * _io__IOBase_seek_impl(PyObject *self, PyTypeObject *cls, PyObject *args) /*[clinic end generated code: output=1dd694ac9de260fa input=ebb5476eb22fc5d4]*/ { - _PyIO_State *state = IO_STATE(); + _PyIO_State *state = get_io_state_by_cls(cls); return iobase_unsupported(state, "seek"); } @@ -134,7 +134,7 @@ static PyObject * _io__IOBase_truncate_impl(PyObject *self, PyTypeObject *cls, PyObject *args) /*[clinic end generated code: output=b7eed4649cbe22c1 input=ad90582a1d8b5cc9]*/ { - _PyIO_State *state = IO_STATE(); + _PyIO_State *state = get_io_state_by_cls(cls); return iobase_unsupported(state, "truncate"); } @@ -220,24 +220,32 @@ _PyIOBase_check_closed(PyObject *self, PyObject *args) static PyObject * iobase_check_seekable(PyObject *self, PyObject *args) { - _PyIO_State *state = IO_STATE(); + _PyIO_State *state = find_io_state_by_def(Py_TYPE(self)); return _PyIOBase_check_seekable(state, self, args); } static PyObject * iobase_check_readable(PyObject *self, PyObject *args) { - _PyIO_State *state = IO_STATE(); + _PyIO_State *state = find_io_state_by_def(Py_TYPE(self)); return _PyIOBase_check_readable(state, self, args); } static PyObject * iobase_check_writable(PyObject *self, PyObject *args) { - _PyIO_State *state = IO_STATE(); + _PyIO_State *state = find_io_state_by_def(Py_TYPE(self)); return _PyIOBase_check_writable(state, self, args); } +PyObject * +_PyIOBase_cannot_pickle(PyObject *self, PyObject *args) +{ + PyErr_Format(PyExc_TypeError, + "cannot pickle '%.100s' instances", _PyType_Name(Py_TYPE(self))); + return NULL; +} + /* XXX: IOBase thinks it has to maintain its own internal state in `__IOBase_closed` and call flush() by itself, but it is redundant with whatever behaviour a non-trivial derived class will implement. */ @@ -351,6 +359,7 @@ _PyIOBase_finalize(PyObject *self) static int iobase_traverse(iobase *self, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(self)); Py_VISIT(self->dict); return 0; } @@ -380,11 +389,13 @@ iobase_dealloc(iobase *self) } return; } + PyTypeObject *tp = Py_TYPE(self); _PyObject_GC_UNTRACK(self); if (self->weakreflist != NULL) PyObject_ClearWeakRefs((PyObject *) self); Py_CLEAR(self->dict); - Py_TYPE(self)->tp_free((PyObject *) self); + tp->tp_free((PyObject *)self); + Py_DECREF(tp); } /* Inquiry methods */ @@ -523,7 +534,7 @@ static PyObject * _io__IOBase_fileno_impl(PyObject *self, PyTypeObject *cls) /*[clinic end generated code: output=7caaa32a6f4ada3d input=1927c8bea5c85099]*/ { - _PyIO_State *state = IO_STATE(); + _PyIO_State *state = get_io_state_by_cls(cls); return iobase_unsupported(state, "fileno"); } @@ -821,7 +832,9 @@ _io__IOBase_writelines(PyObject *self, PyObject *lines) Py_RETURN_NONE; } +#define clinic_state() (find_io_state_by_def(Py_TYPE(self))) #include "clinic/iobase.c.h" +#undef clinic_state static PyMethodDef iobase_methods[] = { _IO__IOBASE_SEEK_METHODDEF @@ -858,59 +871,34 @@ static PyGetSetDef iobase_getset[] = { {NULL} }; +static struct PyMemberDef iobase_members[] = { + {"__weaklistoffset__", T_PYSSIZET, offsetof(iobase, weakreflist), READONLY}, + {"__dictoffset__", T_PYSSIZET, offsetof(iobase, dict), READONLY}, + {NULL}, +}; + -PyTypeObject PyIOBase_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_io._IOBase", /*tp_name*/ - sizeof(iobase), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)iobase_dealloc, /*tp_dealloc*/ - 0, /*tp_vectorcall_offset*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_as_async*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash */ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE - | Py_TPFLAGS_HAVE_GC, /*tp_flags*/ - iobase_doc, /* tp_doc */ - (traverseproc)iobase_traverse, /* tp_traverse */ - (inquiry)iobase_clear, /* tp_clear */ - 0, /* tp_richcompare */ - offsetof(iobase, weakreflist), /* tp_weaklistoffset */ - iobase_iter, /* tp_iter */ - iobase_iternext, /* tp_iternext */ - iobase_methods, /* tp_methods */ - 0, /* tp_members */ - iobase_getset, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - offsetof(iobase, dict), /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - PyType_GenericNew, /* tp_new */ - 0, /* tp_free */ - 0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - 0, /* tp_del */ - 0, /* tp_version_tag */ - iobase_finalize, /* tp_finalize */ +static PyType_Slot iobase_slots[] = { + {Py_tp_dealloc, iobase_dealloc}, + {Py_tp_doc, (void *)iobase_doc}, + {Py_tp_traverse, iobase_traverse}, + {Py_tp_clear, iobase_clear}, + {Py_tp_iter, iobase_iter}, + {Py_tp_iternext, iobase_iternext}, + {Py_tp_methods, iobase_methods}, + {Py_tp_members, iobase_members}, + {Py_tp_getset, iobase_getset}, + {Py_tp_finalize, iobase_finalize}, + {0, NULL}, }; +PyType_Spec iobase_spec = { + .name = "_io._IOBase", + .basicsize = sizeof(iobase), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = iobase_slots, +}; /* * RawIOBase class, Inherits from IOBase. @@ -1045,6 +1033,13 @@ rawiobase_write(PyObject *self, PyObject *args) return NULL; } +static int +rawiobase_traverse(PyObject *self, visitproc visit, void *arg) +{ + Py_VISIT(Py_TYPE(self)); + return 0; +} + static PyMethodDef rawiobase_methods[] = { _IO__RAWIOBASE_READ_METHODDEF _IO__RAWIOBASE_READALL_METHODDEF @@ -1053,53 +1048,16 @@ static PyMethodDef rawiobase_methods[] = { {NULL, NULL} }; -PyTypeObject PyRawIOBase_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_io._RawIOBase", /*tp_name*/ - 0, /*tp_basicsize*/ - 0, /*tp_itemsize*/ - 0, /*tp_dealloc*/ - 0, /*tp_vectorcall_offset*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_as_async*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash */ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ - rawiobase_doc, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - rawiobase_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - &PyIOBase_Type, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ - 0, /* tp_free */ - 0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - 0, /* tp_del */ - 0, /* tp_version_tag */ - 0, /* tp_finalize */ +static PyType_Slot rawiobase_slots[] = { + {Py_tp_doc, (void *)rawiobase_doc}, + {Py_tp_methods, rawiobase_methods}, + {Py_tp_traverse, rawiobase_traverse}, + {0, NULL}, +}; + +PyType_Spec rawiobase_spec = { + .name = "_io._RawIOBase", + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = rawiobase_slots, }; diff --git a/Modules/_io/stringio.c b/Modules/_io/stringio.c index 13d3b870b39a81..3eb25704b4aa55 100644 --- a/Modules/_io/stringio.c +++ b/Modules/_io/stringio.c @@ -583,6 +583,9 @@ static int stringio_traverse(stringio *self, visitproc visit, void *arg) { Py_VISIT(Py_TYPE(self)); + Py_VISIT(self->readnl); + Py_VISIT(self->writenl); + Py_VISIT(self->decoder); Py_VISIT(self->dict); return 0; } @@ -590,6 +593,9 @@ stringio_traverse(stringio *self, visitproc visit, void *arg) static int stringio_clear(stringio *self) { + Py_CLEAR(self->readnl); + Py_CLEAR(self->writenl); + Py_CLEAR(self->decoder); Py_CLEAR(self->dict); return 0; } @@ -605,10 +611,7 @@ stringio_dealloc(stringio *self) self->buf = NULL; } _PyUnicodeWriter_Dealloc(&self->writer); - Py_CLEAR(self->readnl); - Py_CLEAR(self->writenl); - Py_CLEAR(self->decoder); - Py_CLEAR(self->dict); + (void)stringio_clear(self); if (self->weakreflist != NULL) { PyObject_ClearWeakRefs((PyObject *) self); } diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index 3cc292cc35102e..81dd3bed005a61 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -62,7 +62,7 @@ static PyObject * _io__TextIOBase_detach_impl(PyObject *self, PyTypeObject *cls) /*[clinic end generated code: output=50915f40c609eaa4 input=987ca3640d0a3776]*/ { - _PyIO_State *state = IO_STATE(); + _PyIO_State *state = get_io_state_by_cls(cls); return _unsupported(state, "detach"); } @@ -82,7 +82,7 @@ static PyObject * _io__TextIOBase_read_impl(PyObject *self, PyTypeObject *cls, PyObject *args) /*[clinic end generated code: output=3adf28998831f461 input=cee1e84664a20de0]*/ { - _PyIO_State *state = IO_STATE(); + _PyIO_State *state = get_io_state_by_cls(cls); return _unsupported(state, "read"); } @@ -102,7 +102,7 @@ _io__TextIOBase_readline_impl(PyObject *self, PyTypeObject *cls, PyObject *args) /*[clinic end generated code: output=3073a948d02319f3 input=58f801259f7ff3ef]*/ { - _PyIO_State *state = IO_STATE(); + _PyIO_State *state = get_io_state_by_cls(cls); return _unsupported(state, "readline"); } @@ -122,7 +122,7 @@ static PyObject * _io__TextIOBase_write_impl(PyObject *self, PyTypeObject *cls, PyObject *args) /*[clinic end generated code: output=5d985eb529472bc4 input=21b6961b5cba9496]*/ { - _PyIO_State *state = IO_STATE(); + _PyIO_State *state = get_io_state_by_cls(cls); return _unsupported(state, "write"); } @@ -164,6 +164,12 @@ textiobase_errors_get(PyObject *self, void *context) Py_RETURN_NONE; } +static int +textiobase_traverse(PyObject *self, visitproc visit, void *arg) +{ + Py_VISIT(Py_TYPE(self)); + return 0; +} static PyMethodDef textiobase_methods[] = { _IO__TEXTIOBASE_DETACH_METHODDEF @@ -180,57 +186,20 @@ static PyGetSetDef textiobase_getset[] = { {NULL} }; -PyTypeObject PyTextIOBase_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_io._TextIOBase", /*tp_name*/ - 0, /*tp_basicsize*/ - 0, /*tp_itemsize*/ - 0, /*tp_dealloc*/ - 0, /*tp_vectorcall_offset*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_as_async*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash */ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ - textiobase_doc, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - textiobase_methods, /* tp_methods */ - 0, /* tp_members */ - textiobase_getset, /* tp_getset */ - &PyIOBase_Type, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ - 0, /* tp_free */ - 0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - 0, /* tp_del */ - 0, /* tp_version_tag */ - 0, /* tp_finalize */ +static PyType_Slot textiobase_slots[] = { + {Py_tp_doc, (void *)textiobase_doc}, + {Py_tp_methods, textiobase_methods}, + {Py_tp_getset, textiobase_getset}, + {Py_tp_traverse, textiobase_traverse}, + {0, NULL}, }; +PyType_Spec textiobase_spec = { + .name = "_io._TextIOBase", + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = textiobase_slots, +}; /* IncrementalNewlineDecoder */ @@ -1456,7 +1425,7 @@ textiowrapper_dealloc(textio *self) _PyObject_GC_UNTRACK(self); if (self->weakreflist != NULL) PyObject_ClearWeakRefs((PyObject *)self); - textiowrapper_clear(self); + (void)textiowrapper_clear(self); tp->tp_free((PyObject *)self); Py_DECREF(tp); } @@ -3267,6 +3236,9 @@ static PyMethodDef textiowrapper_methods[] = { _IO_TEXTIOWRAPPER_SEEK_METHODDEF _IO_TEXTIOWRAPPER_TELL_METHODDEF _IO_TEXTIOWRAPPER_TRUNCATE_METHODDEF + + {"__reduce__", _PyIOBase_cannot_pickle, METH_VARARGS}, + {"__reduce_ex__", _PyIOBase_cannot_pickle, METH_VARARGS}, {NULL, NULL} }; diff --git a/Modules/_io/winconsoleio.c b/Modules/_io/winconsoleio.c index d65e247737a071..15f3053957da61 100644 --- a/Modules/_io/winconsoleio.c +++ b/Modules/_io/winconsoleio.c @@ -1096,7 +1096,7 @@ _io__WindowsConsoleIO_isatty_impl(winconsoleio *self) Py_RETURN_TRUE; } -#define clinic_state() (IO_STATE()) +#define clinic_state() (find_io_state_by_def(Py_TYPE(self))) #include "clinic/winconsoleio.c.h" #undef clinic_state diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 61f87c5eba60ed..c5dc0f44a38068 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -29,9 +29,6 @@ #include "pycore_unicodeobject.h" // _PyUnicode_InitTypes() #include "opcode.h" -extern PyStatus _PyIO_InitTypes(PyInterpreterState *interp); -extern void _PyIO_FiniTypes(PyInterpreterState *interp); - #include // setlocale() #include // getenv() @@ -706,11 +703,6 @@ pycore_init_types(PyInterpreterState *interp) return _PyStatus_ERR("failed to initialize an exception type"); } - status = _PyIO_InitTypes(interp); - if (_PyStatus_EXCEPTION(status)) { - return status; - } - status = _PyExc_InitGlobalObjects(interp); if (_PyStatus_EXCEPTION(status)) { return status; @@ -1667,8 +1659,6 @@ flush_std_files(void) static void finalize_interp_types(PyInterpreterState *interp) { - _PyIO_FiniTypes(interp); - _PyUnicode_FiniTypes(interp); _PySys_FiniTypes(interp); _PyExc_Fini(interp); diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv index 8afa92ef25d376..9863acdade308b 100644 --- a/Tools/c-analyzer/cpython/globals-to-fix.tsv +++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv @@ -317,10 +317,6 @@ Python/instrumentation.c - _PyInstrumentation_MISSING - ##----------------------- ## static types -Modules/_io/bufferedio.c - PyBufferedIOBase_Type - -Modules/_io/iobase.c - PyIOBase_Type - -Modules/_io/iobase.c - PyRawIOBase_Type - -Modules/_io/textio.c - PyTextIOBase_Type - Modules/_testcapi/vectorcall.c - MethodDescriptorBase_Type - Modules/_testcapi/vectorcall.c - MethodDescriptorDerived_Type - Modules/_testcapi/vectorcall.c - MethodDescriptorNopGet_Type - From b378d991f8cd41c33416e590cb83472cce1d6b98 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Mon, 15 May 2023 13:21:38 +0200 Subject: [PATCH 13/13] gh-101819: Fix _io clinic input for unused base class method stubs (#104418) When preparing the _io extension module for isolation, many methods were adapted to Argument Clinic. Some of these used the '*args: object' signature, which is incorrect. These are now corrected to an exact signature, and marked unused, since they are stub methods. --- Modules/_io/bufferedio.c | 26 +++++----- Modules/_io/clinic/bufferedio.c.h | 69 +++++++++++++++----------- Modules/_io/clinic/iobase.c.h | 58 ++++++++++++++-------- Modules/_io/clinic/textio.c.h | 80 ++++++++++++++++++++----------- Modules/_io/iobase.c | 21 ++++---- Modules/_io/textio.c | 23 +++++---- 6 files changed, 168 insertions(+), 109 deletions(-) diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c index d5cc047bd8d563..6f291c34496064 100644 --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -136,15 +136,15 @@ _io__BufferedIOBase_detach_impl(PyObject *self, PyTypeObject *cls) _io._BufferedIOBase.read cls: defining_class + size: int(unused=True) = -1 / - *args: object Read and return up to n bytes. -If the argument is omitted, None, or negative, read and +If the size argument is omitted, None, or negative, read and return all data until EOF. -If the argument is positive, and the underlying raw stream is +If the size argument is positive, and the underlying raw stream is not 'interactive', multiple raw reads may be issued to satisfy the byte count (unless EOF is reached first). However, for interactive raw streams (as well as sockets and pipes), @@ -159,8 +159,8 @@ mode and no data is available at the moment. static PyObject * _io__BufferedIOBase_read_impl(PyObject *self, PyTypeObject *cls, - PyObject *args) -/*[clinic end generated code: output=4521b30940fd7b67 input=390205758adc8510]*/ + int Py_UNUSED(size)) +/*[clinic end generated code: output=aceb2765587b0a29 input=824f6f910465e61a]*/ { _PyIO_State *state = get_io_state_by_cls(cls); return bufferediobase_unsupported(state, "read"); @@ -170,10 +170,10 @@ _io__BufferedIOBase_read_impl(PyObject *self, PyTypeObject *cls, _io._BufferedIOBase.read1 cls: defining_class + size: int(unused=True) = -1 / - *args: object -Read and return up to n bytes, with at most one read() call to the underlying raw stream. +Read and return up to size bytes, with at most one read() call to the underlying raw stream. Return an empty bytes object on EOF. A short result does not imply that EOF is imminent. @@ -181,8 +181,8 @@ A short result does not imply that EOF is imminent. static PyObject * _io__BufferedIOBase_read1_impl(PyObject *self, PyTypeObject *cls, - PyObject *args) -/*[clinic end generated code: output=636fd241c21e050a input=ef546a1238c5b41c]*/ + int Py_UNUSED(size)) +/*[clinic end generated code: output=2e7fc62972487eaa input=af76380e020fd9e6]*/ { _PyIO_State *state = get_io_state_by_cls(cls); return bufferediobase_unsupported(state, "read1"); @@ -192,10 +192,10 @@ _io__BufferedIOBase_read1_impl(PyObject *self, PyTypeObject *cls, _io._BufferedIOBase.write cls: defining_class + b: object(unused=True) / - *args: object -Write the given buffer to the IO stream. +Write buffer b to the IO stream. Return the number of bytes written, which is always the length of b in bytes. @@ -206,8 +206,8 @@ underlying raw stream cannot accept more data at the moment. static PyObject * _io__BufferedIOBase_write_impl(PyObject *self, PyTypeObject *cls, - PyObject *args) -/*[clinic end generated code: output=d51feea4bcac9892 input=f79b72c4dccb3dc2]*/ + PyObject *Py_UNUSED(b)) +/*[clinic end generated code: output=712c635246bf2306 input=9793f5c8f71029ad]*/ { _PyIO_State *state = get_io_state_by_cls(cls); return bufferediobase_unsupported(state, "write"); diff --git a/Modules/_io/clinic/bufferedio.c.h b/Modules/_io/clinic/bufferedio.c.h index e2ac90c54a10f0..4f40fdadf8ec85 100644 --- a/Modules/_io/clinic/bufferedio.c.h +++ b/Modules/_io/clinic/bufferedio.c.h @@ -108,15 +108,15 @@ _io__BufferedIOBase_detach(PyObject *self, PyTypeObject *cls, PyObject *const *a } PyDoc_STRVAR(_io__BufferedIOBase_read__doc__, -"read($self, /, *args)\n" +"read($self, size=-1, /)\n" "--\n" "\n" "Read and return up to n bytes.\n" "\n" -"If the argument is omitted, None, or negative, read and\n" +"If the size argument is omitted, None, or negative, read and\n" "return all data until EOF.\n" "\n" -"If the argument is positive, and the underlying raw stream is\n" +"If the size argument is positive, and the underlying raw stream is\n" "not \'interactive\', multiple raw reads may be issued to satisfy\n" "the byte count (unless EOF is reached first).\n" "However, for interactive raw streams (as well as sockets and pipes),\n" @@ -133,7 +133,7 @@ PyDoc_STRVAR(_io__BufferedIOBase_read__doc__, static PyObject * _io__BufferedIOBase_read_impl(PyObject *self, PyTypeObject *cls, - PyObject *args); + int Py_UNUSED(size)); static PyObject * _io__BufferedIOBase_read(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -145,7 +145,7 @@ _io__BufferedIOBase_read(PyObject *self, PyTypeObject *cls, PyObject *const *arg # define KWTUPLE NULL #endif - static const char * const _keywords[] = { NULL}; + static const char * const _keywords[] = {"", NULL}; static _PyArg_Parser _parser = { .keywords = _keywords, .fname = "read", @@ -153,25 +153,31 @@ _io__BufferedIOBase_read(PyObject *self, PyTypeObject *cls, PyObject *const *arg }; #undef KWTUPLE PyObject *argsbuf[1]; - PyObject *__clinic_args = NULL; + int size = -1; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); if (!args) { goto exit; } - __clinic_args = args[0]; - return_value = _io__BufferedIOBase_read_impl(self, cls, __clinic_args); + if (nargs < 1) { + goto skip_optional_posonly; + } + size = _PyLong_AsInt(args[0]); + if (size == -1 && PyErr_Occurred()) { + goto exit; + } +skip_optional_posonly: + return_value = _io__BufferedIOBase_read_impl(self, cls, size); exit: - Py_XDECREF(__clinic_args); return return_value; } PyDoc_STRVAR(_io__BufferedIOBase_read1__doc__, -"read1($self, /, *args)\n" +"read1($self, size=-1, /)\n" "--\n" "\n" -"Read and return up to n bytes, with at most one read() call to the underlying raw stream.\n" +"Read and return up to size bytes, with at most one read() call to the underlying raw stream.\n" "\n" "Return an empty bytes object on EOF.\n" "A short result does not imply that EOF is imminent."); @@ -181,7 +187,7 @@ PyDoc_STRVAR(_io__BufferedIOBase_read1__doc__, static PyObject * _io__BufferedIOBase_read1_impl(PyObject *self, PyTypeObject *cls, - PyObject *args); + int Py_UNUSED(size)); static PyObject * _io__BufferedIOBase_read1(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -193,7 +199,7 @@ _io__BufferedIOBase_read1(PyObject *self, PyTypeObject *cls, PyObject *const *ar # define KWTUPLE NULL #endif - static const char * const _keywords[] = { NULL}; + static const char * const _keywords[] = {"", NULL}; static _PyArg_Parser _parser = { .keywords = _keywords, .fname = "read1", @@ -201,25 +207,31 @@ _io__BufferedIOBase_read1(PyObject *self, PyTypeObject *cls, PyObject *const *ar }; #undef KWTUPLE PyObject *argsbuf[1]; - PyObject *__clinic_args = NULL; + int size = -1; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); if (!args) { goto exit; } - __clinic_args = args[0]; - return_value = _io__BufferedIOBase_read1_impl(self, cls, __clinic_args); + if (nargs < 1) { + goto skip_optional_posonly; + } + size = _PyLong_AsInt(args[0]); + if (size == -1 && PyErr_Occurred()) { + goto exit; + } +skip_optional_posonly: + return_value = _io__BufferedIOBase_read1_impl(self, cls, size); exit: - Py_XDECREF(__clinic_args); return return_value; } PyDoc_STRVAR(_io__BufferedIOBase_write__doc__, -"write($self, /, *args)\n" +"write($self, b, /)\n" "--\n" "\n" -"Write the given buffer to the IO stream.\n" +"Write buffer b to the IO stream.\n" "\n" "Return the number of bytes written, which is always\n" "the length of b in bytes.\n" @@ -232,7 +244,7 @@ PyDoc_STRVAR(_io__BufferedIOBase_write__doc__, static PyObject * _io__BufferedIOBase_write_impl(PyObject *self, PyTypeObject *cls, - PyObject *args); + PyObject *Py_UNUSED(b)); static PyObject * _io__BufferedIOBase_write(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -244,7 +256,7 @@ _io__BufferedIOBase_write(PyObject *self, PyTypeObject *cls, PyObject *const *ar # define KWTUPLE NULL #endif - static const char * const _keywords[] = { NULL}; + static const char * const _keywords[] = {"", NULL}; static _PyArg_Parser _parser = { .keywords = _keywords, .fname = "write", @@ -252,17 +264,16 @@ _io__BufferedIOBase_write(PyObject *self, PyTypeObject *cls, PyObject *const *ar }; #undef KWTUPLE PyObject *argsbuf[1]; - PyObject *__clinic_args = NULL; + PyObject *b; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); if (!args) { goto exit; } - __clinic_args = args[0]; - return_value = _io__BufferedIOBase_write_impl(self, cls, __clinic_args); + b = args[0]; + return_value = _io__BufferedIOBase_write_impl(self, cls, b); exit: - Py_XDECREF(__clinic_args); return return_value; } @@ -1087,4 +1098,4 @@ _io_BufferedRandom___init__(PyObject *self, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=d770e392e8702e12 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=b7ddf84a5bc2bf34 input=a9049054013a1b77]*/ diff --git a/Modules/_io/clinic/iobase.c.h b/Modules/_io/clinic/iobase.c.h index 7e6b3b5b78e8f6..773e0010477053 100644 --- a/Modules/_io/clinic/iobase.c.h +++ b/Modules/_io/clinic/iobase.c.h @@ -9,7 +9,7 @@ preserve PyDoc_STRVAR(_io__IOBase_seek__doc__, -"seek($self, /, *args)\n" +"seek($self, offset, whence=os.SEEK_SET, /)\n" "--\n" "\n" "Change the stream position to the given byte offset.\n" @@ -17,9 +17,9 @@ PyDoc_STRVAR(_io__IOBase_seek__doc__, "The offset is interpreted relative to the position indicated by whence.\n" "Values for whence are:\n" "\n" -"* 0 -- start of stream (the default); offset should be zero or positive\n" -"* 1 -- current stream position; offset may be negative\n" -"* 2 -- end of stream; offset is usually negative\n" +"* os.SEEK_SET or 0 -- start of stream (the default); offset should be zero or positive\n" +"* os.SEEK_CUR or 1 -- current stream position; offset may be negative\n" +"* os.SEEK_END or 2 -- end of stream; offset is usually negative\n" "\n" "Return the new absolute position."); @@ -27,7 +27,8 @@ PyDoc_STRVAR(_io__IOBase_seek__doc__, {"seek", _PyCFunction_CAST(_io__IOBase_seek), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _io__IOBase_seek__doc__}, static PyObject * -_io__IOBase_seek_impl(PyObject *self, PyTypeObject *cls, PyObject *args); +_io__IOBase_seek_impl(PyObject *self, PyTypeObject *cls, + int Py_UNUSED(offset), int Py_UNUSED(whence)); static PyObject * _io__IOBase_seek(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -39,25 +40,36 @@ _io__IOBase_seek(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ss # define KWTUPLE NULL #endif - static const char * const _keywords[] = { NULL}; + static const char * const _keywords[] = {"", "", NULL}; static _PyArg_Parser _parser = { .keywords = _keywords, .fname = "seek", .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[1]; - PyObject *__clinic_args = NULL; + PyObject *argsbuf[2]; + int offset; + int whence = 0; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); if (!args) { goto exit; } - __clinic_args = args[0]; - return_value = _io__IOBase_seek_impl(self, cls, __clinic_args); + offset = _PyLong_AsInt(args[0]); + if (offset == -1 && PyErr_Occurred()) { + goto exit; + } + if (nargs < 2) { + goto skip_optional_posonly; + } + whence = _PyLong_AsInt(args[1]); + if (whence == -1 && PyErr_Occurred()) { + goto exit; + } +skip_optional_posonly: + return_value = _io__IOBase_seek_impl(self, cls, offset, whence); exit: - Py_XDECREF(__clinic_args); return return_value; } @@ -80,7 +92,7 @@ _io__IOBase_tell(PyObject *self, PyObject *Py_UNUSED(ignored)) } PyDoc_STRVAR(_io__IOBase_truncate__doc__, -"truncate($self, /, *args)\n" +"truncate($self, size=None, /)\n" "--\n" "\n" "Truncate file to size bytes.\n" @@ -92,7 +104,8 @@ PyDoc_STRVAR(_io__IOBase_truncate__doc__, {"truncate", _PyCFunction_CAST(_io__IOBase_truncate), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _io__IOBase_truncate__doc__}, static PyObject * -_io__IOBase_truncate_impl(PyObject *self, PyTypeObject *cls, PyObject *args); +_io__IOBase_truncate_impl(PyObject *self, PyTypeObject *cls, + PyObject *Py_UNUSED(size)); static PyObject * _io__IOBase_truncate(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -104,7 +117,7 @@ _io__IOBase_truncate(PyObject *self, PyTypeObject *cls, PyObject *const *args, P # define KWTUPLE NULL #endif - static const char * const _keywords[] = { NULL}; + static const char * const _keywords[] = {"", NULL}; static _PyArg_Parser _parser = { .keywords = _keywords, .fname = "truncate", @@ -112,17 +125,20 @@ _io__IOBase_truncate(PyObject *self, PyTypeObject *cls, PyObject *const *args, P }; #undef KWTUPLE PyObject *argsbuf[1]; - PyObject *__clinic_args = NULL; + PyObject *size = Py_None; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); if (!args) { goto exit; } - __clinic_args = args[0]; - return_value = _io__IOBase_truncate_impl(self, cls, __clinic_args); + if (nargs < 1) { + goto skip_optional_posonly; + } + size = args[0]; +skip_optional_posonly: + return_value = _io__IOBase_truncate_impl(self, cls, size); exit: - Py_XDECREF(__clinic_args); return return_value; } @@ -420,4 +436,4 @@ _io__RawIOBase_readall(PyObject *self, PyObject *Py_UNUSED(ignored)) { return _io__RawIOBase_readall_impl(self); } -/*[clinic end generated code: output=63bc25a5bfcecaf0 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=301b22f8f75ce3dc input=a9049054013a1b77]*/ diff --git a/Modules/_io/clinic/textio.c.h b/Modules/_io/clinic/textio.c.h index 01965013ec6abc..33fc23bd4c0c69 100644 --- a/Modules/_io/clinic/textio.c.h +++ b/Modules/_io/clinic/textio.c.h @@ -33,7 +33,7 @@ _io__TextIOBase_detach(PyObject *self, PyTypeObject *cls, PyObject *const *args, } PyDoc_STRVAR(_io__TextIOBase_read__doc__, -"read($self, /, *args)\n" +"read($self, size=-1, /)\n" "--\n" "\n" "Read at most size characters from stream.\n" @@ -45,7 +45,8 @@ PyDoc_STRVAR(_io__TextIOBase_read__doc__, {"read", _PyCFunction_CAST(_io__TextIOBase_read), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _io__TextIOBase_read__doc__}, static PyObject * -_io__TextIOBase_read_impl(PyObject *self, PyTypeObject *cls, PyObject *args); +_io__TextIOBase_read_impl(PyObject *self, PyTypeObject *cls, + int Py_UNUSED(size)); static PyObject * _io__TextIOBase_read(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -57,7 +58,7 @@ _io__TextIOBase_read(PyObject *self, PyTypeObject *cls, PyObject *const *args, P # define KWTUPLE NULL #endif - static const char * const _keywords[] = { NULL}; + static const char * const _keywords[] = {"", NULL}; static _PyArg_Parser _parser = { .keywords = _keywords, .fname = "read", @@ -65,34 +66,41 @@ _io__TextIOBase_read(PyObject *self, PyTypeObject *cls, PyObject *const *args, P }; #undef KWTUPLE PyObject *argsbuf[1]; - PyObject *__clinic_args = NULL; + int size = -1; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); if (!args) { goto exit; } - __clinic_args = args[0]; - return_value = _io__TextIOBase_read_impl(self, cls, __clinic_args); + if (nargs < 1) { + goto skip_optional_posonly; + } + size = _PyLong_AsInt(args[0]); + if (size == -1 && PyErr_Occurred()) { + goto exit; + } +skip_optional_posonly: + return_value = _io__TextIOBase_read_impl(self, cls, size); exit: - Py_XDECREF(__clinic_args); return return_value; } PyDoc_STRVAR(_io__TextIOBase_readline__doc__, -"readline($self, /, *args)\n" +"readline($self, size=-1, /)\n" "--\n" "\n" "Read until newline or EOF.\n" "\n" -"Return an empty string if EOF is hit immediately."); +"Return an empty string if EOF is hit immediately.\n" +"If size is specified, at most size characters will be read."); #define _IO__TEXTIOBASE_READLINE_METHODDEF \ {"readline", _PyCFunction_CAST(_io__TextIOBase_readline), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _io__TextIOBase_readline__doc__}, static PyObject * _io__TextIOBase_readline_impl(PyObject *self, PyTypeObject *cls, - PyObject *args); + int Py_UNUSED(size)); static PyObject * _io__TextIOBase_readline(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -104,7 +112,7 @@ _io__TextIOBase_readline(PyObject *self, PyTypeObject *cls, PyObject *const *arg # define KWTUPLE NULL #endif - static const char * const _keywords[] = { NULL}; + static const char * const _keywords[] = {"", NULL}; static _PyArg_Parser _parser = { .keywords = _keywords, .fname = "readline", @@ -112,25 +120,31 @@ _io__TextIOBase_readline(PyObject *self, PyTypeObject *cls, PyObject *const *arg }; #undef KWTUPLE PyObject *argsbuf[1]; - PyObject *__clinic_args = NULL; + int size = -1; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); if (!args) { goto exit; } - __clinic_args = args[0]; - return_value = _io__TextIOBase_readline_impl(self, cls, __clinic_args); + if (nargs < 1) { + goto skip_optional_posonly; + } + size = _PyLong_AsInt(args[0]); + if (size == -1 && PyErr_Occurred()) { + goto exit; + } +skip_optional_posonly: + return_value = _io__TextIOBase_readline_impl(self, cls, size); exit: - Py_XDECREF(__clinic_args); return return_value; } PyDoc_STRVAR(_io__TextIOBase_write__doc__, -"write($self, /, *args)\n" +"write($self, s, /)\n" "--\n" "\n" -"Write string to stream.\n" +"Write string s to stream.\n" "\n" "Return the number of characters written\n" "(which is always equal to the length of the string)."); @@ -139,7 +153,8 @@ PyDoc_STRVAR(_io__TextIOBase_write__doc__, {"write", _PyCFunction_CAST(_io__TextIOBase_write), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _io__TextIOBase_write__doc__}, static PyObject * -_io__TextIOBase_write_impl(PyObject *self, PyTypeObject *cls, PyObject *args); +_io__TextIOBase_write_impl(PyObject *self, PyTypeObject *cls, + const char *Py_UNUSED(s)); static PyObject * _io__TextIOBase_write(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -151,7 +166,7 @@ _io__TextIOBase_write(PyObject *self, PyTypeObject *cls, PyObject *const *args, # define KWTUPLE NULL #endif - static const char * const _keywords[] = { NULL}; + static const char * const _keywords[] = {"", NULL}; static _PyArg_Parser _parser = { .keywords = _keywords, .fname = "write", @@ -159,17 +174,28 @@ _io__TextIOBase_write(PyObject *self, PyTypeObject *cls, PyObject *const *args, }; #undef KWTUPLE PyObject *argsbuf[1]; - PyObject *__clinic_args = NULL; + const char *s; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); if (!args) { goto exit; } - __clinic_args = args[0]; - return_value = _io__TextIOBase_write_impl(self, cls, __clinic_args); + if (!PyUnicode_Check(args[0])) { + _PyArg_BadArgument("write", "argument 1", "str", args[0]); + goto exit; + } + Py_ssize_t s_length; + s = PyUnicode_AsUTF8AndSize(args[0], &s_length); + if (s == NULL) { + goto exit; + } + if (strlen(s) != (size_t)s_length) { + PyErr_SetString(PyExc_ValueError, "embedded null character"); + goto exit; + } + return_value = _io__TextIOBase_write_impl(self, cls, s); exit: - Py_XDECREF(__clinic_args); return return_value; } @@ -934,4 +960,4 @@ _io_TextIOWrapper_close(textio *self, PyObject *Py_UNUSED(ignored)) { return _io_TextIOWrapper_close_impl(self); } -/*[clinic end generated code: output=d800e5a8a50d6720 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=42f592331302973f input=a9049054013a1b77]*/ diff --git a/Modules/_io/iobase.c b/Modules/_io/iobase.c index 764c5fb3320072..14d48813aefe83 100644 --- a/Modules/_io/iobase.c +++ b/Modules/_io/iobase.c @@ -82,24 +82,26 @@ iobase_unsupported(_PyIO_State *state, const char *message) /*[clinic input] _io._IOBase.seek cls: defining_class + offset: int(unused=True) + whence: int(unused=True, c_default='0') = os.SEEK_SET / - *args: object Change the stream position to the given byte offset. The offset is interpreted relative to the position indicated by whence. Values for whence are: -* 0 -- start of stream (the default); offset should be zero or positive -* 1 -- current stream position; offset may be negative -* 2 -- end of stream; offset is usually negative +* os.SEEK_SET or 0 -- start of stream (the default); offset should be zero or positive +* os.SEEK_CUR or 1 -- current stream position; offset may be negative +* os.SEEK_END or 2 -- end of stream; offset is usually negative Return the new absolute position. [clinic start generated code]*/ static PyObject * -_io__IOBase_seek_impl(PyObject *self, PyTypeObject *cls, PyObject *args) -/*[clinic end generated code: output=1dd694ac9de260fa input=ebb5476eb22fc5d4]*/ +_io__IOBase_seek_impl(PyObject *self, PyTypeObject *cls, + int Py_UNUSED(offset), int Py_UNUSED(whence)) +/*[clinic end generated code: output=8bd74ea6538ded53 input=8d4e6adcd08292f2]*/ { _PyIO_State *state = get_io_state_by_cls(cls); return iobase_unsupported(state, "seek"); @@ -121,8 +123,8 @@ _io__IOBase_tell_impl(PyObject *self) /*[clinic input] _io._IOBase.truncate cls: defining_class + size: object(unused=True) = None / - *args: object Truncate file to size bytes. @@ -131,8 +133,9 @@ as reported by tell(). Return the new size. [clinic start generated code]*/ static PyObject * -_io__IOBase_truncate_impl(PyObject *self, PyTypeObject *cls, PyObject *args) -/*[clinic end generated code: output=b7eed4649cbe22c1 input=ad90582a1d8b5cc9]*/ +_io__IOBase_truncate_impl(PyObject *self, PyTypeObject *cls, + PyObject *Py_UNUSED(size)) +/*[clinic end generated code: output=2013179bff1fe8ef input=660ac20936612c27]*/ { _PyIO_State *state = get_io_state_by_cls(cls); return iobase_unsupported(state, "truncate"); diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index 81dd3bed005a61..e858a1fb498f82 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -69,8 +69,8 @@ _io__TextIOBase_detach_impl(PyObject *self, PyTypeObject *cls) /*[clinic input] _io._TextIOBase.read cls: defining_class + size: int(unused=True) = -1 / - *args: object Read at most size characters from stream. @@ -79,8 +79,9 @@ If size is negative or omitted, read until EOF. [clinic start generated code]*/ static PyObject * -_io__TextIOBase_read_impl(PyObject *self, PyTypeObject *cls, PyObject *args) -/*[clinic end generated code: output=3adf28998831f461 input=cee1e84664a20de0]*/ +_io__TextIOBase_read_impl(PyObject *self, PyTypeObject *cls, + int Py_UNUSED(size)) +/*[clinic end generated code: output=51a5178a309ce647 input=f5e37720f9fc563f]*/ { _PyIO_State *state = get_io_state_by_cls(cls); return _unsupported(state, "read"); @@ -89,18 +90,19 @@ _io__TextIOBase_read_impl(PyObject *self, PyTypeObject *cls, PyObject *args) /*[clinic input] _io._TextIOBase.readline cls: defining_class + size: int(unused=True) = -1 / - *args: object Read until newline or EOF. Return an empty string if EOF is hit immediately. +If size is specified, at most size characters will be read. [clinic start generated code]*/ static PyObject * _io__TextIOBase_readline_impl(PyObject *self, PyTypeObject *cls, - PyObject *args) -/*[clinic end generated code: output=3073a948d02319f3 input=58f801259f7ff3ef]*/ + int Py_UNUSED(size)) +/*[clinic end generated code: output=3f47d7966d6d074e input=42eafec94107fa27]*/ { _PyIO_State *state = get_io_state_by_cls(cls); return _unsupported(state, "readline"); @@ -109,18 +111,19 @@ _io__TextIOBase_readline_impl(PyObject *self, PyTypeObject *cls, /*[clinic input] _io._TextIOBase.write cls: defining_class + s: str(unused=True) / - *args: object -Write string to stream. +Write string s to stream. Return the number of characters written (which is always equal to the length of the string). [clinic start generated code]*/ static PyObject * -_io__TextIOBase_write_impl(PyObject *self, PyTypeObject *cls, PyObject *args) -/*[clinic end generated code: output=5d985eb529472bc4 input=21b6961b5cba9496]*/ +_io__TextIOBase_write_impl(PyObject *self, PyTypeObject *cls, + const char *Py_UNUSED(s)) +/*[clinic end generated code: output=18b28231460275de input=e9cabaa5f6732b07]*/ { _PyIO_State *state = get_io_state_by_cls(cls); return _unsupported(state, "write");