From 01415213d72504eafc159721a8f55d57b374fd9c Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Thu, 31 Oct 2024 10:14:37 -0400 Subject: [PATCH 001/219] gh-126223: Propagate unicode errors in `_interpreters.create()` (#126224) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: sobolevn Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Lib/test/test_interpreters/test_api.py | 3 +++ .../Library/2024-10-30-23-42-44.gh-issue-126223.k2qooc.rst | 2 ++ Modules/_interpretersmodule.c | 6 +++++- 3 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2024-10-30-23-42-44.gh-issue-126223.k2qooc.rst diff --git a/Lib/test/test_interpreters/test_api.py b/Lib/test/test_interpreters/test_api.py index 5e3d7a052bae91..a9befbba64daa0 100644 --- a/Lib/test/test_interpreters/test_api.py +++ b/Lib/test/test_interpreters/test_api.py @@ -54,6 +54,9 @@ def test_in_main(self): self.assertIsInstance(interp, interpreters.Interpreter) self.assertIn(interp, interpreters.list_all()) + # GH-126221: Passing an invalid Unicode character used to cause a SystemError + self.assertRaises(UnicodeEncodeError, _interpreters.create, '\udc80') + def test_in_thread(self): lock = threading.Lock() interp = None diff --git a/Misc/NEWS.d/next/Library/2024-10-30-23-42-44.gh-issue-126223.k2qooc.rst b/Misc/NEWS.d/next/Library/2024-10-30-23-42-44.gh-issue-126223.k2qooc.rst new file mode 100644 index 00000000000000..fee391c030b941 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-10-30-23-42-44.gh-issue-126223.k2qooc.rst @@ -0,0 +1,2 @@ +Raise a :exc:`UnicodeEncodeError` instead of a :exc:`SystemError` upon +calling :func:`!_interpreters.create` with an invalid Unicode character. diff --git a/Modules/_interpretersmodule.c b/Modules/_interpretersmodule.c index 6f3392fe6ea28d..63f2bb38768511 100644 --- a/Modules/_interpretersmodule.c +++ b/Modules/_interpretersmodule.c @@ -402,7 +402,11 @@ config_from_object(PyObject *configobj, PyInterpreterConfig *config) } } else if (PyUnicode_Check(configobj)) { - if (init_named_config(config, PyUnicode_AsUTF8(configobj)) < 0) { + const char *utf8name = PyUnicode_AsUTF8(configobj); + if (utf8name == NULL) { + return -1; + } + if (init_named_config(config, utf8name) < 0) { return -1; } } From 94639f6b7182c2e1a82f2f907b03b5b15202acfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Thu, 31 Oct 2024 15:24:07 +0100 Subject: [PATCH 002/219] gh-126240: handle `NULL` returned by `_Py_asdl_expr_seq_new` (#126241) check return value of `_Py_asdl_expr_seq_new` --- Parser/action_helpers.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Parser/action_helpers.c b/Parser/action_helpers.c index cb21777f566189..5ac1dd7813689c 100644 --- a/Parser/action_helpers.c +++ b/Parser/action_helpers.c @@ -1128,6 +1128,9 @@ expr_ty _PyPegen_collect_call_seqs(Parser *p, asdl_expr_seq *a, asdl_seq *b, } asdl_expr_seq *args = _Py_asdl_expr_seq_new(total_len, arena); + if (args == NULL) { + return NULL; + } Py_ssize_t i = 0; for (i = 0; i < args_len; i++) { @@ -1298,6 +1301,9 @@ unpack_top_level_joined_strs(Parser *p, asdl_expr_seq *raw_expressions) } asdl_expr_seq *expressions = _Py_asdl_expr_seq_new(req_size, p->arena); + if (expressions == NULL) { + return NULL; + } Py_ssize_t raw_index, req_index = 0; for (raw_index = 0; raw_index < raw_size; raw_index++) { @@ -1490,6 +1496,9 @@ expr_ty _PyPegen_formatted_value(Parser *p, expr_ty expression, Token *debug, Re } asdl_expr_seq *values = _Py_asdl_expr_seq_new(2, arena); + if (values == NULL) { + return NULL; + } asdl_seq_SET(values, 0, debug_text); asdl_seq_SET(values, 1, formatted_value); return _PyAST_JoinedStr(values, lineno, col_offset, debug_end_line, debug_end_offset, p->arena); From 3275cb19530fb5c7115cf8313f1ada9621ed3a92 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Thu, 31 Oct 2024 17:37:47 +0300 Subject: [PATCH 003/219] gh-101123: Adapt vararg functions in the math module to Argument Clinic (#126235) This implicitly fixes the math.hypot signature, which was previously incomprehensible to inspect.signature(). --- Modules/clinic/mathmodule.c.h | 100 +++++++++++++++++++++++++++++++++- Modules/mathmodule.c | 80 +++++++++++++++------------ 2 files changed, 143 insertions(+), 37 deletions(-) diff --git a/Modules/clinic/mathmodule.c.h b/Modules/clinic/mathmodule.c.h index 81eec310ddb21d..7d0b98d5502267 100644 --- a/Modules/clinic/mathmodule.c.h +++ b/Modules/clinic/mathmodule.c.h @@ -8,6 +8,64 @@ preserve #endif #include "pycore_modsupport.h" // _PyArg_CheckPositional() +PyDoc_STRVAR(math_gcd__doc__, +"gcd($module, /, *integers)\n" +"--\n" +"\n" +"Greatest Common Divisor."); + +#define MATH_GCD_METHODDEF \ + {"gcd", _PyCFunction_CAST(math_gcd), METH_FASTCALL, math_gcd__doc__}, + +static PyObject * +math_gcd_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args); + +static PyObject * +math_gcd(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + Py_ssize_t nvararg = nargs - 0; + PyObject *const *__clinic_args = NULL; + + if (!_PyArg_CheckPositional("gcd", nargs, 0, PY_SSIZE_T_MAX)) { + goto exit; + } + __clinic_args = args + 0; + return_value = math_gcd_impl(module, nvararg, __clinic_args); + +exit: + return return_value; +} + +PyDoc_STRVAR(math_lcm__doc__, +"lcm($module, /, *integers)\n" +"--\n" +"\n" +"Least Common Multiple."); + +#define MATH_LCM_METHODDEF \ + {"lcm", _PyCFunction_CAST(math_lcm), METH_FASTCALL, math_lcm__doc__}, + +static PyObject * +math_lcm_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args); + +static PyObject * +math_lcm(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + Py_ssize_t nvararg = nargs - 0; + PyObject *const *__clinic_args = NULL; + + if (!_PyArg_CheckPositional("lcm", nargs, 0, PY_SSIZE_T_MAX)) { + goto exit; + } + __clinic_args = args + 0; + return_value = math_lcm_impl(module, nvararg, __clinic_args); + +exit: + return return_value; +} + PyDoc_STRVAR(math_ceil__doc__, "ceil($module, x, /)\n" "--\n" @@ -351,6 +409,46 @@ math_dist(PyObject *module, PyObject *const *args, Py_ssize_t nargs) return return_value; } +PyDoc_STRVAR(math_hypot__doc__, +"hypot($module, /, *coordinates)\n" +"--\n" +"\n" +"Multidimensional Euclidean distance from the origin to a point.\n" +"\n" +"Roughly equivalent to:\n" +" sqrt(sum(x**2 for x in coordinates))\n" +"\n" +"For a two dimensional point (x, y), gives the hypotenuse\n" +"using the Pythagorean theorem: sqrt(x*x + y*y).\n" +"\n" +"For example, the hypotenuse of a 3/4/5 right triangle is:\n" +"\n" +" >>> hypot(3.0, 4.0)\n" +" 5.0"); + +#define MATH_HYPOT_METHODDEF \ + {"hypot", _PyCFunction_CAST(math_hypot), METH_FASTCALL, math_hypot__doc__}, + +static PyObject * +math_hypot_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args); + +static PyObject * +math_hypot(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + Py_ssize_t nvararg = nargs - 0; + PyObject *const *__clinic_args = NULL; + + if (!_PyArg_CheckPositional("hypot", nargs, 0, PY_SSIZE_T_MAX)) { + goto exit; + } + __clinic_args = args + 0; + return_value = math_hypot_impl(module, nvararg, __clinic_args); + +exit: + return return_value; +} + PyDoc_STRVAR(math_sumprod__doc__, "sumprod($module, p, q, /)\n" "--\n" @@ -1011,4 +1109,4 @@ math_ulp(PyObject *module, PyObject *arg) exit: return return_value; } -/*[clinic end generated code: output=755da3b1dbd9e45f input=a9049054013a1b77]*/ +/*[clinic end generated code: output=ee0a2f6bd1220061 input=a9049054013a1b77]*/ diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 058f57770755aa..ad23dadd7b86cc 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -719,8 +719,17 @@ m_log10(double x) } +/*[clinic input] +math.gcd + + *integers as args: object + +Greatest Common Divisor. +[clinic start generated code]*/ + static PyObject * -math_gcd(PyObject *module, PyObject * const *args, Py_ssize_t nargs) +math_gcd_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args) +/*[clinic end generated code: output=b57687fcf431c1b8 input=94e675b7ceeaf0c9]*/ { // Fast-path for the common case: gcd(int, int) if (nargs == 2 && PyLong_CheckExact(args[0]) && PyLong_CheckExact(args[1])) @@ -763,12 +772,6 @@ math_gcd(PyObject *module, PyObject * const *args, Py_ssize_t nargs) return res; } -PyDoc_STRVAR(math_gcd_doc, -"gcd($module, *integers)\n" -"--\n" -"\n" -"Greatest Common Divisor."); - static PyObject * long_lcm(PyObject *a, PyObject *b) @@ -798,8 +801,17 @@ long_lcm(PyObject *a, PyObject *b) } +/*[clinic input] +math.lcm + + *integers as args: object + +Least Common Multiple. +[clinic start generated code]*/ + static PyObject * -math_lcm(PyObject *module, PyObject * const *args, Py_ssize_t nargs) +math_lcm_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args) +/*[clinic end generated code: output=f3eff0c25e4d7030 input=e64c33e85f4c47c6]*/ { PyObject *res, *x; Py_ssize_t i; @@ -839,13 +851,6 @@ math_lcm(PyObject *module, PyObject * const *args, Py_ssize_t nargs) } -PyDoc_STRVAR(math_lcm_doc, -"lcm($module, *integers)\n" -"--\n" -"\n" -"Least Common Multiple."); - - /* Call is_error when errno != 0, and where x is the result libm * returned. is_error will usually set up an exception and return * true (1), but may return false (0) without setting up an exception. @@ -2621,9 +2626,28 @@ math_dist_impl(PyObject *module, PyObject *p, PyObject *q) return NULL; } -/* AC: cannot convert yet, waiting for *args support */ +/*[clinic input] +math.hypot + + *coordinates as args: object + +Multidimensional Euclidean distance from the origin to a point. + +Roughly equivalent to: + sqrt(sum(x**2 for x in coordinates)) + +For a two dimensional point (x, y), gives the hypotenuse +using the Pythagorean theorem: sqrt(x*x + y*y). + +For example, the hypotenuse of a 3/4/5 right triangle is: + + >>> hypot(3.0, 4.0) + 5.0 +[clinic start generated code]*/ + static PyObject * -math_hypot(PyObject *self, PyObject *const *args, Py_ssize_t nargs) +math_hypot_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args) +/*[clinic end generated code: output=dcb6d4b7a1102ee1 input=5c0061a2d11235ed]*/ { Py_ssize_t i; PyObject *item; @@ -2664,22 +2688,6 @@ math_hypot(PyObject *self, PyObject *const *args, Py_ssize_t nargs) #undef NUM_STACK_ELEMS -PyDoc_STRVAR(math_hypot_doc, - "hypot(*coordinates) -> value\n\n\ -Multidimensional Euclidean distance from the origin to a point.\n\ -\n\ -Roughly equivalent to:\n\ - sqrt(sum(x**2 for x in coordinates))\n\ -\n\ -For a two dimensional point (x, y), gives the hypotenuse\n\ -using the Pythagorean theorem: sqrt(x*x + y*y).\n\ -\n\ -For example, the hypotenuse of a 3/4/5 right triangle is:\n\ -\n\ - >>> hypot(3.0, 4.0)\n\ - 5.0\n\ -"); - /** sumprod() ***************************************************************/ /* Forward declaration */ @@ -4112,14 +4120,14 @@ static PyMethodDef math_methods[] = { MATH_FREXP_METHODDEF MATH_FSUM_METHODDEF {"gamma", math_gamma, METH_O, math_gamma_doc}, - {"gcd", _PyCFunction_CAST(math_gcd), METH_FASTCALL, math_gcd_doc}, - {"hypot", _PyCFunction_CAST(math_hypot), METH_FASTCALL, math_hypot_doc}, + MATH_GCD_METHODDEF + MATH_HYPOT_METHODDEF MATH_ISCLOSE_METHODDEF MATH_ISFINITE_METHODDEF MATH_ISINF_METHODDEF MATH_ISNAN_METHODDEF MATH_ISQRT_METHODDEF - {"lcm", _PyCFunction_CAST(math_lcm), METH_FASTCALL, math_lcm_doc}, + MATH_LCM_METHODDEF MATH_LDEXP_METHODDEF {"lgamma", math_lgamma, METH_O, math_lgamma_doc}, {"log", _PyCFunction_CAST(math_log), METH_FASTCALL, math_log_doc}, From 0e8665554b2f1334e530fd6de5b3a4e908405419 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Thu, 31 Oct 2024 18:14:47 +0100 Subject: [PATCH 004/219] gh-126080: fix UAF on `task->task_context` in `task_call_step_soon` due to an evil `loop.__getattribute__` (#126120) --- .../Library/2024-10-29-10-38-28.gh-issue-126080.qKRBuo.rst | 3 +++ Modules/_asynciomodule.c | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2024-10-29-10-38-28.gh-issue-126080.qKRBuo.rst diff --git a/Misc/NEWS.d/next/Library/2024-10-29-10-38-28.gh-issue-126080.qKRBuo.rst b/Misc/NEWS.d/next/Library/2024-10-29-10-38-28.gh-issue-126080.qKRBuo.rst new file mode 100644 index 00000000000000..e54ac17b217c92 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-10-29-10-38-28.gh-issue-126080.qKRBuo.rst @@ -0,0 +1,3 @@ +Fix a use-after-free crash on :class:`asyncio.Task` objects for which the +underlying event loop implements an evil :meth:`~object.__getattribute__`. +Reported by Nico-Posada. Patch by Bénédikt Tran. diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index c2500fbd692d4d..7483e9c0f43acf 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -2738,7 +2738,11 @@ task_call_step_soon(asyncio_state *state, TaskObj *task, PyObject *arg) return -1; } - int ret = call_soon(state, task->task_loop, cb, NULL, task->task_context); + // Beware: An evil call_soon could alter task_context. + // See: https://github.com/python/cpython/issues/126080. + PyObject *task_context = Py_NewRef(task->task_context); + int ret = call_soon(state, task->task_loop, cb, NULL, task_context); + Py_DECREF(task_context); Py_DECREF(cb); return ret; } From dd3c0fa3fd2795326dae0e0ed63c668f5506cf32 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 31 Oct 2024 14:05:40 -0500 Subject: [PATCH 005/219] gh-126156: Improve performance of creating `Morsel` objects (#126157) Replaces the manually constructed loop with a call to `dict.update` --- Lib/http/cookies.py | 5 +++-- .../Library/2024-10-30-00-12-22.gh-issue-126156.BOSqv0.rst | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-10-30-00-12-22.gh-issue-126156.BOSqv0.rst diff --git a/Lib/http/cookies.py b/Lib/http/cookies.py index 6b9ed24ad8ec78..d7e8d08b2d92c1 100644 --- a/Lib/http/cookies.py +++ b/Lib/http/cookies.py @@ -266,6 +266,8 @@ class Morsel(dict): "samesite" : "SameSite", } + _reserved_defaults = dict.fromkeys(_reserved, "") + _flags = {'secure', 'httponly'} def __init__(self): @@ -273,8 +275,7 @@ def __init__(self): self._key = self._value = self._coded_value = None # Set default attributes - for key in self._reserved: - dict.__setitem__(self, key, "") + dict.update(self, self._reserved_defaults) @property def key(self): diff --git a/Misc/NEWS.d/next/Library/2024-10-30-00-12-22.gh-issue-126156.BOSqv0.rst b/Misc/NEWS.d/next/Library/2024-10-30-00-12-22.gh-issue-126156.BOSqv0.rst new file mode 100644 index 00000000000000..4fe18275ab9384 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-10-30-00-12-22.gh-issue-126156.BOSqv0.rst @@ -0,0 +1 @@ +Improved performances of creating :py:class:`~http.cookies.Morsel` objects by a factor of 3.8x. From d0abd0b826cfa574d1515c6f8459c9901939388f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alperen=20Kele=C5=9F?= Date: Thu, 31 Oct 2024 17:44:34 -0400 Subject: [PATCH 006/219] gh-126256: Update time.rst to use the same clock as instead of the same clock than (#126257) Update time.rst to use `the same clock as` instead of `the same clock than` The time documentation uses the same clock than time.monotonic instead of the same clock as time.monotonic, which is grammatically false. This PR fixes changes two instances of `the same clock than` to `the same clock as`. --- Doc/library/time.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/time.rst b/Doc/library/time.rst index 8e29e57d00f9b2..9cd5db768e9853 100644 --- a/Doc/library/time.rst +++ b/Doc/library/time.rst @@ -327,7 +327,7 @@ Functions .. impl-detail:: - On CPython, use the same clock than :func:`time.monotonic` and is a + On CPython, use the same clock as :func:`time.monotonic` and is a monotonic clock, i.e. a clock that cannot go backwards. Use :func:`perf_counter_ns` to avoid the precision loss caused by the @@ -339,7 +339,7 @@ Functions On Windows, the function is now system-wide. .. versionchanged:: 3.13 - Use the same clock than :func:`time.monotonic`. + Use the same clock as :func:`time.monotonic`. .. function:: perf_counter_ns() -> int From 260843df1bd8a28596b9a377d8266e2547f7eedc Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Fri, 1 Nov 2024 01:19:01 +0000 Subject: [PATCH 007/219] GH-125413: Add `pathlib.Path.scandir()` method (#126060) Add `pathlib.Path.scandir()` as a trivial wrapper of `os.scandir()`. This will be used to implement several `PathBase` methods more efficiently, including methods that provide `Path.copy()`. --- Doc/library/pathlib.rst | 29 ++++++++ Doc/whatsnew/3.14.rst | 6 ++ Lib/pathlib/_abc.py | 12 +++- Lib/pathlib/_local.py | 8 +++ Lib/test/test_pathlib/test_pathlib_abc.py | 67 ++++++++++++++++--- ...-10-28-01-24-52.gh-issue-125413.Jat5kq.rst | 3 + 6 files changed, 114 insertions(+), 11 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-10-28-01-24-52.gh-issue-125413.Jat5kq.rst diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index 4380122eb1be7d..b6fb36554f7cec 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -1289,6 +1289,35 @@ Reading directories raised. +.. method:: Path.scandir() + + When the path points to a directory, return an iterator of + :class:`os.DirEntry` objects corresponding to entries in the directory. The + returned iterator supports the :term:`context manager` protocol. It is + implemented using :func:`os.scandir` and gives the same guarantees. + + Using :meth:`~Path.scandir` instead of :meth:`~Path.iterdir` can + significantly increase the performance of code that also needs file type or + file attribute information, because :class:`os.DirEntry` objects expose + this information if the operating system provides it when scanning a + directory. + + The following example displays the names of subdirectories. The + ``entry.is_dir()`` check will generally not make an additional system call:: + + >>> p = Path('docs') + >>> with p.scandir() as entries: + ... for entry in entries: + ... if entry.is_dir(): + ... entry.name + ... + '_templates' + '_build' + '_static' + + .. versionadded:: 3.14 + + .. method:: Path.glob(pattern, *, case_sensitive=None, recurse_symlinks=False) Glob the given relative *pattern* in the directory represented by this path, diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 7f9e3107a6e1a0..48314f9c98c036 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -380,6 +380,12 @@ pathlib (Contributed by Barney Gale in :gh:`73991`.) +* Add :meth:`pathlib.Path.scandir` to scan a directory and return an iterator + of :class:`os.DirEntry` objects. This is exactly equivalent to calling + :func:`os.scandir` on a path object. + + (Contributed by Barney Gale in :gh:`125413`.) + pdb --- diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py index 11c8018b28f26b..dfff8b460d1bf1 100644 --- a/Lib/pathlib/_abc.py +++ b/Lib/pathlib/_abc.py @@ -639,13 +639,23 @@ def write_text(self, data, encoding=None, errors=None, newline=None): with self.open(mode='w', encoding=encoding, errors=errors, newline=newline) as f: return f.write(data) + def scandir(self): + """Yield os.DirEntry objects of the directory contents. + + The children are yielded in arbitrary order, and the + special entries '.' and '..' are not included. + """ + raise UnsupportedOperation(self._unsupported_msg('scandir()')) + def iterdir(self): """Yield path objects of the directory contents. The children are yielded in arbitrary order, and the special entries '.' and '..' are not included. """ - raise UnsupportedOperation(self._unsupported_msg('iterdir()')) + with self.scandir() as entries: + names = [entry.name for entry in entries] + return map(self.joinpath, names) def _glob_selector(self, parts, case_sensitive, recurse_symlinks): if case_sensitive is None: diff --git a/Lib/pathlib/_local.py b/Lib/pathlib/_local.py index a78997179820b1..ef072b83d96904 100644 --- a/Lib/pathlib/_local.py +++ b/Lib/pathlib/_local.py @@ -615,6 +615,14 @@ def _filter_trailing_slash(self, paths): path_str = path_str[:-1] yield path_str + def scandir(self): + """Yield os.DirEntry objects of the directory contents. + + The children are yielded in arbitrary order, and the + special entries '.' and '..' are not included. + """ + return os.scandir(self) + def iterdir(self): """Yield path objects of the directory contents. diff --git a/Lib/test/test_pathlib/test_pathlib_abc.py b/Lib/test/test_pathlib/test_pathlib_abc.py index 08355a71453807..11e34f5d378a58 100644 --- a/Lib/test/test_pathlib/test_pathlib_abc.py +++ b/Lib/test/test_pathlib/test_pathlib_abc.py @@ -1,4 +1,5 @@ import collections +import contextlib import io import os import errno @@ -1424,6 +1425,24 @@ def close(self): 'st_mode st_ino st_dev st_nlink st_uid st_gid st_size st_atime st_mtime st_ctime') +class DummyDirEntry: + """ + Minimal os.DirEntry-like object. Returned from DummyPath.scandir(). + """ + __slots__ = ('name', '_is_symlink', '_is_dir') + + def __init__(self, name, is_symlink, is_dir): + self.name = name + self._is_symlink = is_symlink + self._is_dir = is_dir + + def is_symlink(self): + return self._is_symlink + + def is_dir(self, *, follow_symlinks=True): + return self._is_dir and (follow_symlinks or not self._is_symlink) + + class DummyPath(PathBase): """ Simple implementation of PathBase that keeps files and directories in @@ -1491,14 +1510,25 @@ def open(self, mode='r', buffering=-1, encoding=None, stream = io.TextIOWrapper(stream, encoding=encoding, errors=errors, newline=newline) return stream - def iterdir(self): - path = str(self.resolve()) - if path in self._files: - raise NotADirectoryError(errno.ENOTDIR, "Not a directory", path) - elif path in self._directories: - return iter([self / name for name in self._directories[path]]) + @contextlib.contextmanager + def scandir(self): + path = self.resolve() + path_str = str(path) + if path_str in self._files: + raise NotADirectoryError(errno.ENOTDIR, "Not a directory", path_str) + elif path_str in self._directories: + yield iter([path.joinpath(name)._dir_entry for name in self._directories[path_str]]) else: - raise FileNotFoundError(errno.ENOENT, "File not found", path) + raise FileNotFoundError(errno.ENOENT, "File not found", path_str) + + @property + def _dir_entry(self): + path_str = str(self) + is_symlink = path_str in self._symlinks + is_directory = (path_str in self._directories + if not is_symlink + else self._symlinks[path_str][1]) + return DummyDirEntry(self.name, is_symlink, is_directory) def mkdir(self, mode=0o777, parents=False, exist_ok=False): path = str(self.parent.resolve() / self.name) @@ -1602,7 +1632,7 @@ def setUp(self): if self.can_symlink: p.joinpath('linkA').symlink_to('fileA') p.joinpath('brokenLink').symlink_to('non-existing') - p.joinpath('linkB').symlink_to('dirB') + p.joinpath('linkB').symlink_to('dirB', target_is_directory=True) p.joinpath('dirA', 'linkC').symlink_to(parser.join('..', 'dirB')) p.joinpath('dirB', 'linkD').symlink_to(parser.join('..', 'dirB')) p.joinpath('brokenLinkLoop').symlink_to('brokenLinkLoop') @@ -2187,6 +2217,23 @@ def test_iterdir_nodir(self): self.assertIn(cm.exception.errno, (errno.ENOTDIR, errno.ENOENT, errno.EINVAL)) + def test_scandir(self): + p = self.cls(self.base) + with p.scandir() as entries: + self.assertTrue(list(entries)) + with p.scandir() as entries: + for entry in entries: + child = p / entry.name + self.assertIsNotNone(entry) + self.assertEqual(entry.name, child.name) + self.assertEqual(entry.is_symlink(), + child.is_symlink()) + self.assertEqual(entry.is_dir(follow_symlinks=False), + child.is_dir(follow_symlinks=False)) + if entry.name != 'brokenLinkLoop': + self.assertEqual(entry.is_dir(), child.is_dir()) + + def test_glob_common(self): def _check(glob, expected): self.assertEqual(set(glob), { P(self.base, q) for q in expected }) @@ -3038,7 +3085,7 @@ class DummyPathWithSymlinks(DummyPath): def readlink(self): path = str(self.parent.resolve() / self.name) if path in self._symlinks: - return self.with_segments(self._symlinks[path]) + return self.with_segments(self._symlinks[path][0]) elif path in self._files or path in self._directories: raise OSError(errno.EINVAL, "Not a symlink", path) else: @@ -3050,7 +3097,7 @@ def symlink_to(self, target, target_is_directory=False): if path in self._symlinks: raise FileExistsError(errno.EEXIST, "File exists", path) self._directories[parent].add(self.name) - self._symlinks[path] = str(target) + self._symlinks[path] = str(target), target_is_directory class DummyPathWithSymlinksTest(DummyPathTest): diff --git a/Misc/NEWS.d/next/Library/2024-10-28-01-24-52.gh-issue-125413.Jat5kq.rst b/Misc/NEWS.d/next/Library/2024-10-28-01-24-52.gh-issue-125413.Jat5kq.rst new file mode 100644 index 00000000000000..ddf1f9725d9695 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-10-28-01-24-52.gh-issue-125413.Jat5kq.rst @@ -0,0 +1,3 @@ +Add :meth:`pathlib.Path.scandir` method to efficiently fetch directory +children and their file attributes. This is a trivial wrapper of +:func:`os.scandir`. From 295262c8ecb085b4fea552bc6229af3551bbaf7a Mon Sep 17 00:00:00 2001 From: sobolevn Date: Fri, 1 Nov 2024 10:17:05 +0300 Subject: [PATCH 008/219] gh-126259: Fix "unclosed database" warning in sqlite3 doctest (#126260) --- Doc/library/sqlite3.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index fc0383823a172b..096892b605b99c 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -2442,6 +2442,7 @@ Some useful URI tricks include: >>> con.execute("CREATE TABLE readonly(data)") Traceback (most recent call last): OperationalError: attempt to write a readonly database + >>> con.close() * Do not implicitly create a new database file if it does not already exist; will raise :exc:`~sqlite3.OperationalError` if unable to create a new file: From 43447cb63421fb4db5411c1e74bdd8a4cfcf9263 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Fri, 1 Nov 2024 09:17:54 +0100 Subject: [PATCH 009/219] gh-126206: make clinic now forcefully regenerates clinic code (#126244) --- Makefile.pre.in | 2 +- .../next/Build/2024-10-31-15-37-05.gh-issue-126206.oC6z2i.rst | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Build/2024-10-31-15-37-05.gh-issue-126206.oC6z2i.rst diff --git a/Makefile.pre.in b/Makefile.pre.in index aa7fa4e29d84c2..b0263f9f4c21da 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -904,7 +904,7 @@ coverage-report: regen-token regen-frozen # Run "Argument Clinic" over all source files .PHONY: clinic clinic: check-clean-src - $(PYTHON_FOR_REGEN) $(srcdir)/Tools/clinic/clinic.py --make --exclude Lib/test/clinic.test.c --srcdir $(srcdir) + $(PYTHON_FOR_REGEN) $(srcdir)/Tools/clinic/clinic.py --force --make --exclude Lib/test/clinic.test.c --srcdir $(srcdir) .PHONY: clinic-tests clinic-tests: check-clean-src $(srcdir)/Lib/test/clinic.test.c diff --git a/Misc/NEWS.d/next/Build/2024-10-31-15-37-05.gh-issue-126206.oC6z2i.rst b/Misc/NEWS.d/next/Build/2024-10-31-15-37-05.gh-issue-126206.oC6z2i.rst new file mode 100644 index 00000000000000..24b172e1747403 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2024-10-31-15-37-05.gh-issue-126206.oC6z2i.rst @@ -0,0 +1,2 @@ +``make clinic`` now runs Argument Clinic using the ``--force`` option, +thus forcefully regenerating generated code. From 6c67446a6e73ab0e9a26e4360412cbd2f5550e66 Mon Sep 17 00:00:00 2001 From: Jun Komoda <45822440+junkmd@users.noreply.github.com> Date: Fri, 1 Nov 2024 21:50:02 +0900 Subject: [PATCH 010/219] gh-125783: Add more tests to prevent regressions with the combination of ctypes and metaclasses. (GH-126126) --- .../test_ctypes/test_c_simple_type_meta.py | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/Lib/test/test_ctypes/test_c_simple_type_meta.py b/Lib/test/test_ctypes/test_c_simple_type_meta.py index fa5144a3ca01bb..eb77d6d7782478 100644 --- a/Lib/test/test_ctypes/test_c_simple_type_meta.py +++ b/Lib/test/test_ctypes/test_c_simple_type_meta.py @@ -85,3 +85,68 @@ class Sub(CtBase): self.assertIsInstance(POINTER(Sub), p_meta) self.assertTrue(issubclass(POINTER(Sub), Sub)) + + def test_creating_pointer_in_dunder_init_1(self): + class ct_meta(type): + def __init__(self, name, bases, namespace): + super().__init__(name, bases, namespace) + + # Avoid recursion. + # (See test_creating_pointer_in_dunder_new_1) + if bases == (c_void_p,): + return + if issubclass(self, PtrBase): + return + if bases == (object,): + ptr_bases = (self, PtrBase) + else: + ptr_bases = (self, POINTER(bases[0])) + p = p_meta(f"POINTER({self.__name__})", ptr_bases, {}) + ctypes._pointer_type_cache[self] = p + + class p_meta(PyCSimpleType, ct_meta): + pass + + class PtrBase(c_void_p, metaclass=p_meta): + pass + + class CtBase(object, metaclass=ct_meta): + pass + + class Sub(CtBase): + pass + + class Sub2(Sub): + pass + + self.assertIsInstance(POINTER(Sub2), p_meta) + self.assertTrue(issubclass(POINTER(Sub2), Sub2)) + self.assertTrue(issubclass(POINTER(Sub2), POINTER(Sub))) + self.assertTrue(issubclass(POINTER(Sub), POINTER(CtBase))) + + def test_creating_pointer_in_dunder_init_2(self): + class ct_meta(type): + def __init__(self, name, bases, namespace): + super().__init__(name, bases, namespace) + + # Avoid recursion. + # (See test_creating_pointer_in_dunder_new_2) + if isinstance(self, p_meta): + return + p = p_meta(f"POINTER({self.__name__})", (self, c_void_p), {}) + ctypes._pointer_type_cache[self] = p + + class p_meta(PyCSimpleType, ct_meta): + pass + + class Core(object): + pass + + class CtBase(Core, metaclass=ct_meta): + pass + + class Sub(CtBase): + pass + + self.assertIsInstance(POINTER(Sub), p_meta) + self.assertTrue(issubclass(POINTER(Sub), Sub)) From 32e07fd377f81cbeb8c108fc791a3e7d631319b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Fri, 1 Nov 2024 14:28:18 +0100 Subject: [PATCH 011/219] gh-111495: improve test coverage of codecs C API (GH-126030) For now, skip some crashers (tracked in gh-123378). --- Lib/test/test_capi/test_codecs.py | 138 +++++++++++++++++++++++++----- 1 file changed, 115 insertions(+), 23 deletions(-) diff --git a/Lib/test/test_capi/test_codecs.py b/Lib/test/test_capi/test_codecs.py index 85491a89947318..a557e35e68915d 100644 --- a/Lib/test/test_capi/test_codecs.py +++ b/Lib/test/test_capi/test_codecs.py @@ -747,6 +747,49 @@ def test_codec_stream_writer(self): class CAPICodecErrors(unittest.TestCase): + @classmethod + def _generate_exception_args(cls): + for objlen in range(5): + maxind = 2 * max(2, objlen) + for start in range(-maxind, maxind + 1): + for end in range(-maxind, maxind + 1): + yield objlen, start, end + + @classmethod + def generate_encode_errors(cls): + return tuple( + UnicodeEncodeError('utf-8', '0' * objlen, start, end, 'why') + for objlen, start, end in cls._generate_exception_args() + ) + + @classmethod + def generate_decode_errors(cls): + return tuple( + UnicodeDecodeError('utf-8', b'0' * objlen, start, end, 'why') + for objlen, start, end in cls._generate_exception_args() + ) + + @classmethod + def generate_translate_errors(cls): + return tuple( + UnicodeTranslateError('0' * objlen, start, end, 'why') + for objlen, start, end in cls._generate_exception_args() + ) + + @classmethod + def setUpClass(cls): + cls.unicode_encode_errors = cls.generate_encode_errors() + cls.unicode_decode_errors = cls.generate_decode_errors() + cls.unicode_translate_errors = cls.generate_translate_errors() + cls.all_unicode_errors = ( + cls.unicode_encode_errors + + cls.unicode_decode_errors + + cls.unicode_translate_errors + ) + cls.bad_unicode_errors = ( + ValueError(), + ) + def test_codec_register_error(self): # for cleaning up between tests from _codecs import _unregister_error as _codecs_unregister_error @@ -780,33 +823,82 @@ def test_codec_lookup_error(self): self.assertIs(codec_lookup_error('ignore'), codecs.ignore_errors) self.assertIs(codec_lookup_error('replace'), codecs.replace_errors) self.assertIs(codec_lookup_error('xmlcharrefreplace'), codecs.xmlcharrefreplace_errors) + self.assertIs(codec_lookup_error('backslashreplace'), codecs.backslashreplace_errors) self.assertIs(codec_lookup_error('namereplace'), codecs.namereplace_errors) self.assertRaises(LookupError, codec_lookup_error, 'unknown') - def test_codec_error_handlers(self): - exceptions = [ - # A UnicodeError with an empty message currently crashes: - # See: https://github.com/python/cpython/issues/123378 - # UnicodeEncodeError('bad', '', 0, 1, 'reason'), - UnicodeEncodeError('bad', 'x', 0, 1, 'reason'), - UnicodeEncodeError('bad', 'xyz123', 0, 1, 'reason'), - UnicodeEncodeError('bad', 'xyz123', 1, 4, 'reason'), - ] - - strict_handler = _testcapi.codec_strict_errors + def test_codec_strict_errors_handler(self): + handler = _testcapi.codec_strict_errors + for exc in self.all_unicode_errors + self.bad_unicode_errors: + with self.subTest(handler=handler, exc=exc): + self.assertRaises(type(exc), handler, exc) + + def test_codec_ignore_errors_handler(self): + handler = _testcapi.codec_ignore_errors + self.do_test_codec_errors_handler(handler, self.all_unicode_errors) + + def test_codec_replace_errors_handler(self): + handler = _testcapi.codec_replace_errors + self.do_test_codec_errors_handler(handler, self.all_unicode_errors) + + def test_codec_xmlcharrefreplace_errors_handler(self): + handler = _testcapi.codec_xmlcharrefreplace_errors + self.do_test_codec_errors_handler(handler, self.unicode_encode_errors) + + def test_codec_backslashreplace_errors_handler(self): + handler = _testcapi.codec_backslashreplace_errors + self.do_test_codec_errors_handler(handler, self.all_unicode_errors) + + def test_codec_namereplace_errors_handler(self): + handler = _testlimitedcapi.codec_namereplace_errors + self.do_test_codec_errors_handler(handler, self.unicode_encode_errors) + + def do_test_codec_errors_handler(self, handler, exceptions): + at_least_one = False for exc in exceptions: - with self.subTest(handler=strict_handler, exc=exc): - self.assertRaises(UnicodeEncodeError, strict_handler, exc) - - for handler in [ - _testcapi.codec_ignore_errors, - _testcapi.codec_replace_errors, - _testcapi.codec_xmlcharrefreplace_errors, - _testlimitedcapi.codec_namereplace_errors, - ]: - for exc in exceptions: - with self.subTest(handler=handler, exc=exc): - self.assertIsInstance(handler(exc), tuple) + # See https://github.com/python/cpython/issues/123378 and related + # discussion and issues for details. + if self._exception_may_crash(exc): + continue + + at_least_one = True + with self.subTest(handler=handler, exc=exc): + # test that the handler does not crash + self.assertIsInstance(handler(exc), tuple) + + if exceptions: + self.assertTrue(at_least_one, "all exceptions are crashing") + + for bad_exc in ( + self.bad_unicode_errors + + tuple(e for e in self.all_unicode_errors if e not in exceptions) + ): + with self.subTest('bad type', handler=handler, exc=bad_exc): + self.assertRaises(TypeError, handler, bad_exc) + + @classmethod + def _exception_may_crash(cls, exc): + """Indicate whether a Unicode exception might currently crash + the interpreter when used by a built-in codecs error handler. + + Until gh-123378 is fixed, we skip the tests for these exceptions. + + This should only be used by "do_test_codec_errors_handler". + """ + message, start, end = exc.object, exc.start, exc.end + match exc: + case UnicodeEncodeError(): + return end < start or (end - start) >= len(message) + case UnicodeDecodeError(): + # The case "end - start >= len(message)" does not crash. + return end < start + case UnicodeTranslateError(): + # Test "end <= start" because PyCodec_ReplaceErrors checks + # the Unicode kind of a 0-length string which by convention + # is PyUnicode_1BYTE_KIND and not PyUnicode_2BYTE_KIND as + # the handler currently expects. + return end <= start or (end - start) >= len(message) + return False if __name__ == "__main__": From 821759d63101334fa8a1863135429c341ca0b234 Mon Sep 17 00:00:00 2001 From: mpage Date: Fri, 1 Nov 2024 08:53:03 -0700 Subject: [PATCH 012/219] gh-126211: Exclude preprocessor directives from statements containing escaping calls (#126213) The cases generator inserts code to save and restore the stack pointer around statements that contain escaping calls. To find the beginning of such statements, we would walk backwards from the escaping call until we encountered a token that was treated as a statement terminator. This set of terminators should include preprocessor directives. --- Lib/test/test_generated_cases.py | 33 +++++++++++++++++++++++++++++++ Tools/cases_generator/analyzer.py | 2 +- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_generated_cases.py b/Lib/test/test_generated_cases.py index 173e405b785ddc..ff9a52b7adac8a 100644 --- a/Lib/test/test_generated_cases.py +++ b/Lib/test/test_generated_cases.py @@ -1429,6 +1429,39 @@ def test_instruction_size_macro(self): with self.assertRaisesRegex(SyntaxError, "All instructions containing a uop"): self.run_cases_test(input, output) + def test_escaping_call_next_to_cmacro(self): + input = """ + inst(OP, (--)) { + #ifdef Py_GIL_DISABLED + escaping_call(); + #else + another_escaping_call(); + #endif + yet_another_escaping_call(); + } + """ + output = """ + TARGET(OP) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(OP); + #ifdef Py_GIL_DISABLED + _PyFrame_SetStackPointer(frame, stack_pointer); + escaping_call(); + stack_pointer = _PyFrame_GetStackPointer(frame); + #else + _PyFrame_SetStackPointer(frame, stack_pointer); + another_escaping_call(); + stack_pointer = _PyFrame_GetStackPointer(frame); + #endif + _PyFrame_SetStackPointer(frame, stack_pointer); + yet_another_escaping_call(); + stack_pointer = _PyFrame_GetStackPointer(frame); + DISPATCH(); + } + """ + self.run_cases_test(input, output) + class TestGeneratedAbstractCases(unittest.TestCase): def setUp(self) -> None: diff --git a/Tools/cases_generator/analyzer.py b/Tools/cases_generator/analyzer.py index 66ead741b87a2b..a725ec10d4e52a 100644 --- a/Tools/cases_generator/analyzer.py +++ b/Tools/cases_generator/analyzer.py @@ -637,7 +637,7 @@ def find_stmt_start(node: parser.InstDef, idx: int) -> lexer.Token: assert idx < len(node.block.tokens) while True: tkn = node.block.tokens[idx-1] - if tkn.kind in {"SEMI", "LBRACE", "RBRACE"}: + if tkn.kind in {"SEMI", "LBRACE", "RBRACE", "CMACRO"}: break idx -= 1 assert idx > 0 From 38a604fd90e765d5ac42373755397ab1e6cc0a39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Fri, 1 Nov 2024 17:18:44 +0100 Subject: [PATCH 013/219] gh-99108: Cleanup references to inexisting `Modules/_blake2`. (GH-126270) * Remove references to `Modules/_blake2`. * Remove `Modules/_blake2` entry from CODEOWNERS The folder does not exist anymore. * Remove `Modules/_blake2` entry from `Tools/c-analyzer/TODO` --- .github/CODEOWNERS | 1 - PCbuild/pythoncore.vcxproj.filters | 9 --------- Tools/c-analyzer/TODO | 2 -- Tools/c-analyzer/cpython/ignored.tsv | 2 -- configure | 1 - configure.ac | 1 - 6 files changed, 16 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 9024639e25fcdf..9162f9c7bb1576 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -88,7 +88,6 @@ Objects/exceptions.c @iritkatriel **/sha* @gpshead @tiran Modules/md5* @gpshead @tiran **/*blake* @gpshead @tiran -Modules/_blake2/** @gpshead @tiran Modules/_hacl/** @gpshead # logging diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index 6b294683320a73..740790cc5e1119 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -917,15 +917,6 @@ Modules - - Modules - - - Modules - - - Modules - Modules diff --git a/Tools/c-analyzer/TODO b/Tools/c-analyzer/TODO index 3d599538510bd9..e81ceb29c64bf0 100644 --- a/Tools/c-analyzer/TODO +++ b/Tools/c-analyzer/TODO @@ -562,8 +562,6 @@ Objects/unicodeobject.c:static_strings static _Py_Iden # PyTypeObject (311) Modules/_abc.c:_abc_data_type static PyTypeObject _abc_data_type -Modules/_blake2/blake2b_impl.c:PyBlake2_BLAKE2bType PyTypeObject PyBlake2_BLAKE2bType -Modules/_blake2/blake2s_impl.c:PyBlake2_BLAKE2sType PyTypeObject PyBlake2_BLAKE2sType Modules/_collectionsmodule.c:defdict_type static PyTypeObject defdict_type Modules/_collectionsmodule.c:deque_type static PyTypeObject deque_type Modules/_collectionsmodule.c:dequeiter_type static PyTypeObject dequeiter_type diff --git a/Tools/c-analyzer/cpython/ignored.tsv b/Tools/c-analyzer/cpython/ignored.tsv index 2605825d3d0078..686f3935d91bda 100644 --- a/Tools/c-analyzer/cpython/ignored.tsv +++ b/Tools/c-analyzer/cpython/ignored.tsv @@ -629,8 +629,6 @@ Modules/_xxtestfuzz/fuzzer.c LLVMFuzzerTestOneInput ELEMENTTREE_PARSEWHOLE_INITI Include/internal/pycore_importdl.h - _PyImport_DynLoadFiletab - Include/py_curses.h - PyCurses_API - Include/pydecimal.h - _decimal_api - -Modules/_blake2/blake2module.c - blake2b_type_spec - -Modules/_blake2/blake2module.c - blake2s_type_spec - Modules/_io/fileio.c - _Py_open_cloexec_works - Modules/_io/_iomodule.h - PyIOBase_Type - Modules/_io/_iomodule.h - PyRawIOBase_Type - diff --git a/configure b/configure index 3989de82a99ecd..1097747e055179 100755 --- a/configure +++ b/configure @@ -28091,7 +28091,6 @@ done SRCDIRS="\ Modules \ - Modules/_blake2 \ Modules/_ctypes \ Modules/_decimal \ Modules/_decimal/libmpdec \ diff --git a/configure.ac b/configure.ac index 6a98cc5e2a0835..6d514705e91ce5 100644 --- a/configure.ac +++ b/configure.ac @@ -7074,7 +7074,6 @@ done AC_SUBST([SRCDIRS]) SRCDIRS="\ Modules \ - Modules/_blake2 \ Modules/_ctypes \ Modules/_decimal \ Modules/_decimal/libmpdec \ From 464a7a91d0c75241a2f1913e78dbfbc29d4193b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Fri, 1 Nov 2024 17:35:22 +0100 Subject: [PATCH 014/219] gh-97850: remove ``find_loader`` and ``get_loader`` from ``pkgutil`` (#119656) Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Co-authored-by: Brett Cannon --- Doc/deprecations/pending-removal-in-3.14.rst | 2 +- Doc/library/pkgutil.rst | 40 ----------- Doc/whatsnew/3.12.rst | 2 +- Doc/whatsnew/3.14.rst | 7 ++ Lib/pkgutil.py | 55 +-------------- Lib/test/test_doctest/test_doctest.py | 19 ------ Lib/test/test_pkgutil.py | 67 ------------------- Misc/NEWS.d/3.12.0b1.rst | 2 +- ...4-05-28-14-35-23.gh-issue-97850.dCtjel.rst | 1 + 9 files changed, 12 insertions(+), 183 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-05-28-14-35-23.gh-issue-97850.dCtjel.rst diff --git a/Doc/deprecations/pending-removal-in-3.14.rst b/Doc/deprecations/pending-removal-in-3.14.rst index b8791b8d6c387e..23c9bde5c01ba7 100644 --- a/Doc/deprecations/pending-removal-in-3.14.rst +++ b/Doc/deprecations/pending-removal-in-3.14.rst @@ -85,7 +85,7 @@ Pending removal in Python 3.14 :meth:`~pathlib.PurePath.relative_to`: passing additional arguments is deprecated. -* :mod:`pkgutil`: :func:`~pkgutil.find_loader` and :func:`~pkgutil.get_loader` +* :mod:`pkgutil`: :func:`!pkgutil.find_loader` and :func:!pkgutil.get_loader` now raise :exc:`DeprecationWarning`; use :func:`importlib.util.find_spec` instead. (Contributed by Nikita Sobolev in :gh:`97850`.) diff --git a/Doc/library/pkgutil.rst b/Doc/library/pkgutil.rst index 4a39d53a5f1440..20b8f6bcf19117 100644 --- a/Doc/library/pkgutil.rst +++ b/Doc/library/pkgutil.rst @@ -49,25 +49,6 @@ support. this function to raise an exception (in line with :func:`os.path.isdir` behavior). -.. function:: find_loader(fullname) - - Retrieve a module :term:`loader` for the given *fullname*. - - This is a backwards compatibility wrapper around - :func:`importlib.util.find_spec` that converts most failures to - :exc:`ImportError` and only returns the loader rather than the full - :class:`importlib.machinery.ModuleSpec`. - - .. versionchanged:: 3.3 - Updated to be based directly on :mod:`importlib` rather than relying - on the package internal :pep:`302` import emulation. - - .. versionchanged:: 3.4 - Updated to be based on :pep:`451` - - .. deprecated-removed:: 3.12 3.14 - Use :func:`importlib.util.find_spec` instead. - .. function:: get_importer(path_item) @@ -84,27 +65,6 @@ support. on the package internal :pep:`302` import emulation. -.. function:: get_loader(module_or_name) - - Get a :term:`loader` object for *module_or_name*. - - If the module or package is accessible via the normal import mechanism, a - wrapper around the relevant part of that machinery is returned. Returns - ``None`` if the module cannot be found or imported. If the named module is - not already imported, its containing package (if any) is imported, in order - to establish the package ``__path__``. - - .. versionchanged:: 3.3 - Updated to be based directly on :mod:`importlib` rather than relying - on the package internal :pep:`302` import emulation. - - .. versionchanged:: 3.4 - Updated to be based on :pep:`451` - - .. deprecated-removed:: 3.12 3.14 - Use :func:`importlib.util.find_spec` instead. - - .. function:: iter_importers(fullname='') Yield :term:`finder` objects for the given module name. diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 1354355894e375..3640095acbaa2b 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -1229,7 +1229,7 @@ Deprecated your code *requires* ``'fork'``. See :ref:`contexts and start methods `. -* :mod:`pkgutil`: :func:`pkgutil.find_loader` and :func:`pkgutil.get_loader` +* :mod:`pkgutil`: :func:`!pkgutil.find_loader` and :func:`!pkgutil.get_loader` are deprecated and will be removed in Python 3.14; use :func:`importlib.util.find_spec` instead. (Contributed by Nikita Sobolev in :gh:`97850`.) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 48314f9c98c036..db32be89cf88ff 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -621,6 +621,13 @@ pathlib :meth:`~pathlib.PurePath.is_relative_to`. In previous versions, any such arguments are joined onto *other*. +pkgutil +------- + +* Remove deprecated :func:`!pkgutil.get_loader` and :func:`!pkgutil.find_loader`. + These had previously raised a :exc:`DeprecationWarning` since Python 3.12. + (Contributed by Bénédikt Tran in :gh:`97850`.) + pty --- diff --git a/Lib/pkgutil.py b/Lib/pkgutil.py index dccbec52aa731e..b84d72f2395d45 100644 --- a/Lib/pkgutil.py +++ b/Lib/pkgutil.py @@ -12,7 +12,7 @@ import warnings __all__ = [ - 'get_importer', 'iter_importers', 'get_loader', 'find_loader', + 'get_importer', 'iter_importers', 'walk_packages', 'iter_modules', 'get_data', 'read_code', 'extend_path', 'ModuleInfo', @@ -263,59 +263,6 @@ def iter_importers(fullname=""): yield get_importer(item) -def get_loader(module_or_name): - """Get a "loader" object for module_or_name - - Returns None if the module cannot be found or imported. - If the named module is not already imported, its containing package - (if any) is imported, in order to establish the package __path__. - """ - warnings._deprecated("pkgutil.get_loader", - f"{warnings._DEPRECATED_MSG}; " - "use importlib.util.find_spec() instead", - remove=(3, 14)) - if module_or_name in sys.modules: - module_or_name = sys.modules[module_or_name] - if module_or_name is None: - return None - if isinstance(module_or_name, ModuleType): - module = module_or_name - loader = getattr(module, '__loader__', None) - if loader is not None: - return loader - if getattr(module, '__spec__', None) is None: - return None - fullname = module.__name__ - else: - fullname = module_or_name - return find_loader(fullname) - - -def find_loader(fullname): - """Find a "loader" object for fullname - - This is a backwards compatibility wrapper around - importlib.util.find_spec that converts most failures to ImportError - and only returns the loader rather than the full spec - """ - warnings._deprecated("pkgutil.find_loader", - f"{warnings._DEPRECATED_MSG}; " - "use importlib.util.find_spec() instead", - remove=(3, 14)) - if fullname.startswith('.'): - msg = "Relative module name {!r} not supported".format(fullname) - raise ImportError(msg) - try: - spec = importlib.util.find_spec(fullname) - except (ImportError, AttributeError, TypeError, ValueError) as ex: - # This hack fixes an impedance mismatch between pkgutil and - # importlib, where the latter raises other errors for cases where - # pkgutil previously raised ImportError - msg = "Error while finding loader for {!r} ({}: {})" - raise ImportError(msg.format(fullname, type(ex), ex)) from ex - return spec.loader if spec is not None else None - - def extend_path(path, name): """Extend a package's path. diff --git a/Lib/test/test_doctest/test_doctest.py b/Lib/test/test_doctest/test_doctest.py index 171412cb7cb08c..b1e165fe16b54f 100644 --- a/Lib/test/test_doctest/test_doctest.py +++ b/Lib/test/test_doctest/test_doctest.py @@ -2410,25 +2410,6 @@ def test_DocFileSuite(): >>> suite.run(unittest.TestResult()) - Support for using a package's __loader__.get_data() is also - provided. - - >>> import unittest, pkgutil, test - >>> added_loader = False - >>> if not hasattr(test, '__loader__'): - ... test.__loader__ = pkgutil.get_loader(test) - ... added_loader = True - >>> try: - ... suite = doctest.DocFileSuite('test_doctest.txt', - ... 'test_doctest2.txt', - ... 'test_doctest4.txt', - ... package='test.test_doctest') - ... suite.run(unittest.TestResult()) - ... finally: - ... if added_loader: - ... del test.__loader__ - - '/' should be used as a path separator. It will be converted to a native separator at run time: diff --git a/Lib/test/test_pkgutil.py b/Lib/test/test_pkgutil.py index ca6927554b053c..736b83711def03 100644 --- a/Lib/test/test_pkgutil.py +++ b/Lib/test/test_pkgutil.py @@ -607,73 +607,6 @@ class ImportlibMigrationTests(unittest.TestCase): # PEP 302 emulation in this module is in the process of being # deprecated in favour of importlib proper - @unittest.skipIf(__name__ == '__main__', 'not compatible with __main__') - @ignore_warnings(category=DeprecationWarning) - def test_get_loader_handles_missing_loader_attribute(self): - global __loader__ - this_loader = __loader__ - del __loader__ - try: - self.assertIsNotNone(pkgutil.get_loader(__name__)) - finally: - __loader__ = this_loader - - @ignore_warnings(category=DeprecationWarning) - def test_get_loader_handles_missing_spec_attribute(self): - name = 'spam' - mod = type(sys)(name) - del mod.__spec__ - with CleanImport(name): - try: - sys.modules[name] = mod - loader = pkgutil.get_loader(name) - finally: - sys.modules.pop(name, None) - self.assertIsNone(loader) - - @ignore_warnings(category=DeprecationWarning) - def test_get_loader_handles_spec_attribute_none(self): - name = 'spam' - mod = type(sys)(name) - mod.__spec__ = None - with CleanImport(name): - try: - sys.modules[name] = mod - loader = pkgutil.get_loader(name) - finally: - sys.modules.pop(name, None) - self.assertIsNone(loader) - - @ignore_warnings(category=DeprecationWarning) - def test_get_loader_None_in_sys_modules(self): - name = 'totally bogus' - sys.modules[name] = None - try: - loader = pkgutil.get_loader(name) - finally: - del sys.modules[name] - self.assertIsNone(loader) - - def test_get_loader_is_deprecated(self): - with check_warnings( - (r".*\bpkgutil.get_loader\b.*", DeprecationWarning), - ): - res = pkgutil.get_loader("sys") - self.assertIsNotNone(res) - - def test_find_loader_is_deprecated(self): - with check_warnings( - (r".*\bpkgutil.find_loader\b.*", DeprecationWarning), - ): - res = pkgutil.find_loader("sys") - self.assertIsNotNone(res) - - @ignore_warnings(category=DeprecationWarning) - def test_find_loader_missing_module(self): - name = 'totally bogus' - loader = pkgutil.find_loader(name) - self.assertIsNone(loader) - def test_get_importer_avoids_emulation(self): # We use an illegal path so *none* of the path hooks should fire with check_warnings() as w: diff --git a/Misc/NEWS.d/3.12.0b1.rst b/Misc/NEWS.d/3.12.0b1.rst index 7126e08a20c7fd..3a3870ac9fe621 100644 --- a/Misc/NEWS.d/3.12.0b1.rst +++ b/Misc/NEWS.d/3.12.0b1.rst @@ -1750,7 +1750,7 @@ Remove the long-deprecated ``imp`` module. .. nonce: N46coo .. section: Library -Deprecate :func:`pkgutil.find_loader` and :func:`pkgutil.get_loader` in +Deprecate :func:`!pkgutil.find_loader` and :func:`!pkgutil.get_loader` in favor of :func:`importlib.util.find_spec`. .. diff --git a/Misc/NEWS.d/next/Library/2024-05-28-14-35-23.gh-issue-97850.dCtjel.rst b/Misc/NEWS.d/next/Library/2024-05-28-14-35-23.gh-issue-97850.dCtjel.rst new file mode 100644 index 00000000000000..bb94f7d8ad124d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-05-28-14-35-23.gh-issue-97850.dCtjel.rst @@ -0,0 +1 @@ +Remove deprecated :func:`!pkgutil.get_loader` and :func:`!pkgutil.find_loader`. From 68a51e0178e86be8b697683fd108aa795f235507 Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Fri, 1 Nov 2024 17:48:58 +0000 Subject: [PATCH 015/219] GH-125413: pathlib ABCs: use `scandir()` to speed up `glob()` (#126261) Use the new `PathBase.scandir()` method in `PathBase.glob()`, which greatly reduces the number of `PathBase.stat()` calls needed when globbing. There are no user-facing changes, because the pathlib ABCs are still private and `Path.glob()` doesn't use the implementation in its superclass. --- Lib/glob.py | 13 ++++--------- Lib/pathlib/_abc.py | 14 +------------- Lib/test/test_pathlib/test_pathlib_abc.py | 8 +++++--- 3 files changed, 10 insertions(+), 25 deletions(-) diff --git a/Lib/glob.py b/Lib/glob.py index 574e5ad51b601d..ce9b3698888dd9 100644 --- a/Lib/glob.py +++ b/Lib/glob.py @@ -364,12 +364,6 @@ def concat_path(path, text): """ raise NotImplementedError - @staticmethod - def parse_entry(entry): - """Returns the path of an entry yielded from scandir(). - """ - raise NotImplementedError - # High-level methods def compile(self, pat): @@ -438,6 +432,7 @@ def select_wildcard(path, exists=False): except OSError: pass else: + prefix = self.add_slash(path) for entry in entries: if match is None or match(entry.name): if dir_only: @@ -446,7 +441,7 @@ def select_wildcard(path, exists=False): continue except OSError: continue - entry_path = self.parse_entry(entry) + entry_path = self.concat_path(prefix, entry.name) if dir_only: yield from select_next(entry_path, exists=True) else: @@ -495,6 +490,7 @@ def select_recursive_step(stack, match_pos): except OSError: pass else: + prefix = self.add_slash(path) for entry in entries: is_dir = False try: @@ -504,7 +500,7 @@ def select_recursive_step(stack, match_pos): pass if is_dir or not dir_only: - entry_path = self.parse_entry(entry) + entry_path = self.concat_path(prefix, entry.name) if match is None or match(str(entry_path), match_pos): if dir_only: yield from select_next(entry_path, exists=True) @@ -533,7 +529,6 @@ class _StringGlobber(_GlobberBase): """ lexists = staticmethod(os.path.lexists) scandir = staticmethod(os.scandir) - parse_entry = operator.attrgetter('path') concat_path = operator.add if os.name == 'nt': diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py index dfff8b460d1bf1..cc7c1991d0e528 100644 --- a/Lib/pathlib/_abc.py +++ b/Lib/pathlib/_abc.py @@ -94,25 +94,13 @@ class PathGlobber(_GlobberBase): lexists = operator.methodcaller('exists', follow_symlinks=False) add_slash = operator.methodcaller('joinpath', '') - - @staticmethod - def scandir(path): - """Emulates os.scandir(), which returns an object that can be used as - a context manager. This method is called by walk() and glob(). - """ - import contextlib - return contextlib.nullcontext(path.iterdir()) + scandir = operator.methodcaller('scandir') @staticmethod def concat_path(path, text): """Appends text to the given path.""" return path.with_segments(path._raw_path + text) - @staticmethod - def parse_entry(entry): - """Returns the path of an entry yielded from scandir().""" - return entry - class PurePathBase: """Base class for pure path objects. diff --git a/Lib/test/test_pathlib/test_pathlib_abc.py b/Lib/test/test_pathlib/test_pathlib_abc.py index 11e34f5d378a58..4596d0b0e26763 100644 --- a/Lib/test/test_pathlib/test_pathlib_abc.py +++ b/Lib/test/test_pathlib/test_pathlib_abc.py @@ -1633,8 +1633,10 @@ def setUp(self): p.joinpath('linkA').symlink_to('fileA') p.joinpath('brokenLink').symlink_to('non-existing') p.joinpath('linkB').symlink_to('dirB', target_is_directory=True) - p.joinpath('dirA', 'linkC').symlink_to(parser.join('..', 'dirB')) - p.joinpath('dirB', 'linkD').symlink_to(parser.join('..', 'dirB')) + p.joinpath('dirA', 'linkC').symlink_to( + parser.join('..', 'dirB'), target_is_directory=True) + p.joinpath('dirB', 'linkD').symlink_to( + parser.join('..', 'dirB'), target_is_directory=True) p.joinpath('brokenLinkLoop').symlink_to('brokenLinkLoop') def tearDown(self): @@ -2479,7 +2481,7 @@ def test_glob_permissions(self): if i % 2: link.symlink_to(P(self.base, "dirE", "nonexistent")) else: - link.symlink_to(P(self.base, "dirC")) + link.symlink_to(P(self.base, "dirC"), target_is_directory=True) self.assertEqual(len(set(base.glob("*"))), 100) self.assertEqual(len(set(base.glob("*/"))), 50) From 37651cfbce514a8daed75a8c63d6889081a63a23 Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Fri, 1 Nov 2024 18:52:00 +0000 Subject: [PATCH 016/219] GH-125413: pathlib ABCs: use `scandir()` to speed up `walk()` (#126262) Use the new `PathBase.scandir()` method in `PathBase.walk()`, which greatly reduces the number of `PathBase.stat()` calls needed when walking. There are no user-facing changes, because the pathlib ABCs are still private and `Path.walk()` doesn't use the implementation in its superclass. --- Lib/pathlib/_abc.py | 22 ++++++++++++---------- Lib/test/test_pathlib/test_pathlib_abc.py | 4 ++-- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py index cc7c1991d0e528..f5eed6f025c250 100644 --- a/Lib/pathlib/_abc.py +++ b/Lib/pathlib/_abc.py @@ -693,16 +693,18 @@ def walk(self, top_down=True, on_error=None, follow_symlinks=False): if not top_down: paths.append((path, dirnames, filenames)) try: - for child in path.iterdir(): - try: - if child.is_dir(follow_symlinks=follow_symlinks): - if not top_down: - paths.append(child) - dirnames.append(child.name) - else: - filenames.append(child.name) - except OSError: - filenames.append(child.name) + with path.scandir() as entries: + for entry in entries: + name = entry.name + try: + if entry.is_dir(follow_symlinks=follow_symlinks): + if not top_down: + paths.append(path.joinpath(name)) + dirnames.append(name) + else: + filenames.append(name) + except OSError: + filenames.append(name) except OSError as error: if on_error is not None: on_error(error) diff --git a/Lib/test/test_pathlib/test_pathlib_abc.py b/Lib/test/test_pathlib/test_pathlib_abc.py index 4596d0b0e26763..4ab804850e9c3e 100644 --- a/Lib/test/test_pathlib/test_pathlib_abc.py +++ b/Lib/test/test_pathlib/test_pathlib_abc.py @@ -1951,7 +1951,7 @@ def ordered_walk(path): if self.can_symlink: # Add some symlinks source.joinpath('linkC').symlink_to('fileC') - source.joinpath('linkD').symlink_to('dirD') + source.joinpath('linkD').symlink_to('dirD', target_is_directory=True) # Perform the copy target = base / 'copyC' @@ -2969,7 +2969,7 @@ def setUpWalk(self): f.write(f"I'm {path} and proud of it. Blame test_pathlib.\n") if self.can_symlink: - self.link_path.symlink_to(t2_path) + self.link_path.symlink_to(t2_path, target_is_directory=True) broken_link_path.symlink_to('broken') broken_link2_path.symlink_to(self.cls('tmp3', 'broken')) self.sub2_tree = (self.sub2_path, [], ["broken_link", "broken_link2", "link", "tmp3"]) From ff257c7843d8ed0dffb6624f2f14996a46e74801 Mon Sep 17 00:00:00 2001 From: "Filip \"Ret2Me\" Poplewski" <37419029+Ret2Me@users.noreply.github.com> Date: Fri, 1 Nov 2024 20:28:50 +0100 Subject: [PATCH 017/219] docs: add a more precise example in enum doc (GH-121015) * docs: add a more precise example Previous example used manual integer value assignment in class based declaration but in functional syntax has been used auto value assignment what could be confusing for the new users. Additionally documentation doesn't show how to declare new enum via functional syntax with usage of the manual value assignment. * docs: remove whitespace characters * refactor: change example --------- Co-authored-by: Ethan Furman --- Doc/library/enum.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst index 242b2436439903..16a9b0326e9f3d 100644 --- a/Doc/library/enum.rst +++ b/Doc/library/enum.rst @@ -44,7 +44,7 @@ using function-call syntax:: ... BLUE = 3 >>> # functional syntax - >>> Color = Enum('Color', ['RED', 'GREEN', 'BLUE']) + >>> Color = Enum('Color', [('RED', 1), ('GREEN', 2), ('BLUE', 3)]) Even though we can use :keyword:`class` syntax to create Enums, Enums are not normal Python classes. See From 74cf5967f345f944f1c308e5e30fee21b438eb6c Mon Sep 17 00:00:00 2001 From: Joseph Martinot-Lagarde Date: Fri, 1 Nov 2024 20:35:05 +0100 Subject: [PATCH 018/219] Doc: Add a single table as summary to math documentation (GH-125810) * Summary for math module with separate tables * Forgot remainder description * Single table * data instead of func * Add arguments in the table * Fix inconsistencies in pow documentation * Remove full stops from the table Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> * Fix math.pow link * Fix spacing --------- Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- Doc/library/math.rst | 92 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 89 insertions(+), 3 deletions(-) diff --git a/Doc/library/math.rst b/Doc/library/math.rst index dd2ba419b5bd12..6be61c99274eb7 100644 --- a/Doc/library/math.rst +++ b/Doc/library/math.rst @@ -26,6 +26,92 @@ The following functions are provided by this module. Except when explicitly noted otherwise, all return values are floats. +==================================================== ============================================ +**Number-theoretic and representation functions** +-------------------------------------------------------------------------------------------------- +:func:`ceil(x) ` Ceiling of *x*, the smallest integer greater than or equal to *x* +:func:`comb(n, k) ` Number of ways to choose *k* items from *n* items without repetition and without order +:func:`copysign(x, y) ` Magnitude (absolute value) of *x* with the sign of *y* +:func:`fabs(x) ` Absolute value of *x* +:func:`factorial(n) ` *n* factorial +:func:`floor (x) ` Floor of *x*, the largest integer less than or equal to *x* +:func:`fma(x, y, z) ` Fused multiply-add operation: ``(x * y) + z`` +:func:`fmod(x, y) ` Remainder of division ``x / y`` +:func:`frexp(x) ` Mantissa and exponent of *x* +:func:`fsum(iterable) ` Sum of values in the input *iterable* +:func:`gcd(*integers) ` Greatest common divisor of the integer arguments +:func:`isclose(a, b, rel_tol, abs_tol) ` Check if the values *a* and *b* are close to each other +:func:`isfinite(x) ` Check if *x* is neither an infinity nor a NaN +:func:`isinf(x) ` Check if *x* is a positive or negative infinity +:func:`isnan(x) ` Check if *x* is a NaN (not a number) +:func:`isqrt(n) ` Integer square root of a nonnegative integer *n* +:func:`lcm(*integers) ` Least common multiple of the integer arguments +:func:`ldexp(x, i) ` ``x * (2**i)``, inverse of function :func:`frexp` +:func:`modf(x) ` Fractional and integer parts of *x* +:func:`nextafter(x, y, steps) ` Floating-point value *steps* steps after *x* towards *y* +:func:`perm(n, k) ` Number of ways to choose *k* items from *n* items without repetition and with order +:func:`prod(iterable, start) ` Product of elements in the input *iterable* with a *start* value +:func:`remainder(x, y) ` Remainder of *x* with respect to *y* +:func:`sumprod(p, q) ` Sum of products from two iterables *p* and *q* +:func:`trunc(x) ` Integer part of *x* +:func:`ulp(x) ` Value of the least significant bit of *x* + +**Power and logarithmic functions** +-------------------------------------------------------------------------------------------------- +:func:`cbrt(x) ` Cube root of *x* +:func:`exp(x) ` *e* raised to the power *x* +:func:`exp2(x) ` *2* raised to the power *x* +:func:`expm1(x) ` *e* raised to the power *x*, minus 1 +:func:`log(x, base) ` Logarithm of *x* to the given base (*e* by default) +:func:`log1p(x) ` Natural logarithm of *1+x* (base *e*) +:func:`log2(x) ` Base-2 logarithm of *x* +:func:`log10(x) ` Base-10 logarithm of *x* +:func:`pow(x, y) ` *x* raised to the power *y* +:func:`sqrt(x) ` Square root of *x* + +**Trigonometric functions** +-------------------------------------------------------------------------------------------------- +:func:`acos(x) ` Arc cosine of *x* +:func:`asin(x) ` Arc sine of *x* +:func:`atan(x) ` Arc tangent of *x* +:func:`atan2(y, x) ` ``atan(y / x)`` +:func:`cos(x) ` Cosine of *x* +:func:`dist(p, q) ` Euclidean distance between two points *p* and *q* given as an iterable of coordinates +:func:`hypot(*coordinates) ` Euclidean norm of an iterable of coordinates +:func:`sin(x) ` Sine of *x* +:func:`tan(x) ` Tangent of *x* + +**Angular conversion** +-------------------------------------------------------------------------------------------------- +:func:`degrees(x) ` Convert angle *x* from radians to degrees +:func:`radians(x) ` Convert angle *x* from degrees to radians + +**Hyperbolic functions** +-------------------------------------------------------------------------------------------------- +:func:`acosh(x) ` Inverse hyperbolic cosine of *x* +:func:`asinh(x) ` Inverse hyperbolic sine of *x* +:func:`atanh(x) ` Inverse hyperbolic tangent of *x* +:func:`cosh(x) ` Hyperbolic cosine of *x* +:func:`sinh(x) ` Hyperbolic sine of *x* +:func:`tanh(x) ` Hyperbolic tangent of *x* + +**Special functions** +-------------------------------------------------------------------------------------------------- +:func:`erf(x) ` `Error function `_ at *x* +:func:`erfc(x) ` `Complementary error function `_ at *x* +:func:`gamma(x) ` `Gamma function `_ at *x* +:func:`lgamma(x) ` Natural logarithm of the absolute value of the `Gamma function `_ at *x* + +**Constants** +-------------------------------------------------------------------------------------------------- +:data:`pi` *π* = 3.141592... +:data:`e` *e* = 2.718281... +:data:`tau` *τ* = 2\ *π* = 6.283185... +:data:`inf` Positive infinity +:data:`nan` "Not a number" (NaN) +==================================================== ============================================ + + Number-theoretic and representation functions --------------------------------------------- @@ -447,11 +533,11 @@ Power and logarithmic functions .. function:: pow(x, y) - Return ``x`` raised to the power ``y``. Exceptional cases follow + Return *x* raised to the power *y*. Exceptional cases follow the IEEE 754 standard as far as possible. In particular, ``pow(1.0, x)`` and ``pow(x, 0.0)`` always return ``1.0``, even - when ``x`` is a zero or a NaN. If both ``x`` and ``y`` are finite, - ``x`` is negative, and ``y`` is not an integer then ``pow(x, y)`` + when *x* is a zero or a NaN. If both *x* and *y* are finite, + *x* is negative, and *y* is not an integer then ``pow(x, y)`` is undefined, and raises :exc:`ValueError`. Unlike the built-in ``**`` operator, :func:`math.pow` converts both From 51ef54abc42e020d7e80549d49ca32310495b4eb Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Fri, 1 Nov 2024 23:15:39 +0300 Subject: [PATCH 019/219] gh-125916: Adapt functools.reduce() to Argument Clinic (#125999) --- Lib/test/test_inspect/test_inspect.py | 4 +-- Modules/_functoolsmodule.c | 42 +++++++++++++----------- Modules/clinic/_functoolsmodule.c.h | 46 ++++++++++++++++++++++++++- 3 files changed, 70 insertions(+), 22 deletions(-) diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index 2250b7e76dac01..a4430a868676e2 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -5708,8 +5708,8 @@ def test_faulthandler_module_has_signatures(self): self._test_module_has_signatures(faulthandler, unsupported_signature=unsupported_signature) def test_functools_module_has_signatures(self): - no_signature = {'reduce'} - self._test_module_has_signatures(functools, no_signature) + unsupported_signature = {"reduce"} + self._test_module_has_signatures(functools, unsupported_signature=unsupported_signature) def test_gc_module_has_signatures(self): import gc diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index da4e088e54621e..d2afe1a1bea018 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -932,15 +932,31 @@ _functools_cmp_to_key_impl(PyObject *module, PyObject *mycmp) /* reduce (used to be a builtin) ********************************************/ -// Not converted to argument clinic, because of `args` in-place modification. -// AC will affect performance. +/*[clinic input] +_functools.reduce + + function as func: object + iterable as seq: object + initial as result: object = NULL + / + +Apply a function of two arguments cumulatively to the items of an iterable, from left to right. + +This effectively reduces the iterable to a single value. If initial is present, +it is placed before the items of the iterable in the calculation, and serves as +a default when the iterable is empty. + +For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) +calculates ((((1 + 2) + 3) + 4) + 5). +[clinic start generated code]*/ + static PyObject * -functools_reduce(PyObject *self, PyObject *args) +_functools_reduce_impl(PyObject *module, PyObject *func, PyObject *seq, + PyObject *result) +/*[clinic end generated code: output=30d898fe1267c79d input=d233c2670cba7f66]*/ { - PyObject *seq, *func, *result = NULL, *it; + PyObject *args, *it; - if (!PyArg_UnpackTuple(args, "reduce", 2, 3, &func, &seq, &result)) - return NULL; if (result != NULL) Py_INCREF(result); @@ -1006,18 +1022,6 @@ functools_reduce(PyObject *self, PyObject *args) return NULL; } -PyDoc_STRVAR(functools_reduce_doc, -"reduce(function, iterable[, initial], /) -> value\n\ -\n\ -Apply a function of two arguments cumulatively to the items of an iterable, from left to right.\n\ -\n\ -This effectively reduces the iterable to a single value. If initial is present,\n\ -it is placed before the items of the iterable in the calculation, and serves as\n\ -a default when the iterable is empty.\n\ -\n\ -For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])\n\ -calculates ((((1 + 2) + 3) + 4) + 5)."); - /* lru_cache object **********************************************************/ /* There are four principal algorithmic differences from the pure python version: @@ -1722,7 +1726,7 @@ PyDoc_STRVAR(_functools_doc, "Tools that operate on functions."); static PyMethodDef _functools_methods[] = { - {"reduce", functools_reduce, METH_VARARGS, functools_reduce_doc}, + _FUNCTOOLS_REDUCE_METHODDEF _FUNCTOOLS_CMP_TO_KEY_METHODDEF {NULL, NULL} /* sentinel */ }; diff --git a/Modules/clinic/_functoolsmodule.c.h b/Modules/clinic/_functoolsmodule.c.h index e98984dc4d3a09..0564921034be47 100644 --- a/Modules/clinic/_functoolsmodule.c.h +++ b/Modules/clinic/_functoolsmodule.c.h @@ -67,6 +67,50 @@ _functools_cmp_to_key(PyObject *module, PyObject *const *args, Py_ssize_t nargs, return return_value; } +PyDoc_STRVAR(_functools_reduce__doc__, +"reduce($module, function, iterable, initial=, /)\n" +"--\n" +"\n" +"Apply a function of two arguments cumulatively to the items of an iterable, from left to right.\n" +"\n" +"This effectively reduces the iterable to a single value. If initial is present,\n" +"it is placed before the items of the iterable in the calculation, and serves as\n" +"a default when the iterable is empty.\n" +"\n" +"For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])\n" +"calculates ((((1 + 2) + 3) + 4) + 5)."); + +#define _FUNCTOOLS_REDUCE_METHODDEF \ + {"reduce", _PyCFunction_CAST(_functools_reduce), METH_FASTCALL, _functools_reduce__doc__}, + +static PyObject * +_functools_reduce_impl(PyObject *module, PyObject *func, PyObject *seq, + PyObject *result); + +static PyObject * +_functools_reduce(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *func; + PyObject *seq; + PyObject *result = NULL; + + if (!_PyArg_CheckPositional("reduce", nargs, 2, 3)) { + goto exit; + } + func = args[0]; + seq = args[1]; + if (nargs < 3) { + goto skip_optional; + } + result = args[2]; +skip_optional: + return_value = _functools_reduce_impl(module, func, seq, result); + +exit: + return return_value; +} + PyDoc_STRVAR(_functools__lru_cache_wrapper_cache_info__doc__, "cache_info($self, /)\n" "--\n" @@ -114,4 +158,4 @@ _functools__lru_cache_wrapper_cache_clear(PyObject *self, PyObject *Py_UNUSED(ig return return_value; } -/*[clinic end generated code: output=755265bb6d5ea751 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=214d6c6307cfcd91 input=a9049054013a1b77]*/ From c84a136511c673f495f466887716b55c13b7e3ac Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Fri, 1 Nov 2024 14:43:30 -0700 Subject: [PATCH 020/219] gh-125560: Pin JIT CI to ubuntu-22.04 (#125564) --- .github/workflows/jit.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/jit.yml b/.github/workflows/jit.yml index 5fb599b232dfb5..48f05818a38f96 100644 --- a/.github/workflows/jit.yml +++ b/.github/workflows/jit.yml @@ -28,7 +28,7 @@ concurrency: jobs: interpreter: name: Interpreter (Debug) - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 timeout-minutes: 90 steps: - uses: actions/checkout@v4 @@ -85,19 +85,19 @@ jobs: compiler: clang - target: x86_64-unknown-linux-gnu/gcc architecture: x86_64 - runner: ubuntu-latest + runner: ubuntu-22.04 compiler: gcc - target: x86_64-unknown-linux-gnu/clang architecture: x86_64 - runner: ubuntu-latest + runner: ubuntu-22.04 compiler: clang - target: aarch64-unknown-linux-gnu/gcc architecture: aarch64 - runner: ubuntu-latest + runner: ubuntu-22.04 compiler: gcc - target: aarch64-unknown-linux-gnu/clang architecture: aarch64 - runner: ubuntu-latest + runner: ubuntu-22.04 compiler: clang env: CC: ${{ matrix.compiler }} @@ -169,7 +169,7 @@ jobs: jit-with-disabled-gil: name: Free-Threaded (Debug) needs: interpreter - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 strategy: matrix: llvm: From 72dd4714f944e5927656aa25f5cd9bdcd3b00a76 Mon Sep 17 00:00:00 2001 From: Cody Maloney Date: Fri, 1 Nov 2024 14:50:49 -0700 Subject: [PATCH 021/219] gh-120754: _io Ensure stat cache is cleared on fd change (#125166) Performed an audit of `fileio.c` and `_pyio` and made sure anytime the fd changes the stat result, if set, is also cleared/changed. There's one case where it's not cleared, if code would clear it in __init__, keep the memory allocated and just do another fstat with the existing memory. --- Lib/_pyio.py | 3 +++ Modules/_io/fileio.c | 11 ++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Lib/_pyio.py b/Lib/_pyio.py index 7b6d10c008d3cb..42b0aea4e2eb2e 100644 --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -1480,6 +1480,7 @@ def __init__(self, file, mode='r', closefd=True, opener=None): """ if self._fd >= 0: # Have to close the existing file first. + self._stat_atopen = None try: if self._closefd: os.close(self._fd) @@ -1583,6 +1584,7 @@ def __init__(self, file, mode='r', closefd=True, opener=None): if e.errno != errno.ESPIPE: raise except: + self._stat_atopen = None if owned_fd is not None: os.close(owned_fd) raise @@ -1756,6 +1758,7 @@ def close(self): called more than once without error. """ if not self.closed: + self._stat_atopen = None try: if self._closefd: os.close(self._fd) diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c index f374592eb95967..cf0f1d671b507a 100644 --- a/Modules/_io/fileio.c +++ b/Modules/_io/fileio.c @@ -131,6 +131,8 @@ internal_close(fileio *self) _Py_END_SUPPRESS_IPH Py_END_ALLOW_THREADS } + PyMem_Free(self->stat_atopen); + self->stat_atopen = NULL; if (err < 0) { errno = save_errno; PyErr_SetFromErrno(PyExc_OSError); @@ -268,8 +270,9 @@ _io_FileIO___init___impl(fileio *self, PyObject *nameobj, const char *mode, if (self->fd >= 0) { if (self->closefd) { /* Have to close the existing file first. */ - if (internal_close(self) < 0) + if (internal_close(self) < 0) { return -1; + } } else self->fd = -1; @@ -523,10 +526,8 @@ _io_FileIO___init___impl(fileio *self, PyObject *nameobj, const char *mode, internal_close(self); _PyErr_ChainExceptions1(exc); } - if (self->stat_atopen != NULL) { - PyMem_Free(self->stat_atopen); - self->stat_atopen = NULL; - } + PyMem_Free(self->stat_atopen); + self->stat_atopen = NULL; done: #ifdef MS_WINDOWS From 28b148fb32e4548b461137d18d1ab6d366395d36 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Sat, 2 Nov 2024 00:53:29 +0300 Subject: [PATCH 022/219] gh-126220: Fix crash on calls to `_lsprof.Profiler` methods with 0 args (backportable) (#126271) Co-authored-by: Erlend E. Aasland --- Lib/test/test_cprofile.py | 16 +++++++++++++ ...-10-31-14-06-28.gh-issue-126220.uJAJCU.rst | 2 ++ Modules/_lsprof.c | 24 +++++++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2024-10-31-14-06-28.gh-issue-126220.uJAJCU.rst diff --git a/Lib/test/test_cprofile.py b/Lib/test/test_cprofile.py index b2595eccc82f70..65720871d5c5f0 100644 --- a/Lib/test/test_cprofile.py +++ b/Lib/test/test_cprofile.py @@ -30,6 +30,22 @@ def test_bad_counter_during_dealloc(self): self.assertEqual(cm.unraisable.exc_type, TypeError) + def test_crash_with_not_enough_args(self): + # gh-126220 + import _lsprof + + for profile in [_lsprof.Profiler(), cProfile.Profile()]: + for method in [ + "_pystart_callback", + "_pyreturn_callback", + "_ccall_callback", + "_creturn_callback", + ]: + with self.subTest(profile=profile, method=method): + method_obj = getattr(profile, method) + with self.assertRaises(TypeError): + method_obj() # should not crash + def test_evil_external_timer(self): # gh-120289 # Disabling profiler in external timer should not crash diff --git a/Misc/NEWS.d/next/Library/2024-10-31-14-06-28.gh-issue-126220.uJAJCU.rst b/Misc/NEWS.d/next/Library/2024-10-31-14-06-28.gh-issue-126220.uJAJCU.rst new file mode 100644 index 00000000000000..555f2f3bafbf33 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-10-31-14-06-28.gh-issue-126220.uJAJCU.rst @@ -0,0 +1,2 @@ +Fix crash in :class:`!cProfile.Profile` and :class:`!_lsprof.Profiler` when their +callbacks were directly called with 0 arguments. diff --git a/Modules/_lsprof.c b/Modules/_lsprof.c index 8b6906234bdc25..06958a078509d9 100644 --- a/Modules/_lsprof.c +++ b/Modules/_lsprof.c @@ -608,6 +608,12 @@ setBuiltins(ProfilerObject *pObj, int nvalue) PyObject* pystart_callback(ProfilerObject* self, PyObject *const *args, Py_ssize_t size) { + if (size < 2) { + PyErr_Format(PyExc_TypeError, + "_pystart_callback expected 2 arguments, got %zd", + size); + return NULL; + } PyObject* code = args[0]; ptrace_enter_call((PyObject*)self, (void *)code, (PyObject *)code); @@ -616,6 +622,12 @@ PyObject* pystart_callback(ProfilerObject* self, PyObject *const *args, Py_ssize PyObject* pyreturn_callback(ProfilerObject* self, PyObject *const *args, Py_ssize_t size) { + if (size < 3) { + PyErr_Format(PyExc_TypeError, + "_pyreturn_callback expected 3 arguments, got %zd", + size); + return NULL; + } PyObject* code = args[0]; ptrace_leave_call((PyObject*)self, (void *)code); @@ -651,6 +663,12 @@ PyObject* get_cfunc_from_callable(PyObject* callable, PyObject* self_arg, PyObje PyObject* ccall_callback(ProfilerObject* self, PyObject *const *args, Py_ssize_t size) { + if (size < 4) { + PyErr_Format(PyExc_TypeError, + "_ccall_callback expected 4 arguments, got %zd", + size); + return NULL; + } if (self->flags & POF_BUILTINS) { PyObject* callable = args[2]; PyObject* self_arg = args[3]; @@ -669,6 +687,12 @@ PyObject* ccall_callback(ProfilerObject* self, PyObject *const *args, Py_ssize_t PyObject* creturn_callback(ProfilerObject* self, PyObject *const *args, Py_ssize_t size) { + if (size < 4) { + PyErr_Format(PyExc_TypeError, + "_creturn_callback expected 4 arguments, got %zd", + size); + return NULL; + } if (self->flags & POF_BUILTINS) { PyObject* callable = args[2]; PyObject* self_arg = args[3]; From 8477951a1c460ff9b7dc7c54e7bf9b66b1722459 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sat, 2 Nov 2024 01:04:31 +0300 Subject: [PATCH 023/219] gh-120026: soft deprecate Py_HUGE_VAL macro (#120027) Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Doc/c-api/conversion.rst | 2 +- Doc/whatsnew/3.14.rst | 4 ++++ Include/floatobject.h | 4 ++-- Include/internal/pycore_pymath.h | 6 +++--- Include/pymath.h | 2 +- .../2024-06-04-13-38-44.gh-issue-120026.uhEvJ9.rst | 1 + Modules/cmathmodule.c | 2 +- Modules/mathmodule.c | 14 +++++++------- Objects/floatobject.c | 2 +- Python/pystrtod.c | 4 ++-- 10 files changed, 23 insertions(+), 18 deletions(-) create mode 100644 Misc/NEWS.d/next/C_API/2024-06-04-13-38-44.gh-issue-120026.uhEvJ9.rst diff --git a/Doc/c-api/conversion.rst b/Doc/c-api/conversion.rst index 4aaf3905e81c8a..c92ef4c653a675 100644 --- a/Doc/c-api/conversion.rst +++ b/Doc/c-api/conversion.rst @@ -105,7 +105,7 @@ The following functions provide locale-independent string to number conversions. If ``s`` represents a value that is too large to store in a float (for example, ``"1e500"`` is such a string on many platforms) then - if ``overflow_exception`` is ``NULL`` return ``Py_HUGE_VAL`` (with + if ``overflow_exception`` is ``NULL`` return ``Py_INFINITY`` (with an appropriate sign) and don't set any exception. Otherwise, ``overflow_exception`` must point to a Python exception object; raise that exception and return ``-1.0``. In both cases, set diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index db32be89cf88ff..21bc289c2be5d8 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -812,6 +812,10 @@ Porting to Python 3.14 Deprecated ---------- +* The :c:macro:`!Py_HUGE_VAL` macro is :term:`soft deprecated`, + use :c:macro:`!Py_INFINITY` instead. + (Contributed by Sergey B Kirpichev in :gh:`120026`.) + * Macros :c:macro:`!Py_IS_NAN`, :c:macro:`!Py_IS_INFINITY` and :c:macro:`!Py_IS_FINITE` are :term:`soft deprecated`, use instead :c:macro:`!isnan`, :c:macro:`!isinf` and diff --git a/Include/floatobject.h b/Include/floatobject.h index 8963c16832a4bc..4d24a76edd5de1 100644 --- a/Include/floatobject.h +++ b/Include/floatobject.h @@ -21,10 +21,10 @@ PyAPI_DATA(PyTypeObject) PyFloat_Type; #define Py_RETURN_INF(sign) \ do { \ if (copysign(1., sign) == 1.) { \ - return PyFloat_FromDouble(Py_HUGE_VAL); \ + return PyFloat_FromDouble(Py_INFINITY); \ } \ else { \ - return PyFloat_FromDouble(-Py_HUGE_VAL); \ + return PyFloat_FromDouble(-Py_INFINITY); \ } \ } while(0) diff --git a/Include/internal/pycore_pymath.h b/Include/internal/pycore_pymath.h index 7a4e1c1eb714f7..eea8996ba68ca0 100644 --- a/Include/internal/pycore_pymath.h +++ b/Include/internal/pycore_pymath.h @@ -33,7 +33,7 @@ extern "C" { static inline void _Py_ADJUST_ERANGE1(double x) { if (errno == 0) { - if (x == Py_HUGE_VAL || x == -Py_HUGE_VAL) { + if (x == Py_INFINITY || x == -Py_INFINITY) { errno = ERANGE; } } @@ -44,8 +44,8 @@ static inline void _Py_ADJUST_ERANGE1(double x) static inline void _Py_ADJUST_ERANGE2(double x, double y) { - if (x == Py_HUGE_VAL || x == -Py_HUGE_VAL || - y == Py_HUGE_VAL || y == -Py_HUGE_VAL) + if (x == Py_INFINITY || x == -Py_INFINITY || + y == Py_INFINITY || y == -Py_INFINITY) { if (errno == 0) { errno = ERANGE; diff --git a/Include/pymath.h b/Include/pymath.h index d8f763f808d662..0ead1f95670fde 100644 --- a/Include/pymath.h +++ b/Include/pymath.h @@ -49,7 +49,7 @@ /* Py_HUGE_VAL should always be the same as Py_INFINITY. But historically * this was not reliable and Python did not require IEEE floats and C99 - * conformity. Prefer Py_INFINITY for new code. + * conformity. The macro was soft deprecated in Python 3.14, use Py_INFINITY instead. */ #ifndef Py_HUGE_VAL # define Py_HUGE_VAL HUGE_VAL diff --git a/Misc/NEWS.d/next/C_API/2024-06-04-13-38-44.gh-issue-120026.uhEvJ9.rst b/Misc/NEWS.d/next/C_API/2024-06-04-13-38-44.gh-issue-120026.uhEvJ9.rst new file mode 100644 index 00000000000000..d43a138724ad33 --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2024-06-04-13-38-44.gh-issue-120026.uhEvJ9.rst @@ -0,0 +1 @@ +The :c:macro:`!Py_HUGE_VAL` macro is :term:`soft deprecated`. diff --git a/Modules/cmathmodule.c b/Modules/cmathmodule.c index e07c2dbd262354..81cbf0d554de3c 100644 --- a/Modules/cmathmodule.c +++ b/Modules/cmathmodule.c @@ -150,7 +150,7 @@ special_type(double d) #define P14 0.25*Py_MATH_PI #define P12 0.5*Py_MATH_PI #define P34 0.75*Py_MATH_PI -#define INF Py_HUGE_VAL +#define INF Py_INFINITY #define N Py_NAN #define U -9.5426319407711027e33 /* unlikely value, used as placeholder */ diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index ad23dadd7b86cc..7e8d8b3f5bafa2 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -438,7 +438,7 @@ m_tgamma(double x) } else { errno = ERANGE; - return Py_HUGE_VAL; + return Py_INFINITY; } } @@ -502,14 +502,14 @@ m_lgamma(double x) if (isnan(x)) return x; /* lgamma(nan) = nan */ else - return Py_HUGE_VAL; /* lgamma(+-inf) = +inf */ + return Py_INFINITY; /* lgamma(+-inf) = +inf */ } /* integer arguments */ if (x == floor(x) && x <= 2.0) { if (x <= 0.0) { errno = EDOM; /* lgamma(n) = inf, divide-by-zero for */ - return Py_HUGE_VAL; /* integers n <= 0 */ + return Py_INFINITY; /* integers n <= 0 */ } else { return 0.0; /* lgamma(1) = lgamma(2) = 0.0 */ @@ -645,7 +645,7 @@ m_log(double x) return log(x); errno = EDOM; if (x == 0.0) - return -Py_HUGE_VAL; /* log(0) = -inf */ + return -Py_INFINITY; /* log(0) = -inf */ else return Py_NAN; /* log(-ve) = nan */ } @@ -688,7 +688,7 @@ m_log2(double x) } else if (x == 0.0) { errno = EDOM; - return -Py_HUGE_VAL; /* log2(0) = -inf, divide-by-zero */ + return -Py_INFINITY; /* log2(0) = -inf, divide-by-zero */ } else { errno = EDOM; @@ -704,7 +704,7 @@ m_log10(double x) return log10(x); errno = EDOM; if (x == 0.0) - return -Py_HUGE_VAL; /* log10(0) = -inf */ + return -Py_INFINITY; /* log10(0) = -inf */ else return Py_NAN; /* log10(-ve) = nan */ } @@ -2126,7 +2126,7 @@ math_ldexp_impl(PyObject *module, double x, PyObject *i) errno = 0; } else if (exp > INT_MAX) { /* overflow */ - r = copysign(Py_HUGE_VAL, x); + r = copysign(Py_INFINITY, x); errno = ERANGE; } else if (exp < INT_MIN) { /* underflow to +-0 */ diff --git a/Objects/floatobject.c b/Objects/floatobject.c index d66863febe8c86..7e14a8ad959590 100644 --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -2390,7 +2390,7 @@ PyFloat_Unpack2(const char *data, int le) if (e == 0x1f) { if (f == 0) { /* Infinity */ - return sign ? -Py_HUGE_VAL : Py_HUGE_VAL; + return sign ? -Py_INFINITY : Py_INFINITY; } else { /* NaN */ diff --git a/Python/pystrtod.c b/Python/pystrtod.c index 2f2b588bd147d8..7b74f613ed563b 100644 --- a/Python/pystrtod.c +++ b/Python/pystrtod.c @@ -43,7 +43,7 @@ _Py_parse_inf_or_nan(const char *p, char **endptr) s += 3; if (case_insensitive_match(s, "inity")) s += 5; - retval = negate ? -Py_HUGE_VAL : Py_HUGE_VAL; + retval = negate ? -Py_INFINITY : Py_INFINITY; } else if (case_insensitive_match(s, "nan")) { s += 3; @@ -286,7 +286,7 @@ _PyOS_ascii_strtod(const char *nptr, char **endptr) string, -1.0 is returned and again ValueError is raised. On overflow (e.g., when trying to convert '1e500' on an IEEE 754 machine), - if overflow_exception is NULL then +-Py_HUGE_VAL is returned, and no Python + if overflow_exception is NULL then +-Py_INFINITY is returned, and no Python exception is raised. Otherwise, overflow_exception should point to a Python exception, this exception will be raised, -1.0 will be returned, and *endptr will point just past the end of the converted value. From f0c6fccd08904787a39269367f09f263d496114c Mon Sep 17 00:00:00 2001 From: mpage Date: Fri, 1 Nov 2024 16:10:58 -0700 Subject: [PATCH 024/219] gh-126255: Ignore warning about JIT being deactivated when perf support is active in `test_embed.InitConfigTests.test_initconfig_api` (#126302) Temporarily ignore warnings about JIT deactivation when perf support is active. This will be reverted as soon as a way is found to determine at run time whether the interpreter was built with JIT. Currently, this is not possible on Windows. Co-authored-by: Kirill Podoprigora Co-authored-by: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Co-authored-by: Pablo Galindo --- Lib/test/test_embed.py | 5 ++++- Lib/test/test_perf_profiler.py | 6 +++--- Python/pylifecycle.c | 7 ++++++- Python/sysmodule.c | 1 + 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py index 4ea43edccda63e..5e886b6c8c38ec 100644 --- a/Lib/test/test_embed.py +++ b/Lib/test/test_embed.py @@ -1,5 +1,6 @@ # Run the tests in Programs/_testembed.c (tests for the CPython embedding APIs) from test import support +from test.libregrtest.utils import get_build_info from test.support import import_helper, os_helper, threading_helper, MS_WINDOWS import unittest @@ -1780,8 +1781,10 @@ def test_initconfig_api(self): 'perf_profiling': 2, } config_dev_mode(preconfig, config) + # Temporarily enable ignore_stderr=True to ignore warnings on JIT builds + # See gh-126255 for more information self.check_all_configs("test_initconfig_api", config, preconfig, - api=API_ISOLATED) + api=API_ISOLATED, ignore_stderr=True) def test_initconfig_get_api(self): self.run_embedded_interpreter("test_initconfig_get_api") diff --git a/Lib/test/test_perf_profiler.py b/Lib/test/test_perf_profiler.py index b55d441759eb69..1e74990878007a 100644 --- a/Lib/test/test_perf_profiler.py +++ b/Lib/test/test_perf_profiler.py @@ -210,14 +210,14 @@ def test_sys_api_with_existing_trampoline(self): sys.activate_stack_trampoline("perf") sys.activate_stack_trampoline("perf") """ - assert_python_ok("-c", code) + assert_python_ok("-c", code, PYTHON_JIT="0") def test_sys_api_with_invalid_trampoline(self): code = """if 1: import sys sys.activate_stack_trampoline("invalid") """ - rc, out, err = assert_python_failure("-c", code) + rc, out, err = assert_python_failure("-c", code, PYTHON_JIT="0") self.assertIn("invalid backend: invalid", err.decode()) def test_sys_api_get_status(self): @@ -228,7 +228,7 @@ def test_sys_api_get_status(self): sys.deactivate_stack_trampoline() assert sys.is_stack_trampoline_active() is False """ - assert_python_ok("-c", code) + assert_python_ok("-c", code, PYTHON_JIT="0") def is_unwinding_reliable_with_frame_pointers(): diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 2efaa9db7d7d58..23882d083844ac 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -1310,12 +1310,17 @@ init_interp_main(PyThreadState *tstate) enabled = *env != '0'; } if (enabled) { +#ifdef _Py_JIT + // perf profiler works fine with tier 2 interpreter, so + // only checking for a "real JIT". if (config->perf_profiling > 0) { (void)PyErr_WarnEx( PyExc_RuntimeWarning, "JIT deactivated as perf profiling support is active", 0); - } else { + } else +#endif + { PyObject *opt = _PyOptimizer_NewUOpOptimizer(); if (opt == NULL) { return _PyStatus_ERR("can't initialize optimizer"); diff --git a/Python/sysmodule.c b/Python/sysmodule.c index cbb73977e1aae6..a4abd7c3c45709 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -2290,6 +2290,7 @@ sys_activate_stack_trampoline_impl(PyObject *module, const char *backend) #ifdef _Py_JIT _PyOptimizerObject* optimizer = _Py_GetOptimizer(); if (optimizer != NULL) { + Py_DECREF(optimizer); PyErr_SetString(PyExc_ValueError, "Cannot activate the perf trampoline if the JIT is active"); return NULL; } From 914356f4d485e378eb692e57d822b893acc0c0da Mon Sep 17 00:00:00 2001 From: rimchoi Date: Sat, 2 Nov 2024 14:08:27 +0900 Subject: [PATCH 025/219] gh-125875: Fix docs typo FORMAT_SPEC to FORMAT_WITH_SPEC (gh-126319) Fix docs typo FORMAT_SPEC to FORMAT_WITH_SPEC --- Doc/library/dis.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst index 6c12d1b5e0dcea..cf203a714ba126 100644 --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -1647,7 +1647,7 @@ iterations of the loop. .. versionadded:: 3.13 -.. opcode:: FORMAT_SPEC +.. opcode:: FORMAT_WITH_SPEC Formats the given value with the given format spec:: From f032f6ba8fec6fab35edeec0eb40cd73e9d58928 Mon Sep 17 00:00:00 2001 From: Nico-Posada <102486290+Nico-Posada@users.noreply.github.com> Date: Sat, 2 Nov 2024 03:46:00 -0400 Subject: [PATCH 026/219] gh-126138: Fix use-after-free in `_asyncio.Task` by evil `__getattribute__` (#126305) Co-authored-by: Carol Willing --- ...-11-01-14-31-41.gh-issue-126138.yTniOG.rst | 3 +++ Modules/_asynciomodule.c | 22 +++++++++++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-11-01-14-31-41.gh-issue-126138.yTniOG.rst diff --git a/Misc/NEWS.d/next/Library/2024-11-01-14-31-41.gh-issue-126138.yTniOG.rst b/Misc/NEWS.d/next/Library/2024-11-01-14-31-41.gh-issue-126138.yTniOG.rst new file mode 100644 index 00000000000000..459eebc82bd42a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-11-01-14-31-41.gh-issue-126138.yTniOG.rst @@ -0,0 +1,3 @@ +Fix a use-after-free crash on :class:`asyncio.Task` objects +whose underlying coroutine yields an object that implements +an evil :meth:`~object.__getattribute__`. Patch by Nico Posada. diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 7483e9c0f43acf..214c18e966c4c1 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -2967,8 +2967,17 @@ task_step_handle_result_impl(asyncio_state *state, TaskObj *task, PyObject *resu if (task->task_must_cancel) { PyObject *r; int is_true; + + // Beware: An evil `__getattribute__` could + // prematurely delete task->task_cancel_msg before the + // task is cancelled, thereby causing a UAF crash. + // + // See https://github.com/python/cpython/issues/126138 + PyObject *task_cancel_msg = Py_NewRef(task->task_cancel_msg); r = PyObject_CallMethodOneArg(result, &_Py_ID(cancel), - task->task_cancel_msg); + task_cancel_msg); + Py_DECREF(task_cancel_msg); + if (r == NULL) { return NULL; } @@ -3060,8 +3069,17 @@ task_step_handle_result_impl(asyncio_state *state, TaskObj *task, PyObject *resu if (task->task_must_cancel) { PyObject *r; int is_true; + + // Beware: An evil `__getattribute__` could + // prematurely delete task->task_cancel_msg before the + // task is cancelled, thereby causing a UAF crash. + // + // See https://github.com/python/cpython/issues/126138 + PyObject *task_cancel_msg = Py_NewRef(task->task_cancel_msg); r = PyObject_CallMethodOneArg(result, &_Py_ID(cancel), - task->task_cancel_msg); + task_cancel_msg); + Py_DECREF(task_cancel_msg); + if (r == NULL) { return NULL; } From cfb1b2f0cb999558a30e61a9e1a62fdb7f55f6a4 Mon Sep 17 00:00:00 2001 From: simple-is-great <103080930+simple-is-great@users.noreply.github.com> Date: Sat, 2 Nov 2024 17:27:07 +0900 Subject: [PATCH 027/219] gh-125522: Remove bare except in test_zlib.test_flushes (gh-126321) --- Lib/test/test_zlib.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/Lib/test/test_zlib.py b/Lib/test/test_zlib.py index 8b4bb8750f8f5c..4d97fe56f3a094 100644 --- a/Lib/test/test_zlib.py +++ b/Lib/test/test_zlib.py @@ -505,20 +505,16 @@ def test_flushes(self): for sync in sync_opt: for level in range(10): - try: + with self.subTest(sync=sync, level=level): obj = zlib.compressobj( level ) a = obj.compress( data[:3000] ) b = obj.flush( sync ) c = obj.compress( data[3000:] ) d = obj.flush() - except: - print("Error for flush mode={}, level={}" - .format(sync, level)) - raise - self.assertEqual(zlib.decompress(b''.join([a,b,c,d])), - data, ("Decompress failed: flush " - "mode=%i, level=%i") % (sync, level)) - del obj + self.assertEqual(zlib.decompress(b''.join([a,b,c,d])), + data, ("Decompress failed: flush " + "mode=%i, level=%i") % (sync, level)) + del obj @unittest.skipUnless(hasattr(zlib, 'Z_SYNC_FLUSH'), 'requires zlib.Z_SYNC_FLUSH') From 10eeec2d4ffb6b09a6d925877b6d9ef6aa6bb59d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B3=A0=EB=B3=91=EC=B0=AC?= <70642609+byungchanKo99@users.noreply.github.com> Date: Sat, 2 Nov 2024 20:37:26 +0900 Subject: [PATCH 028/219] gh-125761: Clarify repeated warning suppression criteria in warnings module (gh-126326) --- Doc/library/warnings.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Doc/library/warnings.rst b/Doc/library/warnings.rst index 1e7c4c8b915e7e..0c7e8543f331db 100644 --- a/Doc/library/warnings.rst +++ b/Doc/library/warnings.rst @@ -180,6 +180,19 @@ If a warning is reported and doesn't match any registered filter then the "default" action is applied (hence its name). + +.. _repeated-warning-suppression-criteria: + +Repeated Warning Suppression Criteria +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The filters that suppress repeated warnings apply the following criteria to determine if a warning is considered a repeat: + +- ``"default"``: A warning is considered a repeat only if the (*message*, *category*, *module*, *lineno*) are all the same. +- ``"module"``: A warning is considered a repeat if the (*message*, *category*, *module*) are the same, ignoring the line number. +- ``"once"``: A warning is considered a repeat if the (*message*, *category*) are the same, ignoring the module and line number. + + .. _describing-warning-filters: Describing Warning Filters From bd4be5e67de5f31e9336ba0fdcd545e88d70b954 Mon Sep 17 00:00:00 2001 From: Lee Dong Wook Date: Sat, 2 Nov 2024 23:07:32 +0900 Subject: [PATCH 029/219] gh-126317: Simplify pickle code by using itertools.batched() (GH-126323) --- Lib/pickle.py | 61 +++++++++++++++++++-------------------------------- 1 file changed, 22 insertions(+), 39 deletions(-) diff --git a/Lib/pickle.py b/Lib/pickle.py index ed8138beb908ee..965e1952fb8c5e 100644 --- a/Lib/pickle.py +++ b/Lib/pickle.py @@ -26,7 +26,7 @@ from types import FunctionType from copyreg import dispatch_table from copyreg import _extension_registry, _inverted_registry, _extension_cache -from itertools import islice +from itertools import batched from functools import partial import sys from sys import maxsize @@ -1033,31 +1033,26 @@ def _batch_appends(self, items, obj): write(APPEND) return - it = iter(items) start = 0 - while True: - tmp = list(islice(it, self._BATCHSIZE)) - n = len(tmp) - if n > 1: + for batch in batched(items, self._BATCHSIZE): + batch_len = len(batch) + if batch_len != 1: write(MARK) - for i, x in enumerate(tmp, start): + for i, x in enumerate(batch, start): try: save(x) except BaseException as exc: exc.add_note(f'when serializing {_T(obj)} item {i}') raise write(APPENDS) - elif n: + else: try: - save(tmp[0]) + save(batch[0]) except BaseException as exc: exc.add_note(f'when serializing {_T(obj)} item {start}') raise write(APPEND) - # else tmp is empty, and we're done - if n < self._BATCHSIZE: - return - start += n + start += batch_len def save_dict(self, obj): if self.bin: @@ -1086,13 +1081,10 @@ def _batch_setitems(self, items, obj): write(SETITEM) return - it = iter(items) - while True: - tmp = list(islice(it, self._BATCHSIZE)) - n = len(tmp) - if n > 1: + for batch in batched(items, self._BATCHSIZE): + if len(batch) != 1: write(MARK) - for k, v in tmp: + for k, v in batch: save(k) try: save(v) @@ -1100,8 +1092,8 @@ def _batch_setitems(self, items, obj): exc.add_note(f'when serializing {_T(obj)} item {k!r}') raise write(SETITEMS) - elif n: - k, v = tmp[0] + else: + k, v = batch[0] save(k) try: save(v) @@ -1109,9 +1101,6 @@ def _batch_setitems(self, items, obj): exc.add_note(f'when serializing {_T(obj)} item {k!r}') raise write(SETITEM) - # else tmp is empty, and we're done - if n < self._BATCHSIZE: - return def save_set(self, obj): save = self.save @@ -1124,21 +1113,15 @@ def save_set(self, obj): write(EMPTY_SET) self.memoize(obj) - it = iter(obj) - while True: - batch = list(islice(it, self._BATCHSIZE)) - n = len(batch) - if n > 0: - write(MARK) - try: - for item in batch: - save(item) - except BaseException as exc: - exc.add_note(f'when serializing {_T(obj)} element') - raise - write(ADDITEMS) - if n < self._BATCHSIZE: - return + for batch in batched(obj, self._BATCHSIZE): + write(MARK) + try: + for item in batch: + save(item) + except BaseException as exc: + exc.add_note(f'when serializing {_T(obj)} element') + raise + write(ADDITEMS) dispatch[set] = save_set def save_frozenset(self, obj): From 868bfcc02ed42a1042851830b79c6877b7f1c7a8 Mon Sep 17 00:00:00 2001 From: rimchoi Date: Sun, 3 Nov 2024 02:04:53 +0900 Subject: [PATCH 030/219] gh-125832: Clarify comment for inlined comprehensions as per PEP-709 (#126322) * Fix comprehensions comment to inlined by pep 709 * Update spacing Co-authored-by: RUANG (James Roy) * Add reference to PEP 709 --------- Co-authored-by: Carol Willing Co-authored-by: RUANG (James Roy) --- Python/codegen.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Python/codegen.c b/Python/codegen.c index d79aee4859e51b..c060ed76f1eb5c 100644 --- a/Python/codegen.c +++ b/Python/codegen.c @@ -4087,9 +4087,12 @@ codegen_call_helper(compiler *c, location loc, return codegen_call_helper_impl(c, loc, n, args, NULL, keywords); } -/* List and set comprehensions and generator expressions work by creating a - nested function to perform the actual iteration. This means that the - iteration variables don't leak into the current scope. +/* List and set comprehensions work by being inlined at the location where + they are defined. The isolation of iteration variables is provided by + pushing/popping clashing locals on the stack. Generator expressions work + by creating a nested function to perform the actual iteration. + This means that the iteration variables don't leak into the current scope. + See https://peps.python.org/pep-0709/ for additional information. The defined function is called immediately following its definition, with the result of that call being the result of the expression. The LC/SC version returns the populated container, while the GE version is From 7d7d56d8b1147a6b85e1c09d01b164df7c5c4942 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sat, 2 Nov 2024 20:11:12 +0300 Subject: [PATCH 031/219] gh-99880: document rounding mode for new-style formatting (GH-121481) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * gh-99880: document rounding mode for new-style formatting The CPython uses _Py_dg_dtoa(), which does rounding to nearest with half to even tie-breaking rule. If that functions is unavailable, PyOS_double_to_string() fallbacks to system snprintf(). Since CPython 3.12, build requirements include C11 compiler *and* support for IEEE 754 floating point numbers (Annex F). This means that FE_TONEAREST macro is available and, per default, printf-like functions should use same rounding mode as _Py_dg_dtoa(). * Update Doc/library/string.rst Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --------- Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Doc/library/string.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Doc/library/string.rst b/Doc/library/string.rst index 9e8e44a8abe770..a000bb49f14800 100644 --- a/Doc/library/string.rst +++ b/Doc/library/string.rst @@ -589,6 +589,11 @@ The available presentation types for :class:`float` and | | as altered by the other format modifiers. | +---------+----------------------------------------------------------+ +The result should be correctly rounded to a given precision ``p`` of digits +after the decimal point. The rounding mode for :class:`float` matches that +of the :func:`round` builtin. For :class:`~decimal.Decimal`, the rounding +mode of the current :ref:`context ` will be used. + The available presentation types for :class:`complex` are the same as those for :class:`float` (``'%'`` is not allowed). Both the real and imaginary components of a complex number are formatted as floating-point numbers, according to the From 9ad57cf58b76d1abe4786fb2e4c77a02e100d047 Mon Sep 17 00:00:00 2001 From: Cody Maloney Date: Sat, 2 Nov 2024 20:37:21 -0700 Subject: [PATCH 032/219] gh-120754: Add a strace helper and test set of syscalls for open().read(), Take 2 (#123413) --- Lib/test/support/strace_helper.py | 170 ++++++++++++++++++++++++++++++ Lib/test/test_fileio.py | 138 +++++++++++++++++++++++- Lib/test/test_subprocess.py | 53 ++++------ Misc/ACKS | 1 + 4 files changed, 329 insertions(+), 33 deletions(-) create mode 100644 Lib/test/support/strace_helper.py diff --git a/Lib/test/support/strace_helper.py b/Lib/test/support/strace_helper.py new file mode 100644 index 00000000000000..90d4b5bccb6fa3 --- /dev/null +++ b/Lib/test/support/strace_helper.py @@ -0,0 +1,170 @@ +import re +import sys +import textwrap +import unittest +from dataclasses import dataclass +from functools import cache +from test import support +from test.support.script_helper import run_python_until_end + +_strace_binary = "/usr/bin/strace" +_syscall_regex = re.compile( + r"(?P[^(]*)\((?P[^)]*)\)\s*[=]\s*(?P.+)") +_returncode_regex = re.compile( + br"\+\+\+ exited with (?P\d+) \+\+\+") + + +@dataclass +class StraceEvent: + syscall: str + args: list[str] + returncode: str + + +@dataclass +class StraceResult: + strace_returncode: int + python_returncode: int + + """The event messages generated by strace. This is very similar to the + stderr strace produces with returncode marker section removed.""" + event_bytes: bytes + stdout: bytes + stderr: bytes + + def events(self): + """Parse event_bytes data into system calls for easier processing. + + This assumes the program under inspection doesn't print any non-utf8 + strings which would mix into the strace output.""" + decoded_events = self.event_bytes.decode('utf-8') + matches = [ + _syscall_regex.match(event) + for event in decoded_events.splitlines() + ] + return [ + StraceEvent(match["syscall"], + [arg.strip() for arg in (match["args"].split(","))], + match["returncode"]) for match in matches if match + ] + + def sections(self): + """Find all "MARK " writes and use them to make groups of events. + + This is useful to avoid variable / overhead events, like those at + interpreter startup or when opening a file so a test can verify just + the small case under study.""" + current_section = "__startup" + sections = {current_section: []} + for event in self.events(): + if event.syscall == 'write' and len( + event.args) > 2 and event.args[1].startswith("\"MARK "): + # Found a new section, don't include the write in the section + # but all events until next mark should be in that section + current_section = event.args[1].split( + " ", 1)[1].removesuffix('\\n"') + if current_section not in sections: + sections[current_section] = list() + else: + sections[current_section].append(event) + + return sections + + +@support.requires_subprocess() +def strace_python(code, strace_flags, check=True): + """Run strace and return the trace. + + Sets strace_returncode and python_returncode to `-1` on error.""" + res = None + + def _make_error(reason, details): + return StraceResult( + strace_returncode=-1, + python_returncode=-1, + event_bytes=f"error({reason},details={details}) = -1".encode('utf-8'), + stdout=res.out if res else b"", + stderr=res.err if res else b"") + + # Run strace, and get out the raw text + try: + res, cmd_line = run_python_until_end( + "-c", + textwrap.dedent(code), + __run_using_command=[_strace_binary] + strace_flags) + except OSError as err: + return _make_error("Caught OSError", err) + + if check and res.rc: + res.fail(cmd_line) + + # Get out program returncode + stripped = res.err.strip() + output = stripped.rsplit(b"\n", 1) + if len(output) != 2: + return _make_error("Expected strace events and exit code line", + stripped[-50:]) + + returncode_match = _returncode_regex.match(output[1]) + if not returncode_match: + return _make_error("Expected to find returncode in last line.", + output[1][:50]) + + python_returncode = int(returncode_match["returncode"]) + if check and python_returncode: + res.fail(cmd_line) + + return StraceResult(strace_returncode=res.rc, + python_returncode=python_returncode, + event_bytes=output[0], + stdout=res.out, + stderr=res.err) + + +def get_events(code, strace_flags, prelude, cleanup): + # NOTE: The flush is currently required to prevent the prints from getting + # buffered and done all at once at exit + prelude = textwrap.dedent(prelude) + code = textwrap.dedent(code) + cleanup = textwrap.dedent(cleanup) + to_run = f""" +print("MARK prelude", flush=True) +{prelude} +print("MARK code", flush=True) +{code} +print("MARK cleanup", flush=True) +{cleanup} +print("MARK __shutdown", flush=True) + """ + trace = strace_python(to_run, strace_flags) + all_sections = trace.sections() + return all_sections['code'] + + +def get_syscalls(code, strace_flags, prelude="", cleanup=""): + """Get the syscalls which a given chunk of python code generates""" + events = get_events(code, strace_flags, prelude=prelude, cleanup=cleanup) + return [ev.syscall for ev in events] + + +# Moderately expensive (spawns a subprocess), so share results when possible. +@cache +def _can_strace(): + res = strace_python("import sys; sys.exit(0)", [], check=False) + assert res.events(), "Should have parsed multiple calls" + + return res.strace_returncode == 0 and res.python_returncode == 0 + + +def requires_strace(): + if sys.platform != "linux": + return unittest.skip("Linux only, requires strace.") + + if support.check_sanitizer(address=True, memory=True): + return unittest.skip("LeakSanitizer does not work under ptrace (strace, gdb, etc)") + + return unittest.skipUnless(_can_strace(), "Requires working strace") + + +__all__ = ["get_events", "get_syscalls", "requires_strace", "strace_python", + "StraceEvent", "StraceResult"] diff --git a/Lib/test/test_fileio.py b/Lib/test/test_fileio.py index 0611d1749f41c1..d60aabcdf1ae22 100644 --- a/Lib/test/test_fileio.py +++ b/Lib/test/test_fileio.py @@ -11,7 +11,7 @@ from test.support import ( cpython_only, swap_attr, gc_collect, is_emscripten, is_wasi, - infinite_recursion, + infinite_recursion, strace_helper ) from test.support.os_helper import ( TESTFN, TESTFN_ASCII, TESTFN_UNICODE, make_bad_fd, @@ -24,6 +24,9 @@ import _pyio # Python implementation of io +_strace_flags=["--trace=%file,%desc"] + + class AutoFileTests: # file tests for which a test file is automatically set up @@ -359,6 +362,139 @@ def testErrnoOnClosedReadinto(self, f): a = array('b', b'x'*10) f.readinto(a) + @strace_helper.requires_strace() + def test_syscalls_read(self): + """Check that the set of system calls produced by the I/O stack is what + is expected for various read cases. + + It's expected as bits of the I/O implementation change, this will need + to change. The goal is to catch changes that unintentionally add + additional systemcalls (ex. additional calls have been looked at in + bpo-21679 and gh-120754). + """ + self.f.write(b"Hello, World!") + self.f.close() + + + def check_readall(name, code, prelude="", cleanup="", + extra_checks=None): + with self.subTest(name=name): + syscalls = strace_helper.get_events(code, _strace_flags, + prelude=prelude, + cleanup=cleanup) + + # The first call should be an open that returns a + # file descriptor (fd). Afer that calls may vary. Once the file + # is opened, check calls refer to it by fd as the filename + # could be removed from the filesystem, renamed, etc. See: + # Time-of-check time-of-use (TOCTOU) software bug class. + # + # There are a number of related but distinct open system calls + # so not checking precise name here. + self.assertGreater( + len(syscalls), + 1, + f"Should have had at least an open call|calls={syscalls}") + fd_str = syscalls[0].returncode + + # All other calls should contain the fd in their argument set. + for ev in syscalls[1:]: + self.assertIn( + fd_str, + ev.args, + f"Looking for file descriptor in arguments|ev={ev}" + ) + + # There are a number of related syscalls used to implement + # behaviors in a libc (ex. fstat, newfstatat, statx, open, openat). + # Allow any that use the same substring. + def count_similarname(name): + return len([ev for ev in syscalls if name in ev.syscall]) + + checks = [ + # Should open and close the file exactly once + ("open", 1), + ("close", 1), + # There should no longer be an isatty call (All files being + # tested are block devices / not character devices). + ('ioctl', 0), + # Should only have one fstat (bpo-21679, gh-120754) + # note: It's important this uses a fd rather than filename, + # That is validated by the `fd` check above. + # note: fstat, newfstatat, and statx have all been observed + # here in the underlying C library implementations. + ("stat", 1) + ] + + if extra_checks: + checks += extra_checks + + for call, count in checks: + self.assertEqual( + count_similarname(call), + count, + msg=f"call={call}|count={count}|syscalls={syscalls}" + ) + + # "open, read, close" file using different common patterns. + check_readall( + "open builtin with default options", + f""" + f = open('{TESTFN}') + f.read() + f.close() + """ + ) + + check_readall( + "open in binary mode", + f""" + f = open('{TESTFN}', 'rb') + f.read() + f.close() + """ + ) + + check_readall( + "open in text mode", + f""" + f = open('{TESTFN}', 'rt') + f.read() + f.close() + """, + # GH-122111: read_text uses BufferedIO which requires looking up + # position in file. `read_bytes` disables that buffering and avoids + # these calls which is tested the `pathlib read_bytes` case. + extra_checks=[("seek", 1)] + ) + + check_readall( + "pathlib read_bytes", + "p.read_bytes()", + prelude=f"""from pathlib import Path; p = Path("{TESTFN}")""", + # GH-122111: Buffering is disabled so these calls are avoided. + extra_checks=[("seek", 0)] + ) + + check_readall( + "pathlib read_text", + "p.read_text()", + prelude=f"""from pathlib import Path; p = Path("{TESTFN}")""" + ) + + # Focus on just `read()`. + calls = strace_helper.get_syscalls( + prelude=f"f = open('{TESTFN}')", + code="f.read()", + cleanup="f.close()", + strace_flags=_strace_flags + ) + # One to read all the bytes + # One to read the EOF and get a size 0 return. + self.assertEqual(calls.count("read"), 2) + + + class CAutoFileTests(AutoFileTests, unittest.TestCase): FileIO = _io.FileIO modulename = '_io' diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index f065b9c9bb1c2c..e45701dfe033a6 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -4,6 +4,7 @@ from test.support import check_sanitizer from test.support import import_helper from test.support import os_helper +from test.support import strace_helper from test.support import warnings_helper from test.support.script_helper import assert_python_ok import subprocess @@ -3415,7 +3416,7 @@ def __del__(self): @unittest.skipIf(not sysconfig.get_config_var("HAVE_VFORK"), "vfork() not enabled by configure.") - @unittest.skipIf(sys.platform != "linux", "Linux only, requires strace.") + @strace_helper.requires_strace() @mock.patch("subprocess._USE_POSIX_SPAWN", new=False) def test_vfork_used_when_expected(self): # This is a performance regression test to ensure we default to using @@ -3423,36 +3424,25 @@ def test_vfork_used_when_expected(self): # Technically this test could pass when posix_spawn is used as well # because libc tends to implement that internally using vfork. But # that'd just be testing a libc+kernel implementation detail. - strace_binary = "/usr/bin/strace" - # The only system calls we are interested in. - strace_filter = "--trace=clone,clone2,clone3,fork,vfork,exit,exit_group" - true_binary = "/bin/true" - strace_command = [strace_binary, strace_filter] - try: - does_strace_work_process = subprocess.run( - strace_command + [true_binary], - stderr=subprocess.PIPE, - stdout=subprocess.DEVNULL, - ) - rc = does_strace_work_process.returncode - stderr = does_strace_work_process.stderr - except OSError: - rc = -1 - stderr = "" - if rc or (b"+++ exited with 0 +++" not in stderr): - self.skipTest("strace not found or not working as expected.") + # Are intersted in the system calls: + # clone,clone2,clone3,fork,vfork,exit,exit_group + # Unfortunately using `--trace` with that list to strace fails because + # not all are supported on all platforms (ex. clone2 is ia64 only...) + # So instead use `%process` which is recommended by strace, and contains + # the above. + true_binary = "/bin/true" + strace_args = ["--trace=%process"] with self.subTest(name="default_is_vfork"): - vfork_result = assert_python_ok( - "-c", - textwrap.dedent(f"""\ - import subprocess - subprocess.check_call([{true_binary!r}])"""), - __run_using_command=strace_command, + vfork_result = strace_helper.strace_python( + f"""\ + import subprocess + subprocess.check_call([{true_binary!r}])""", + strace_args ) # Match both vfork() and clone(..., flags=...|CLONE_VFORK|...) - self.assertRegex(vfork_result.err, br"(?i)vfork") + self.assertRegex(vfork_result.event_bytes, br"(?i)vfork") # Do NOT check that fork() or other clones did not happen. # If the OS denys the vfork it'll fallback to plain fork(). @@ -3465,9 +3455,8 @@ def test_vfork_used_when_expected(self): ("setgroups", "", "extra_groups=[]", True), ): with self.subTest(name=sub_name): - non_vfork_result = assert_python_ok( - "-c", - textwrap.dedent(f"""\ + non_vfork_result = strace_helper.strace_python( + f"""\ import subprocess {preamble} try: @@ -3475,11 +3464,11 @@ def test_vfork_used_when_expected(self): [{true_binary!r}], **dict({sp_kwarg})) except PermissionError: if not {expect_permission_error}: - raise"""), - __run_using_command=strace_command, + raise""", + strace_args ) # Ensure neither vfork() or clone(..., flags=...|CLONE_VFORK|...). - self.assertNotRegex(non_vfork_result.err, br"(?i)vfork") + self.assertNotRegex(non_vfork_result.event_bytes, br"(?i)vfork") @unittest.skipUnless(mswindows, "Windows specific tests") diff --git a/Misc/ACKS b/Misc/ACKS index a1769d9601a2ea..5e36eda554af0f 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1164,6 +1164,7 @@ Grzegorz Makarewicz David Malcolm Greg Malcolm William Mallard +Cody Maloney Ken Manheimer Vladimir Marangozov Colin Marc From 8161afe51c65afbf0332da58837d94975cec9f65 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sun, 3 Nov 2024 13:15:07 +0900 Subject: [PATCH 033/219] gh-125832: Reformat comments for inlined comprehensions (gh-126346) --- Python/codegen.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Python/codegen.c b/Python/codegen.c index c060ed76f1eb5c..d6ba85887e3860 100644 --- a/Python/codegen.c +++ b/Python/codegen.c @@ -4088,11 +4088,11 @@ codegen_call_helper(compiler *c, location loc, } /* List and set comprehensions work by being inlined at the location where - they are defined. The isolation of iteration variables is provided by - pushing/popping clashing locals on the stack. Generator expressions work - by creating a nested function to perform the actual iteration. - This means that the iteration variables don't leak into the current scope. - See https://peps.python.org/pep-0709/ for additional information. + they are defined. The isolation of iteration variables is provided by + pushing/popping clashing locals on the stack. Generator expressions work + by creating a nested function to perform the actual iteration. + This means that the iteration variables don't leak into the current scope. + See https://peps.python.org/pep-0709/ for additional information. The defined function is called immediately following its definition, with the result of that call being the result of the expression. The LC/SC version returns the populated container, while the GE version is From 556dc9b8a78bad296513221f3f414a3f8fd0ae70 Mon Sep 17 00:00:00 2001 From: Cody Maloney Date: Sat, 2 Nov 2024 22:28:51 -0700 Subject: [PATCH 034/219] gh-113977, gh-120754: Remove unbounded reads from zipfile (GH-122101) GH-113977, GH-120754: Remove unbounded reads from zipfile Read without a size may read an unbounded amount of data + allocate unbounded size buffers. Move to capped size reads to prevent potential issues. Co-authored-by: Daniel Hillier Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> --- Lib/zipfile/__init__.py | 6 +++--- .../Library/2024-07-23-02-24-50.gh-issue-120754.nHb5mG.rst | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-07-23-02-24-50.gh-issue-120754.nHb5mG.rst diff --git a/Lib/zipfile/__init__.py b/Lib/zipfile/__init__.py index e2aaf8bab4913d..08c83cfb760250 100644 --- a/Lib/zipfile/__init__.py +++ b/Lib/zipfile/__init__.py @@ -309,7 +309,7 @@ def _EndRecData(fpin): fpin.seek(-sizeEndCentDir, 2) except OSError: return None - data = fpin.read() + data = fpin.read(sizeEndCentDir) if (len(data) == sizeEndCentDir and data[0:4] == stringEndArchive and data[-2:] == b"\000\000"): @@ -329,9 +329,9 @@ def _EndRecData(fpin): # record signature. The comment is the last item in the ZIP file and may be # up to 64K long. It is assumed that the "end of central directory" magic # number does not appear in the comment. - maxCommentStart = max(filesize - (1 << 16) - sizeEndCentDir, 0) + maxCommentStart = max(filesize - ZIP_MAX_COMMENT - sizeEndCentDir, 0) fpin.seek(maxCommentStart, 0) - data = fpin.read() + data = fpin.read(ZIP_MAX_COMMENT + sizeEndCentDir) start = data.rfind(stringEndArchive) if start >= 0: # found the magic number; attempt to unpack and interpret diff --git a/Misc/NEWS.d/next/Library/2024-07-23-02-24-50.gh-issue-120754.nHb5mG.rst b/Misc/NEWS.d/next/Library/2024-07-23-02-24-50.gh-issue-120754.nHb5mG.rst new file mode 100644 index 00000000000000..6c33e7b7ec7716 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-07-23-02-24-50.gh-issue-120754.nHb5mG.rst @@ -0,0 +1 @@ +Update unbounded ``read`` calls in :mod:`zipfile` to specify an explicit ``size`` putting a limit on how much data they may read. This also updates handling around ZIP max comment size to match the standard instead of reading comments that are one byte too long. From dcae5cd6abaae4f73e656ebc054f30d3f15ca7b8 Mon Sep 17 00:00:00 2001 From: "Tomas R." Date: Sun, 3 Nov 2024 15:01:09 +0100 Subject: [PATCH 035/219] gh-104400: Add more tests to pygettext (GH-108173) --- Lib/test/test_tools/i18n_data/docstrings.pot | 40 +++++++ Lib/test/test_tools/i18n_data/docstrings.py | 41 +++++++ Lib/test/test_tools/i18n_data/fileloc.pot | 35 ++++++ Lib/test/test_tools/i18n_data/fileloc.py | 26 +++++ Lib/test/test_tools/i18n_data/messages.pot | 67 +++++++++++ Lib/test/test_tools/i18n_data/messages.py | 64 +++++++++++ Lib/test/test_tools/test_i18n.py | 110 +++++++++++++++---- Makefile.pre.in | 1 + 8 files changed, 363 insertions(+), 21 deletions(-) create mode 100644 Lib/test/test_tools/i18n_data/docstrings.pot create mode 100644 Lib/test/test_tools/i18n_data/docstrings.py create mode 100644 Lib/test/test_tools/i18n_data/fileloc.pot create mode 100644 Lib/test/test_tools/i18n_data/fileloc.py create mode 100644 Lib/test/test_tools/i18n_data/messages.pot create mode 100644 Lib/test/test_tools/i18n_data/messages.py diff --git a/Lib/test/test_tools/i18n_data/docstrings.pot b/Lib/test/test_tools/i18n_data/docstrings.pot new file mode 100644 index 00000000000000..5af1d41422ff62 --- /dev/null +++ b/Lib/test/test_tools/i18n_data/docstrings.pot @@ -0,0 +1,40 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2000-01-01 00:00+0000\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: pygettext.py 1.5\n" + + +#: docstrings.py:7 +#, docstring +msgid "" +msgstr "" + +#: docstrings.py:18 +#, docstring +msgid "" +"multiline\n" +" docstring\n" +" " +msgstr "" + +#: docstrings.py:25 +#, docstring +msgid "docstring1" +msgstr "" + +#: docstrings.py:30 +#, docstring +msgid "Hello, {}!" +msgstr "" + diff --git a/Lib/test/test_tools/i18n_data/docstrings.py b/Lib/test/test_tools/i18n_data/docstrings.py new file mode 100644 index 00000000000000..85d7f159d37775 --- /dev/null +++ b/Lib/test/test_tools/i18n_data/docstrings.py @@ -0,0 +1,41 @@ +# Test docstring extraction +from gettext import gettext as _ + + +# Empty docstring +def test(x): + """""" + + +# Leading empty line +def test2(x): + + """docstring""" # XXX This should be extracted but isn't. + + +# XXX Multiline docstrings should be cleaned with `inspect.cleandoc`. +def test3(x): + """multiline + docstring + """ + + +# Multiple docstrings - only the first should be extracted +def test4(x): + """docstring1""" + """docstring2""" + + +def test5(x): + """Hello, {}!""".format("world!") # XXX This should not be extracted. + + +# Nested docstrings +def test6(x): + def inner(y): + """nested docstring""" # XXX This should be extracted but isn't. + + +class Outer: + class Inner: + "nested class docstring" # XXX This should be extracted but isn't. diff --git a/Lib/test/test_tools/i18n_data/fileloc.pot b/Lib/test/test_tools/i18n_data/fileloc.pot new file mode 100644 index 00000000000000..dbd28687a73556 --- /dev/null +++ b/Lib/test/test_tools/i18n_data/fileloc.pot @@ -0,0 +1,35 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2000-01-01 00:00+0000\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: pygettext.py 1.5\n" + + +#: fileloc.py:5 fileloc.py:6 +msgid "foo" +msgstr "" + +#: fileloc.py:9 +msgid "bar" +msgstr "" + +#: fileloc.py:14 fileloc.py:18 +#, docstring +msgid "docstring" +msgstr "" + +#: fileloc.py:22 fileloc.py:26 +#, docstring +msgid "baz" +msgstr "" + diff --git a/Lib/test/test_tools/i18n_data/fileloc.py b/Lib/test/test_tools/i18n_data/fileloc.py new file mode 100644 index 00000000000000..c5d4d0595fea52 --- /dev/null +++ b/Lib/test/test_tools/i18n_data/fileloc.py @@ -0,0 +1,26 @@ +# Test file locations +from gettext import gettext as _ + +# Duplicate strings +_('foo') +_('foo') + +# Duplicate strings on the same line should only add one location to the output +_('bar'), _('bar') + + +# Duplicate docstrings +class A: + """docstring""" + + +def f(): + """docstring""" + + +# Duplicate message and docstring +_('baz') + + +def g(): + """baz""" diff --git a/Lib/test/test_tools/i18n_data/messages.pot b/Lib/test/test_tools/i18n_data/messages.pot new file mode 100644 index 00000000000000..ddfbd18349ef4f --- /dev/null +++ b/Lib/test/test_tools/i18n_data/messages.pot @@ -0,0 +1,67 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2000-01-01 00:00+0000\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: pygettext.py 1.5\n" + + +#: messages.py:5 +msgid "" +msgstr "" + +#: messages.py:8 messages.py:9 +msgid "parentheses" +msgstr "" + +#: messages.py:12 +msgid "Hello, world!" +msgstr "" + +#: messages.py:15 +msgid "" +"Hello,\n" +" multiline!\n" +msgstr "" + +#: messages.py:29 +msgid "Hello, {}!" +msgstr "" + +#: messages.py:33 +msgid "1" +msgstr "" + +#: messages.py:33 +msgid "2" +msgstr "" + +#: messages.py:34 messages.py:35 +msgid "A" +msgstr "" + +#: messages.py:34 messages.py:35 +msgid "B" +msgstr "" + +#: messages.py:36 +msgid "set" +msgstr "" + +#: messages.py:42 +msgid "nested string" +msgstr "" + +#: messages.py:47 +msgid "baz" +msgstr "" + diff --git a/Lib/test/test_tools/i18n_data/messages.py b/Lib/test/test_tools/i18n_data/messages.py new file mode 100644 index 00000000000000..f220294b8d5c67 --- /dev/null +++ b/Lib/test/test_tools/i18n_data/messages.py @@ -0,0 +1,64 @@ +# Test message extraction +from gettext import gettext as _ + +# Empty string +_("") + +# Extra parentheses +(_("parentheses")) +((_("parentheses"))) + +# Multiline strings +_("Hello, " + "world!") + +_("""Hello, + multiline! +""") + +# Invalid arguments +_() +_(None) +_(1) +_(False) +_(x="kwargs are not allowed") +_("foo", "bar") +_("something", x="something else") + +# .format() +_("Hello, {}!").format("world") # valid +_("Hello, {}!".format("world")) # invalid + +# Nested structures +_("1"), _("2") +arr = [_("A"), _("B")] +obj = {'a': _("A"), 'b': _("B")} +{{{_('set')}}} + + +# Nested functions and classes +def test(): + _("nested string") # XXX This should be extracted but isn't. + [_("nested string")] + + +class Foo: + def bar(self): + return _("baz") + + +def bar(x=_('default value')): # XXX This should be extracted but isn't. + pass + + +def baz(x=[_('default value')]): # XXX This should be extracted but isn't. + pass + + +# Shadowing _() +def _(x): + pass + + +def _(x="don't extract me"): + pass diff --git a/Lib/test/test_tools/test_i18n.py b/Lib/test/test_tools/test_i18n.py index c083a04475e726..21dead8f943bb7 100644 --- a/Lib/test/test_tools/test_i18n.py +++ b/Lib/test/test_tools/test_i18n.py @@ -1,9 +1,11 @@ """Tests to cover the Tools/i18n package""" import os +import re import sys import unittest from textwrap import dedent +from pathlib import Path from test.support.script_helper import assert_python_ok from test.test_tools import skip_if_missing, toolsdir @@ -12,20 +14,47 @@ skip_if_missing() +DATA_DIR = Path(__file__).resolve().parent / 'i18n_data' + + +def normalize_POT_file(pot): + """Normalize the POT creation timestamp, charset and + file locations to make the POT file easier to compare. + + """ + # Normalize the creation date. + date_pattern = re.compile(r'"POT-Creation-Date: .+?\\n"') + header = r'"POT-Creation-Date: 2000-01-01 00:00+0000\\n"' + pot = re.sub(date_pattern, header, pot) + + # Normalize charset to UTF-8 (currently there's no way to specify the output charset). + charset_pattern = re.compile(r'"Content-Type: text/plain; charset=.+?\\n"') + charset = r'"Content-Type: text/plain; charset=UTF-8\\n"' + pot = re.sub(charset_pattern, charset, pot) + + # Normalize file location path separators in case this test is + # running on Windows (which uses '\'). + fileloc_pattern = re.compile(r'#:.+') + + def replace(match): + return match[0].replace(os.sep, "/") + pot = re.sub(fileloc_pattern, replace, pot) + return pot + class Test_pygettext(unittest.TestCase): """Tests for the pygettext.py tool""" - script = os.path.join(toolsdir,'i18n', 'pygettext.py') + script = Path(toolsdir, 'i18n', 'pygettext.py') def get_header(self, data): """ utility: return the header of a .po file as a dictionary """ headers = {} for line in data.split('\n'): - if not line or line.startswith(('#', 'msgid','msgstr')): + if not line or line.startswith(('#', 'msgid', 'msgstr')): continue line = line.strip('"') - key, val = line.split(':',1) + key, val = line.split(':', 1) headers[key] = val.strip() return headers @@ -53,13 +82,18 @@ def get_msgids(self, data): return msgids + def assert_POT_equal(self, expected, actual): + """Check if two POT files are equal""" + self.maxDiff = None + self.assertEqual(normalize_POT_file(expected), normalize_POT_file(actual)) + def extract_docstrings_from_str(self, module_content): """ utility: return all msgids extracted from module_content """ filename = 'test_docstrings.py' with temp_cwd(None) as cwd: with open(filename, 'w', encoding='utf-8') as fp: fp.write(module_content) - assert_python_ok(self.script, '-D', filename) + assert_python_ok('-Xutf8', self.script, '-D', filename) with open('messages.pot', encoding='utf-8') as fp: data = fp.read() return self.get_msgids(data) @@ -69,7 +103,7 @@ def test_header(self): http://www.gnu.org/software/gettext/manual/gettext.html#Header-Entry """ with temp_cwd(None) as cwd: - assert_python_ok(self.script) + assert_python_ok('-Xutf8', self.script) with open('messages.pot', encoding='utf-8') as fp: data = fp.read() header = self.get_header(data) @@ -96,7 +130,7 @@ def test_POT_Creation_Date(self): """ Match the date format from xgettext for POT-Creation-Date """ from datetime import datetime with temp_cwd(None) as cwd: - assert_python_ok(self.script) + assert_python_ok('-Xutf8', self.script) with open('messages.pot', encoding='utf-8') as fp: data = fp.read() header = self.get_header(data) @@ -310,6 +344,20 @@ def test_calls_in_fstring_with_partially_wrong_expression(self): self.assertNotIn('foo', msgids) self.assertIn('bar', msgids) + def test_pygettext_output(self): + """Test that the pygettext output exactly matches snapshots.""" + for input_file in DATA_DIR.glob('*.py'): + output_file = input_file.with_suffix('.pot') + with self.subTest(input_file=f'i18n_data/{input_file}'): + contents = input_file.read_text(encoding='utf-8') + with temp_cwd(None): + Path(input_file.name).write_text(contents) + assert_python_ok('-Xutf8', self.script, '--docstrings', input_file.name) + output = Path('messages.pot').read_text(encoding='utf-8') + + expected = output_file.read_text(encoding='utf-8') + self.assert_POT_equal(expected, output) + def test_files_list(self): """Make sure the directories are inspected for source files bpo-31920 @@ -318,21 +366,41 @@ def test_files_list(self): text2 = 'Text to translate2' text3 = 'Text to ignore' with temp_cwd(None), temp_dir(None) as sdir: - os.mkdir(os.path.join(sdir, 'pypkg')) - with open(os.path.join(sdir, 'pypkg', 'pymod.py'), 'w', - encoding='utf-8') as sfile: - sfile.write(f'_({text1!r})') - os.mkdir(os.path.join(sdir, 'pkg.py')) - with open(os.path.join(sdir, 'pkg.py', 'pymod2.py'), 'w', - encoding='utf-8') as sfile: - sfile.write(f'_({text2!r})') - os.mkdir(os.path.join(sdir, 'CVS')) - with open(os.path.join(sdir, 'CVS', 'pymod3.py'), 'w', - encoding='utf-8') as sfile: - sfile.write(f'_({text3!r})') - assert_python_ok(self.script, sdir) - with open('messages.pot', encoding='utf-8') as fp: - data = fp.read() + pymod = Path(sdir, 'pypkg', 'pymod.py') + pymod.parent.mkdir() + pymod.write_text(f'_({text1!r})', encoding='utf-8') + + pymod2 = Path(sdir, 'pkg.py', 'pymod2.py') + pymod2.parent.mkdir() + pymod2.write_text(f'_({text2!r})', encoding='utf-8') + + pymod3 = Path(sdir, 'CVS', 'pymod3.py') + pymod3.parent.mkdir() + pymod3.write_text(f'_({text3!r})', encoding='utf-8') + + assert_python_ok('-Xutf8', self.script, sdir) + data = Path('messages.pot').read_text(encoding='utf-8') self.assertIn(f'msgid "{text1}"', data) self.assertIn(f'msgid "{text2}"', data) self.assertNotIn(text3, data) + + +def update_POT_snapshots(): + for input_file in DATA_DIR.glob('*.py'): + output_file = input_file.with_suffix('.pot') + contents = input_file.read_bytes() + with temp_cwd(None): + Path(input_file.name).write_bytes(contents) + assert_python_ok('-Xutf8', Test_pygettext.script, '--docstrings', input_file.name) + output = Path('messages.pot').read_text(encoding='utf-8') + + output = normalize_POT_file(output) + output_file.write_text(output, encoding='utf-8') + + +if __name__ == '__main__': + # To regenerate POT files + if len(sys.argv) > 1 and sys.argv[1] == '--snapshot-update': + update_POT_snapshots() + sys.exit(0) + unittest.main() diff --git a/Makefile.pre.in b/Makefile.pre.in index b0263f9f4c21da..1a9191ec0ce48f 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -2545,6 +2545,7 @@ TESTSUBDIRS= idlelib/idle_test \ test/test_tomllib/data/valid/dates-and-times \ test/test_tomllib/data/valid/multiline-basic-str \ test/test_tools \ + test/test_tools/i18n_data \ test/test_ttk \ test/test_unittest \ test/test_unittest/namespace_test_pkg \ From 19d935843727fff93b2c0e09a63fd32c0d97098d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sun, 3 Nov 2024 16:08:34 +0100 Subject: [PATCH 036/219] gh-126313: Fix a crash in curses.napms() due to incorrect error handling (GH-126351) --- .../Library/2024-11-03-09-42-42.gh-issue-126313.EFP6Dl.rst | 2 ++ Modules/_cursesmodule.c | 7 +++++-- 2 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-11-03-09-42-42.gh-issue-126313.EFP6Dl.rst diff --git a/Misc/NEWS.d/next/Library/2024-11-03-09-42-42.gh-issue-126313.EFP6Dl.rst b/Misc/NEWS.d/next/Library/2024-11-03-09-42-42.gh-issue-126313.EFP6Dl.rst new file mode 100644 index 00000000000000..dad348d8898f13 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-11-03-09-42-42.gh-issue-126313.EFP6Dl.rst @@ -0,0 +1,2 @@ +Fix an issue in :func:`curses.napms` when :func:`curses.initscr` has not yet +been called. Patch by Bénédikt Tran. diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 27d5df08de933e..040ffa81153ebe 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -3815,8 +3815,11 @@ static int _curses_napms_impl(PyObject *module, int ms) /*[clinic end generated code: output=5f292a6a724491bd input=c6d6e01f2f1df9f7]*/ { - PyCursesStatefulInitialised(module); - + if (!_PyCursesStatefulCheckFunction(module, + curses_initscr_called, + "initscr")) { + return -1; + } return napms(ms); } From ac556a2ad1213b8bb81372fe6fb762f5fcb076de Mon Sep 17 00:00:00 2001 From: "Tomas R." Date: Sun, 3 Nov 2024 17:54:42 +0100 Subject: [PATCH 037/219] gh-126357: Remove gettext import guards (GH-126358) --- Lib/getopt.py | 7 ++----- Lib/optparse.py | 15 ++------------- 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/Lib/getopt.py b/Lib/getopt.py index e5fd04fe12a7ee..1df5b96472a45c 100644 --- a/Lib/getopt.py +++ b/Lib/getopt.py @@ -34,11 +34,8 @@ __all__ = ["GetoptError","error","getopt","gnu_getopt"] import os -try: - from gettext import gettext as _ -except ImportError: - # Bootstrapping Python: gettext's dependencies not built yet - def _(s): return s +from gettext import gettext as _ + class GetoptError(Exception): opt = '' diff --git a/Lib/optparse.py b/Lib/optparse.py index 1c450c6fcbe3b6..04112eca37c801 100644 --- a/Lib/optparse.py +++ b/Lib/optparse.py @@ -75,6 +75,8 @@ import sys, os import textwrap +from gettext import gettext as _, ngettext + def _repr(self): return "<%s at 0x%x: %s>" % (self.__class__.__name__, id(self), self) @@ -86,19 +88,6 @@ def _repr(self): # Id: help.py 527 2006-07-23 15:21:30Z greg # Id: errors.py 509 2006-04-20 00:58:24Z gward -try: - from gettext import gettext, ngettext -except ImportError: - def gettext(message): - return message - - def ngettext(singular, plural, n): - if n == 1: - return singular - return plural - -_ = gettext - class OptParseError (Exception): def __init__(self, msg): From 9441993f272f42e4a97d90616ec629a11c06aa3a Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Sun, 3 Nov 2024 23:22:49 +0200 Subject: [PATCH 038/219] Docs: Delist sqlite3 deprecation from "Pending removal in 3.14" (#126370) --- Doc/deprecations/pending-removal-in-3.14.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/Doc/deprecations/pending-removal-in-3.14.rst b/Doc/deprecations/pending-removal-in-3.14.rst index 23c9bde5c01ba7..0863853339b8b5 100644 --- a/Doc/deprecations/pending-removal-in-3.14.rst +++ b/Doc/deprecations/pending-removal-in-3.14.rst @@ -103,9 +103,6 @@ Pending removal in Python 3.14 if :ref:`named placeholders ` are used and *parameters* is a sequence instead of a :class:`dict`. - * date and datetime adapter, date and timestamp converter: - see the :mod:`sqlite3` documentation for suggested replacement recipes. - * :class:`types.CodeType`: Accessing :attr:`~codeobject.co_lnotab` was deprecated in :pep:`626` since 3.10 and was planned to be removed in 3.12, From 081706f873b7d1a10b27016a9ed350b20c719709 Mon Sep 17 00:00:00 2001 From: Zhikang Yan <2951256653@qq.com> Date: Mon, 4 Nov 2024 12:08:15 +0800 Subject: [PATCH 039/219] gh-126165: Improve docs of function `math.isclose` (#126215) Co-authored-by: Sergey B Kirpichev Co-authored-by: Carol Willing Co-authored-by: Terry Jan Reedy --- Doc/library/cmath.rst | 18 ++++++++++-------- Doc/library/math.rst | 18 ++++++++++-------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/Doc/library/cmath.rst b/Doc/library/cmath.rst index 381a8332f4b187..f122e3644ece56 100644 --- a/Doc/library/cmath.rst +++ b/Doc/library/cmath.rst @@ -221,19 +221,21 @@ Classification functions ``False`` otherwise. Whether or not two values are considered close is determined according to - given absolute and relative tolerances. + given absolute and relative tolerances. If no errors occur, the result will + be: ``abs(a-b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)``. *rel_tol* is the relative tolerance -- it is the maximum allowed difference between *a* and *b*, relative to the larger absolute value of *a* or *b*. For example, to set a tolerance of 5%, pass ``rel_tol=0.05``. The default tolerance is ``1e-09``, which assures that the two values are the same - within about 9 decimal digits. *rel_tol* must be greater than zero. - - *abs_tol* is the minimum absolute tolerance -- useful for comparisons near - zero. *abs_tol* must be at least zero. - - If no errors occur, the result will be: - ``abs(a-b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)``. + within about 9 decimal digits. *rel_tol* must be nonnegative and less + than ``1.0``. + + *abs_tol* is the absolute tolerance; it defaults to ``0.0`` and it must be + nonnegative. When comparing ``x`` to ``0.0``, ``isclose(x, 0)`` is computed + as ``abs(x) <= rel_tol * abs(x)``, which is ``False`` for any ``x`` and + rel_tol less than ``1.0``. So add an appropriate positive abs_tol argument + to the call. The IEEE 754 special values of ``NaN``, ``inf``, and ``-inf`` will be handled according to IEEE rules. Specifically, ``NaN`` is not considered diff --git a/Doc/library/math.rst b/Doc/library/math.rst index 6be61c99274eb7..2ecee89a7db165 100644 --- a/Doc/library/math.rst +++ b/Doc/library/math.rst @@ -244,19 +244,21 @@ Number-theoretic and representation functions ``False`` otherwise. Whether or not two values are considered close is determined according to - given absolute and relative tolerances. + given absolute and relative tolerances. If no errors occur, the result will + be: ``abs(a-b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)``. *rel_tol* is the relative tolerance -- it is the maximum allowed difference between *a* and *b*, relative to the larger absolute value of *a* or *b*. For example, to set a tolerance of 5%, pass ``rel_tol=0.05``. The default tolerance is ``1e-09``, which assures that the two values are the same - within about 9 decimal digits. *rel_tol* must be greater than zero. - - *abs_tol* is the minimum absolute tolerance -- useful for comparisons near - zero. *abs_tol* must be at least zero. - - If no errors occur, the result will be: - ``abs(a-b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)``. + within about 9 decimal digits. *rel_tol* must be nonnegative and less + than ``1.0``. + + *abs_tol* is the absolute tolerance; it defaults to ``0.0`` and it must be + nonnegative. When comparing ``x`` to ``0.0``, ``isclose(x, 0)`` is computed + as ``abs(x) <= rel_tol * abs(x)``, which is ``False`` for any ``x`` and + rel_tol less than ``1.0``. So add an appropriate positive abs_tol argument + to the call. The IEEE 754 special values of ``NaN``, ``inf``, and ``-inf`` will be handled according to IEEE rules. Specifically, ``NaN`` is not considered From 0d80777981f95bbc79b146fc78b2189c82521ab9 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Mon, 4 Nov 2024 09:27:25 +0100 Subject: [PATCH 040/219] Docs: turn getopt examples into doctests (#126377) --- Doc/library/getopt.rst | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Doc/library/getopt.rst b/Doc/library/getopt.rst index d43d3250732306..3ab44b9fc56108 100644 --- a/Doc/library/getopt.rst +++ b/Doc/library/getopt.rst @@ -97,6 +97,8 @@ exception: An example using only Unix style options: +.. doctest:: + >>> import getopt >>> args = '-a -b -cfoo -d bar a1 a2'.split() >>> args @@ -109,6 +111,8 @@ An example using only Unix style options: Using long option names is equally easy: +.. doctest:: + >>> s = '--condition=foo --testing --output-file abc.def -x a1 a2' >>> args = s.split() >>> args @@ -120,7 +124,9 @@ Using long option names is equally easy: >>> args ['a1', 'a2'] -In a script, typical usage is something like this:: +In a script, typical usage is something like this: + +.. testcode:: import getopt, sys @@ -150,7 +156,9 @@ In a script, typical usage is something like this:: main() Note that an equivalent command line interface could be produced with less code -and more informative help and error messages by using the :mod:`argparse` module:: +and more informative help and error messages by using the :mod:`argparse` module: + +.. testcode:: import argparse From fe5a6ab7bea927e887b7b3c2d4e8fe8eac7106c3 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Mon, 4 Nov 2024 14:21:20 +0530 Subject: [PATCH 041/219] gh-126353: remove implicit creation of loop from `asyncio.get_event_loop` (#126354) Remove implicit creation of loop from `asyncio.get_event_loop`. This is a step forward of deprecating the policy system of asyncio. --- Doc/library/asyncio-eventloop.rst | 5 ++- Doc/library/asyncio-policy.rst | 8 ++--- Doc/whatsnew/3.14.rst | 5 +++ Lib/asyncio/events.py | 24 ------------- Lib/test/test_asyncio/test_events.py | 34 +++++-------------- Lib/test/test_asyncio/test_unix_events.py | 6 ++-- Lib/test/test_cmd_line.py | 2 +- ...-11-03-10-48-07.gh-issue-126353.ChDzot.rst | 2 ++ 8 files changed, 24 insertions(+), 62 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-11-03-10-48-07.gh-issue-126353.ChDzot.rst diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst index 14fd153f640f05..3ace6eda4d7f29 100644 --- a/Doc/library/asyncio-eventloop.rst +++ b/Doc/library/asyncio-eventloop.rst @@ -59,9 +59,8 @@ an event loop: instead of using these lower level functions to manually create and close an event loop. - .. deprecated:: 3.12 - Deprecation warning is emitted if there is no current event loop. - In some future Python release this will become an error. + .. versionchanged:: 3.14 + Raises a :exc:`RuntimeError` if there is no current event loop. .. function:: set_event_loop(loop) diff --git a/Doc/library/asyncio-policy.rst b/Doc/library/asyncio-policy.rst index 837ccc6606786e..09b75762ff0272 100644 --- a/Doc/library/asyncio-policy.rst +++ b/Doc/library/asyncio-policy.rst @@ -97,11 +97,9 @@ asyncio ships with the following built-in policies: On Windows, :class:`ProactorEventLoop` is now used by default. - .. deprecated:: 3.12 - The :meth:`get_event_loop` method of the default asyncio policy now emits - a :exc:`DeprecationWarning` if there is no current event loop set and it - decides to create one. - In some future Python release this will become an error. + .. versionchanged:: 3.14 + The :meth:`get_event_loop` method of the default asyncio policy now + raises a :exc:`RuntimeError` if there is no set event loop. .. class:: WindowsSelectorEventLoopPolicy diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 21bc289c2be5d8..deee683d7b87b1 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -576,6 +576,11 @@ asyncio (Contributed by Kumar Aditya in :gh:`120804`.) +* Removed implicit creation of event loop by :func:`asyncio.get_event_loop`. + It now raises a :exc:`RuntimeError` if there is no current event loop. + (Contributed by Kumar Aditya in :gh:`126353`.) + + collections.abc --------------- diff --git a/Lib/asyncio/events.py b/Lib/asyncio/events.py index b63fe6aa79604b..ca0a4f2fee5840 100644 --- a/Lib/asyncio/events.py +++ b/Lib/asyncio/events.py @@ -668,7 +668,6 @@ class BaseDefaultEventLoopPolicy(AbstractEventLoopPolicy): class _Local(threading.local): _loop = None - _set_called = False def __init__(self): self._local = self._Local() @@ -678,28 +677,6 @@ def get_event_loop(self): Returns an instance of EventLoop or raises an exception. """ - if (self._local._loop is None and - not self._local._set_called and - threading.current_thread() is threading.main_thread()): - stacklevel = 2 - try: - f = sys._getframe(1) - except AttributeError: - pass - else: - # Move up the call stack so that the warning is attached - # to the line outside asyncio itself. - while f: - module = f.f_globals.get('__name__') - if not (module == 'asyncio' or module.startswith('asyncio.')): - break - f = f.f_back - stacklevel += 1 - import warnings - warnings.warn('There is no current event loop', - DeprecationWarning, stacklevel=stacklevel) - self.set_event_loop(self.new_event_loop()) - if self._local._loop is None: raise RuntimeError('There is no current event loop in thread %r.' % threading.current_thread().name) @@ -708,7 +685,6 @@ def get_event_loop(self): def set_event_loop(self, loop): """Set the event loop.""" - self._local._set_called = True if loop is not None and not isinstance(loop, AbstractEventLoop): raise TypeError(f"loop must be an instance of AbstractEventLoop or None, not '{type(loop).__name__}'") self._local._loop = loop diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py index 4dcf9f0e4037b6..2ab638dc527aec 100644 --- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py @@ -2704,33 +2704,22 @@ def test_event_loop_policy(self): def test_get_event_loop(self): policy = asyncio.DefaultEventLoopPolicy() self.assertIsNone(policy._local._loop) - with self.assertWarns(DeprecationWarning) as cm: - loop = policy.get_event_loop() - self.assertEqual(cm.filename, __file__) - self.assertIsInstance(loop, asyncio.AbstractEventLoop) - self.assertIs(policy._local._loop, loop) - self.assertIs(loop, policy.get_event_loop()) - loop.close() + with self.assertRaises(RuntimeError): + loop = policy.get_event_loop() + self.assertIsNone(policy._local._loop) - def test_get_event_loop_calls_set_event_loop(self): + def test_get_event_loop_does_not_call_set_event_loop(self): policy = asyncio.DefaultEventLoopPolicy() with mock.patch.object( policy, "set_event_loop", wraps=policy.set_event_loop) as m_set_event_loop: - with self.assertWarns(DeprecationWarning) as cm: + with self.assertRaises(RuntimeError): loop = policy.get_event_loop() - self.addCleanup(loop.close) - self.assertEqual(cm.filename, __file__) - # policy._local._loop must be set through .set_event_loop() - # (the unix DefaultEventLoopPolicy needs this call to attach - # the child watcher correctly) - m_set_event_loop.assert_called_with(loop) - - loop.close() + m_set_event_loop.assert_not_called() def test_get_event_loop_after_set_none(self): policy = asyncio.DefaultEventLoopPolicy() @@ -2912,17 +2901,12 @@ def test_get_event_loop_returns_running_loop2(self): loop = asyncio.new_event_loop() self.addCleanup(loop.close) - with self.assertWarns(DeprecationWarning) as cm: - loop2 = asyncio.get_event_loop() - self.addCleanup(loop2.close) - self.assertEqual(cm.filename, __file__) - asyncio.set_event_loop(None) with self.assertRaisesRegex(RuntimeError, 'no current'): asyncio.get_event_loop() - with self.assertRaisesRegex(RuntimeError, 'no running'): - asyncio.get_running_loop() - self.assertIs(asyncio._get_running_loop(), None) + asyncio.set_event_loop(None) + with self.assertRaisesRegex(RuntimeError, 'no current'): + asyncio.get_event_loop() async def func(): self.assertIs(asyncio.get_event_loop(), loop) diff --git a/Lib/test/test_asyncio/test_unix_events.py b/Lib/test/test_asyncio/test_unix_events.py index 9ae54b6887010b..021f45478d6f48 100644 --- a/Lib/test/test_asyncio/test_unix_events.py +++ b/Lib/test/test_asyncio/test_unix_events.py @@ -1195,8 +1195,7 @@ async def test_fork_not_share_event_loop(self): if pid == 0: # child try: - with self.assertWarns(DeprecationWarning): - loop = asyncio.get_event_loop_policy().get_event_loop() + loop = asyncio.get_event_loop() os.write(w, b'LOOP:' + str(id(loop)).encode()) except RuntimeError: os.write(w, b'NO LOOP') @@ -1207,8 +1206,7 @@ async def test_fork_not_share_event_loop(self): else: # parent result = os.read(r, 100) - self.assertEqual(result[:5], b'LOOP:', result) - self.assertNotEqual(int(result[5:]), id(loop)) + self.assertEqual(result, b'NO LOOP') wait_process(pid, exitcode=0) @hashlib_helper.requires_hashdigest('md5') diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py index 35725718152c56..eca9adf9a7dcbc 100644 --- a/Lib/test/test_cmd_line.py +++ b/Lib/test/test_cmd_line.py @@ -924,7 +924,7 @@ def test_python_gil(self): self.assertEqual(proc.stderr, '') def test_python_asyncio_debug(self): - code = "import asyncio; print(asyncio.get_event_loop().get_debug())" + code = "import asyncio; print(asyncio.new_event_loop().get_debug())" rc, out, err = assert_python_ok('-c', code, PYTHONASYNCIODEBUG='1') self.assertIn(b'True', out) diff --git a/Misc/NEWS.d/next/Library/2024-11-03-10-48-07.gh-issue-126353.ChDzot.rst b/Misc/NEWS.d/next/Library/2024-11-03-10-48-07.gh-issue-126353.ChDzot.rst new file mode 100644 index 00000000000000..16d508b7ec6a20 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-11-03-10-48-07.gh-issue-126353.ChDzot.rst @@ -0,0 +1,2 @@ +:func:`asyncio.get_event_loop` now does not implicitly creates an event loop. +It now raises a :exc:`RuntimeError` if there is no set event loop. Patch by Kumar Aditya. From bfc1d2504c183a9464e65c290e48516d176ea41f Mon Sep 17 00:00:00 2001 From: sobolevn Date: Mon, 4 Nov 2024 13:15:57 +0300 Subject: [PATCH 042/219] gh-109413: Add more type hints to `libregrtest` (#126352) --- Lib/test/libregrtest/findtests.py | 14 ++++++----- Lib/test/libregrtest/main.py | 29 +++++++++++----------- Lib/test/libregrtest/pgo.py | 2 +- Lib/test/libregrtest/refleak.py | 2 +- Lib/test/libregrtest/result.py | 1 + Lib/test/libregrtest/results.py | 14 +++++------ Lib/test/libregrtest/runtests.py | 16 ++++++------ Lib/test/libregrtest/setup.py | 9 ++++--- Lib/test/libregrtest/tsan.py | 2 +- Lib/test/libregrtest/utils.py | 41 +++++++++++++++++-------------- Lib/test/libregrtest/worker.py | 2 +- Tools/requirements-dev.txt | 2 +- 12 files changed, 71 insertions(+), 63 deletions(-) diff --git a/Lib/test/libregrtest/findtests.py b/Lib/test/libregrtest/findtests.py index 4ac95e23a56b8f..f01c1240774707 100644 --- a/Lib/test/libregrtest/findtests.py +++ b/Lib/test/libregrtest/findtests.py @@ -1,6 +1,7 @@ import os import sys import unittest +from collections.abc import Container from test import support @@ -34,7 +35,7 @@ def findtestdir(path: StrPath | None = None) -> StrPath: return path or os.path.dirname(os.path.dirname(__file__)) or os.curdir -def findtests(*, testdir: StrPath | None = None, exclude=(), +def findtests(*, testdir: StrPath | None = None, exclude: Container[str] = (), split_test_dirs: set[TestName] = SPLITTESTDIRS, base_mod: str = "") -> TestList: """Return a list of all applicable test modules.""" @@ -60,8 +61,9 @@ def findtests(*, testdir: StrPath | None = None, exclude=(), return sorted(tests) -def split_test_packages(tests, *, testdir: StrPath | None = None, exclude=(), - split_test_dirs=SPLITTESTDIRS): +def split_test_packages(tests, *, testdir: StrPath | None = None, + exclude: Container[str] = (), + split_test_dirs=SPLITTESTDIRS) -> list[TestName]: testdir = findtestdir(testdir) splitted = [] for name in tests: @@ -75,9 +77,9 @@ def split_test_packages(tests, *, testdir: StrPath | None = None, exclude=(), return splitted -def _list_cases(suite): +def _list_cases(suite: unittest.TestSuite) -> None: for test in suite: - if isinstance(test, unittest.loader._FailedTest): + if isinstance(test, unittest.loader._FailedTest): # type: ignore[attr-defined] continue if isinstance(test, unittest.TestSuite): _list_cases(test) @@ -87,7 +89,7 @@ def _list_cases(suite): def list_cases(tests: TestTuple, *, match_tests: TestFilter | None = None, - test_dir: StrPath | None = None): + test_dir: StrPath | None = None) -> None: support.verbose = False set_match_tests(match_tests) diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index 11ebb09fbb4745..133eba8ffe8e69 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -6,6 +6,7 @@ import sysconfig import time import trace +from typing import NoReturn from test.support import os_helper, MS_WINDOWS, flush_std_streams @@ -154,7 +155,7 @@ def __init__(self, ns: Namespace, _add_python_opts: bool = False): self.next_single_test: TestName | None = None self.next_single_filename: StrPath | None = None - def log(self, line=''): + def log(self, line: str = '') -> None: self.logger.log(line) def find_tests(self, tests: TestList | None = None) -> tuple[TestTuple, TestList | None]: @@ -230,11 +231,11 @@ def find_tests(self, tests: TestList | None = None) -> tuple[TestTuple, TestList return (tuple(selected), tests) @staticmethod - def list_tests(tests: TestTuple): + def list_tests(tests: TestTuple) -> None: for name in tests: print(name) - def _rerun_failed_tests(self, runtests: RunTests): + def _rerun_failed_tests(self, runtests: RunTests) -> RunTests: # Configure the runner to re-run tests if self.num_workers == 0 and not self.single_process: # Always run tests in fresh processes to have more deterministic @@ -266,7 +267,7 @@ def _rerun_failed_tests(self, runtests: RunTests): self.run_tests_sequentially(runtests) return runtests - def rerun_failed_tests(self, runtests: RunTests): + def rerun_failed_tests(self, runtests: RunTests) -> None: if self.python_cmd: # Temp patch for https://github.com/python/cpython/issues/94052 self.log( @@ -335,7 +336,7 @@ def run_bisect(self, runtests: RunTests) -> None: if not self._run_bisect(runtests, name, progress): return - def display_result(self, runtests): + def display_result(self, runtests: RunTests) -> None: # If running the test suite for PGO then no one cares about results. if runtests.pgo: return @@ -365,7 +366,7 @@ def run_test( return result - def run_tests_sequentially(self, runtests) -> None: + def run_tests_sequentially(self, runtests: RunTests) -> None: if self.coverage: tracer = trace.Trace(trace=False, count=True) else: @@ -422,7 +423,7 @@ def run_tests_sequentially(self, runtests) -> None: if previous_test: print(previous_test) - def get_state(self): + def get_state(self) -> str: state = self.results.get_state(self.fail_env_changed) if self.first_state: state = f'{self.first_state} then {state}' @@ -452,7 +453,7 @@ def finalize_tests(self, coverage: trace.CoverageResults | None) -> None: if self.junit_filename: self.results.write_junit(self.junit_filename) - def display_summary(self): + def display_summary(self) -> None: duration = time.perf_counter() - self.logger.start_time filtered = bool(self.match_tests) @@ -466,7 +467,7 @@ def display_summary(self): state = self.get_state() print(f"Result: {state}") - def create_run_tests(self, tests: TestTuple): + def create_run_tests(self, tests: TestTuple) -> RunTests: return RunTests( tests, fail_fast=self.fail_fast, @@ -674,9 +675,9 @@ def _execute_python(self, cmd, environ): f"Command: {cmd_text}") # continue executing main() - def _add_python_opts(self): - python_opts = [] - regrtest_opts = [] + def _add_python_opts(self) -> None: + python_opts: list[str] = [] + regrtest_opts: list[str] = [] environ, keep_environ = self._add_cross_compile_opts(regrtest_opts) if self.ci_mode: @@ -709,7 +710,7 @@ def _init(self): self.tmp_dir = get_temp_dir(self.tmp_dir) - def main(self, tests: TestList | None = None): + def main(self, tests: TestList | None = None) -> NoReturn: if self.want_add_python_opts: self._add_python_opts() @@ -738,7 +739,7 @@ def main(self, tests: TestList | None = None): sys.exit(exitcode) -def main(tests=None, _add_python_opts=False, **kwargs): +def main(tests=None, _add_python_opts=False, **kwargs) -> NoReturn: """Run the Python suite.""" ns = _parse_args(sys.argv[1:], **kwargs) Regrtest(ns, _add_python_opts=_add_python_opts).main(tests=tests) diff --git a/Lib/test/libregrtest/pgo.py b/Lib/test/libregrtest/pgo.py index e3a6927be5db1d..f762345c88cde3 100644 --- a/Lib/test/libregrtest/pgo.py +++ b/Lib/test/libregrtest/pgo.py @@ -50,7 +50,7 @@ 'test_xml_etree_c', ] -def setup_pgo_tests(cmdline_args, pgo_extended: bool): +def setup_pgo_tests(cmdline_args, pgo_extended: bool) -> None: if not cmdline_args and not pgo_extended: # run default set of tests for PGO training cmdline_args[:] = PGO_TESTS[:] diff --git a/Lib/test/libregrtest/refleak.py b/Lib/test/libregrtest/refleak.py index fa447a4336a399..e783475cc7a36b 100644 --- a/Lib/test/libregrtest/refleak.py +++ b/Lib/test/libregrtest/refleak.py @@ -262,7 +262,7 @@ def dash_R_cleanup(fs, ps, pic, zdc, abcs): sys._clear_internal_caches() -def warm_caches(): +def warm_caches() -> None: # char cache s = bytes(range(256)) for i in range(256): diff --git a/Lib/test/libregrtest/result.py b/Lib/test/libregrtest/result.py index 74eae40440435d..7553efe5e8abeb 100644 --- a/Lib/test/libregrtest/result.py +++ b/Lib/test/libregrtest/result.py @@ -149,6 +149,7 @@ def __str__(self) -> str: case State.DID_NOT_RUN: return f"{self.test_name} ran no tests" case State.TIMEOUT: + assert self.duration is not None, "self.duration is None" return f"{self.test_name} timed out ({format_duration(self.duration)})" case _: raise ValueError("unknown result state: {state!r}") diff --git a/Lib/test/libregrtest/results.py b/Lib/test/libregrtest/results.py index 0e28435bc7d629..4f3e84282dc5dc 100644 --- a/Lib/test/libregrtest/results.py +++ b/Lib/test/libregrtest/results.py @@ -71,7 +71,7 @@ def get_state(self, fail_env_changed: bool) -> str: return ', '.join(state) - def get_exitcode(self, fail_env_changed, fail_rerun): + def get_exitcode(self, fail_env_changed: bool, fail_rerun: bool) -> int: exitcode = 0 if self.bad: exitcode = EXITCODE_BAD_TEST @@ -87,7 +87,7 @@ def get_exitcode(self, fail_env_changed, fail_rerun): exitcode = EXITCODE_BAD_TEST return exitcode - def accumulate_result(self, result: TestResult, runtests: RunTests): + def accumulate_result(self, result: TestResult, runtests: RunTests) -> None: test_name = result.test_name rerun = runtests.rerun fail_env_changed = runtests.fail_env_changed @@ -135,7 +135,7 @@ def get_coverage_results(self) -> trace.CoverageResults: counts = {loc: 1 for loc in self.covered_lines} return trace.CoverageResults(counts=counts) - def need_rerun(self): + def need_rerun(self) -> bool: return bool(self.rerun_results) def prepare_rerun(self, *, clear: bool = True) -> tuple[TestTuple, FilterDict]: @@ -158,7 +158,7 @@ def prepare_rerun(self, *, clear: bool = True) -> tuple[TestTuple, FilterDict]: return (tuple(tests), match_tests_dict) - def add_junit(self, xml_data: list[str]): + def add_junit(self, xml_data: list[str]) -> None: import xml.etree.ElementTree as ET for e in xml_data: try: @@ -167,7 +167,7 @@ def add_junit(self, xml_data: list[str]): print(xml_data, file=sys.__stderr__) raise - def write_junit(self, filename: StrPath): + def write_junit(self, filename: StrPath) -> None: if not self.testsuite_xml: # Don't create empty XML file return @@ -192,7 +192,7 @@ def write_junit(self, filename: StrPath): for s in ET.tostringlist(root): f.write(s) - def display_result(self, tests: TestTuple, quiet: bool, print_slowest: bool): + def display_result(self, tests: TestTuple, quiet: bool, print_slowest: bool) -> None: if print_slowest: self.test_times.sort(reverse=True) print() @@ -234,7 +234,7 @@ def display_result(self, tests: TestTuple, quiet: bool, print_slowest: bool): print() print("Test suite interrupted by signal SIGINT.") - def display_summary(self, first_runtests: RunTests, filtered: bool): + def display_summary(self, first_runtests: RunTests, filtered: bool) -> None: # Total tests stats = self.stats text = f'run={stats.tests_run:,}' diff --git a/Lib/test/libregrtest/runtests.py b/Lib/test/libregrtest/runtests.py index 8e9779524c56a2..cd1ce8080a04df 100644 --- a/Lib/test/libregrtest/runtests.py +++ b/Lib/test/libregrtest/runtests.py @@ -5,12 +5,12 @@ import shlex import subprocess import sys -from typing import Any +from typing import Any, Iterator from test import support from .utils import ( - StrPath, StrJSON, TestTuple, TestFilter, FilterTuple, FilterDict) + StrPath, StrJSON, TestTuple, TestName, TestFilter, FilterTuple, FilterDict) class JsonFileType: @@ -41,8 +41,8 @@ def configure_subprocess(self, popen_kwargs: dict) -> None: popen_kwargs['startupinfo'] = startupinfo @contextlib.contextmanager - def inherit_subprocess(self): - if self.file_type == JsonFileType.WINDOWS_HANDLE: + def inherit_subprocess(self) -> Iterator[None]: + if sys.platform == 'win32' and self.file_type == JsonFileType.WINDOWS_HANDLE: os.set_handle_inheritable(self.file, True) try: yield @@ -106,25 +106,25 @@ def copy(self, **override) -> 'RunTests': state.update(override) return RunTests(**state) - def create_worker_runtests(self, **override): + def create_worker_runtests(self, **override) -> WorkerRunTests: state = dataclasses.asdict(self) state.update(override) return WorkerRunTests(**state) - def get_match_tests(self, test_name) -> FilterTuple | None: + def get_match_tests(self, test_name: TestName) -> FilterTuple | None: if self.match_tests_dict is not None: return self.match_tests_dict.get(test_name, None) else: return None - def get_jobs(self): + def get_jobs(self) -> int | None: # Number of run_single_test() calls needed to run all tests. # None means that there is not bound limit (--forever option). if self.forever: return None return len(self.tests) - def iter_tests(self): + def iter_tests(self) -> Iterator[TestName]: if self.forever: while True: yield from self.tests diff --git a/Lib/test/libregrtest/setup.py b/Lib/test/libregrtest/setup.py index 9e9741493e9a5b..ba57f06b4841d4 100644 --- a/Lib/test/libregrtest/setup.py +++ b/Lib/test/libregrtest/setup.py @@ -25,9 +25,10 @@ def setup_test_dir(testdir: str | None) -> None: sys.path.insert(0, os.path.abspath(testdir)) -def setup_process(): +def setup_process() -> None: fix_umask() + assert sys.__stderr__ is not None, "sys.__stderr__ is None" try: stderr_fd = sys.__stderr__.fileno() except (ValueError, AttributeError): @@ -35,7 +36,7 @@ def setup_process(): # and ValueError on a closed stream. # # Catch AttributeError for stderr being None. - stderr_fd = None + pass else: # Display the Python traceback on fatal errors (e.g. segfault) faulthandler.enable(all_threads=True, file=stderr_fd) @@ -68,7 +69,7 @@ def setup_process(): for index, path in enumerate(module.__path__): module.__path__[index] = os.path.abspath(path) if getattr(module, '__file__', None): - module.__file__ = os.path.abspath(module.__file__) + module.__file__ = os.path.abspath(module.__file__) # type: ignore[type-var] if hasattr(sys, 'addaudithook'): # Add an auditing hook for all tests to ensure PySys_Audit is tested @@ -87,7 +88,7 @@ def _test_audit_hook(name, args): os.environ.setdefault(UNICODE_GUARD_ENV, FS_NONASCII) -def setup_tests(runtests: RunTests): +def setup_tests(runtests: RunTests) -> None: support.verbose = runtests.verbose support.failfast = runtests.fail_fast support.PGO = runtests.pgo diff --git a/Lib/test/libregrtest/tsan.py b/Lib/test/libregrtest/tsan.py index dd18ae2584f5d8..822ac0f4044d9e 100644 --- a/Lib/test/libregrtest/tsan.py +++ b/Lib/test/libregrtest/tsan.py @@ -28,6 +28,6 @@ ] -def setup_tsan_tests(cmdline_args): +def setup_tsan_tests(cmdline_args) -> None: if not cmdline_args: cmdline_args[:] = TSAN_TESTS[:] diff --git a/Lib/test/libregrtest/utils.py b/Lib/test/libregrtest/utils.py index d6be4ad049d14a..1ecfc61af9e39d 100644 --- a/Lib/test/libregrtest/utils.py +++ b/Lib/test/libregrtest/utils.py @@ -58,7 +58,7 @@ FilterDict = dict[TestName, FilterTuple] -def format_duration(seconds): +def format_duration(seconds: float) -> str: ms = math.ceil(seconds * 1e3) seconds, ms = divmod(ms, 1000) minutes, seconds = divmod(seconds, 60) @@ -92,7 +92,7 @@ def strip_py_suffix(names: list[str] | None) -> None: names[idx] = basename -def plural(n, singular, plural=None): +def plural(n: int, singular: str, plural: str | None = None) -> str: if n == 1: return singular elif plural is not None: @@ -101,7 +101,7 @@ def plural(n, singular, plural=None): return singular + 's' -def count(n, word): +def count(n: int, word: str) -> str: if n == 1: return f"{n} {word}" else: @@ -123,14 +123,14 @@ def printlist(x, width=70, indent=4, file=None): file=file) -def print_warning(msg): +def print_warning(msg: str) -> None: support.print_warning(msg) -orig_unraisablehook = None +orig_unraisablehook: Callable[..., None] | None = None -def regrtest_unraisable_hook(unraisable): +def regrtest_unraisable_hook(unraisable) -> None: global orig_unraisablehook support.environment_altered = True support.print_warning("Unraisable exception") @@ -138,22 +138,23 @@ def regrtest_unraisable_hook(unraisable): try: support.flush_std_streams() sys.stderr = support.print_warning.orig_stderr + assert orig_unraisablehook is not None, "orig_unraisablehook not set" orig_unraisablehook(unraisable) sys.stderr.flush() finally: sys.stderr = old_stderr -def setup_unraisable_hook(): +def setup_unraisable_hook() -> None: global orig_unraisablehook orig_unraisablehook = sys.unraisablehook sys.unraisablehook = regrtest_unraisable_hook -orig_threading_excepthook = None +orig_threading_excepthook: Callable[..., None] | None = None -def regrtest_threading_excepthook(args): +def regrtest_threading_excepthook(args) -> None: global orig_threading_excepthook support.environment_altered = True support.print_warning(f"Uncaught thread exception: {args.exc_type.__name__}") @@ -161,13 +162,14 @@ def regrtest_threading_excepthook(args): try: support.flush_std_streams() sys.stderr = support.print_warning.orig_stderr + assert orig_threading_excepthook is not None, "orig_threading_excepthook not set" orig_threading_excepthook(args) sys.stderr.flush() finally: sys.stderr = old_stderr -def setup_threading_excepthook(): +def setup_threading_excepthook() -> None: global orig_threading_excepthook import threading orig_threading_excepthook = threading.excepthook @@ -476,7 +478,7 @@ def get_temp_dir(tmp_dir: StrPath | None = None) -> StrPath: return os.path.abspath(tmp_dir) -def fix_umask(): +def fix_umask() -> None: if support.is_emscripten: # Emscripten has default umask 0o777, which breaks some tests. # see https://github.com/emscripten-core/emscripten/issues/17269 @@ -572,7 +574,8 @@ def abs_module_name(test_name: TestName, test_dir: StrPath | None) -> TestName: 'setUpModule', 'tearDownModule', )) -def normalize_test_name(test_full_name, *, is_error=False): +def normalize_test_name(test_full_name: str, *, + is_error: bool = False) -> str | None: short_name = test_full_name.split(" ")[0] if is_error and short_name in _TEST_LIFECYCLE_HOOKS: if test_full_name.startswith(('setUpModule (', 'tearDownModule (')): @@ -593,7 +596,7 @@ def normalize_test_name(test_full_name, *, is_error=False): return short_name -def adjust_rlimit_nofile(): +def adjust_rlimit_nofile() -> None: """ On macOS the default fd limit (RLIMIT_NOFILE) is sometimes too low (256) for our test suite to succeed. Raise it to something more reasonable. 1024 @@ -619,17 +622,17 @@ def adjust_rlimit_nofile(): f"{new_fd_limit}: {err}.") -def get_host_runner(): +def get_host_runner() -> str: if (hostrunner := os.environ.get("_PYTHON_HOSTRUNNER")) is None: hostrunner = sysconfig.get_config_var("HOSTRUNNER") return hostrunner -def is_cross_compiled(): +def is_cross_compiled() -> bool: return ('_PYTHON_HOST_PLATFORM' in os.environ) -def format_resources(use_resources: Iterable[str]): +def format_resources(use_resources: Iterable[str]) -> str: use_resources = set(use_resources) all_resources = set(ALL_RESOURCES) @@ -654,7 +657,7 @@ def format_resources(use_resources: Iterable[str]): def display_header(use_resources: tuple[str, ...], - python_cmd: tuple[str, ...] | None): + python_cmd: tuple[str, ...] | None) -> None: # Print basic platform information print("==", platform.python_implementation(), *sys.version.split()) print("==", platform.platform(aliased=True), @@ -732,7 +735,7 @@ def display_header(use_resources: tuple[str, ...], print(flush=True) -def cleanup_temp_dir(tmp_dir: StrPath): +def cleanup_temp_dir(tmp_dir: StrPath) -> None: import glob path = os.path.join(glob.escape(tmp_dir), TMP_PREFIX + '*') @@ -763,5 +766,5 @@ def _sanitize_xml_replace(regs): return ''.join(f'\\x{ord(ch):02x}' if ch <= '\xff' else ascii(ch)[1:-1] for ch in text) -def sanitize_xml(text): +def sanitize_xml(text: str) -> str: return ILLEGAL_XML_CHARS_RE.sub(_sanitize_xml_replace, text) diff --git a/Lib/test/libregrtest/worker.py b/Lib/test/libregrtest/worker.py index 86cc30835fdbda..da24760a82c6c6 100644 --- a/Lib/test/libregrtest/worker.py +++ b/Lib/test/libregrtest/worker.py @@ -104,7 +104,7 @@ def worker_process(worker_json: StrJSON) -> NoReturn: sys.exit(0) -def main(): +def main() -> NoReturn: if len(sys.argv) != 2: print("usage: python -m test.libregrtest.worker JSON") sys.exit(1) diff --git a/Tools/requirements-dev.txt b/Tools/requirements-dev.txt index 57f0b982b00f5d..e5badaccadda40 100644 --- a/Tools/requirements-dev.txt +++ b/Tools/requirements-dev.txt @@ -1,6 +1,6 @@ # Requirements file for external linters and checks we run on # Tools/clinic, Tools/cases_generator/, and Tools/peg_generator/ in CI -mypy==1.12 +mypy==1.13 # needed for peg_generator: types-psutil==6.0.0.20240901 From 3032fcd90ecb745b737cbc93f694f9a802062a3a Mon Sep 17 00:00:00 2001 From: Nice Zombies Date: Mon, 4 Nov 2024 15:00:19 +0100 Subject: [PATCH 043/219] gh-119793: Add optional length-checking to `map()` (GH-120471) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> Co-authored-by: Pieter Eendebak Co-authored-by: Erlend E. Aasland Co-authored-by: Raymond Hettinger --- Doc/library/functions.rst | 11 +- Doc/whatsnew/3.14.rst | 4 + Lib/test/test_builtin.py | 105 ++++++++++++++++++ Lib/test/test_itertools.py | 4 +- ...-06-13-19-12-49.gh-issue-119793.FDVCDk.rst | 3 + Python/bltinmodule.c | 100 +++++++++++++++-- 6 files changed, 210 insertions(+), 17 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-06-13-19-12-49.gh-issue-119793.FDVCDk.rst diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index 03fc41fa793977..a7549b9bce76e2 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -1205,14 +1205,19 @@ are always available. They are listed here in alphabetical order. unchanged from previous versions. -.. function:: map(function, iterable, *iterables) +.. function:: map(function, iterable, /, *iterables, strict=False) Return an iterator that applies *function* to every item of *iterable*, yielding the results. If additional *iterables* arguments are passed, *function* must take that many arguments and is applied to the items from all iterables in parallel. With multiple iterables, the iterator stops when the - shortest iterable is exhausted. For cases where the function inputs are - already arranged into argument tuples, see :func:`itertools.starmap`\. + shortest iterable is exhausted. If *strict* is ``True`` and one of the + iterables is exhausted before the others, a :exc:`ValueError` is raised. For + cases where the function inputs are already arranged into argument tuples, + see :func:`itertools.starmap`. + + .. versionchanged:: 3.14 + Added the *strict* parameter. .. function:: max(iterable, *, key=None) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index deee683d7b87b1..80c1a93b95a6af 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -175,6 +175,10 @@ Improved error messages Other language changes ====================== +* The :func:`map` built-in now has an optional keyword-only *strict* flag + like :func:`zip` to check that all the iterables are of equal length. + (Contributed by Wannes Boeykens in :gh:`119793`.) + * Incorrect usage of :keyword:`await` and asynchronous comprehensions is now detected even if the code is optimized away by the :option:`-O` command-line option. For example, ``python -O -c 'assert await 1'`` diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index eb5906f8944c8e..f8e6f05cd607c8 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -148,6 +148,9 @@ def filter_char(arg): def map_char(arg): return chr(ord(arg)+1) +def pack(*args): + return args + class BuiltinTest(unittest.TestCase): # Helper to check picklability def check_iter_pickle(self, it, seq, proto): @@ -1269,6 +1272,108 @@ def test_map_pickle(self): m2 = map(map_char, "Is this the real life?") self.check_iter_pickle(m1, list(m2), proto) + # strict map tests based on strict zip tests + + def test_map_pickle_strict(self): + a = (1, 2, 3) + b = (4, 5, 6) + t = [(1, 4), (2, 5), (3, 6)] + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + m1 = map(pack, a, b, strict=True) + self.check_iter_pickle(m1, t, proto) + + def test_map_pickle_strict_fail(self): + a = (1, 2, 3) + b = (4, 5, 6, 7) + t = [(1, 4), (2, 5), (3, 6)] + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + m1 = map(pack, a, b, strict=True) + m2 = pickle.loads(pickle.dumps(m1, proto)) + self.assertEqual(self.iter_error(m1, ValueError), t) + self.assertEqual(self.iter_error(m2, ValueError), t) + + def test_map_strict(self): + self.assertEqual(tuple(map(pack, (1, 2, 3), 'abc', strict=True)), + ((1, 'a'), (2, 'b'), (3, 'c'))) + self.assertRaises(ValueError, tuple, + map(pack, (1, 2, 3, 4), 'abc', strict=True)) + self.assertRaises(ValueError, tuple, + map(pack, (1, 2), 'abc', strict=True)) + self.assertRaises(ValueError, tuple, + map(pack, (1, 2), (1, 2), 'abc', strict=True)) + + def test_map_strict_iterators(self): + x = iter(range(5)) + y = [0] + z = iter(range(5)) + self.assertRaises(ValueError, list, + (map(pack, x, y, z, strict=True))) + self.assertEqual(next(x), 2) + self.assertEqual(next(z), 1) + + def test_map_strict_error_handling(self): + + class Error(Exception): + pass + + class Iter: + def __init__(self, size): + self.size = size + def __iter__(self): + return self + def __next__(self): + self.size -= 1 + if self.size < 0: + raise Error + return self.size + + l1 = self.iter_error(map(pack, "AB", Iter(1), strict=True), Error) + self.assertEqual(l1, [("A", 0)]) + l2 = self.iter_error(map(pack, "AB", Iter(2), "A", strict=True), ValueError) + self.assertEqual(l2, [("A", 1, "A")]) + l3 = self.iter_error(map(pack, "AB", Iter(2), "ABC", strict=True), Error) + self.assertEqual(l3, [("A", 1, "A"), ("B", 0, "B")]) + l4 = self.iter_error(map(pack, "AB", Iter(3), strict=True), ValueError) + self.assertEqual(l4, [("A", 2), ("B", 1)]) + l5 = self.iter_error(map(pack, Iter(1), "AB", strict=True), Error) + self.assertEqual(l5, [(0, "A")]) + l6 = self.iter_error(map(pack, Iter(2), "A", strict=True), ValueError) + self.assertEqual(l6, [(1, "A")]) + l7 = self.iter_error(map(pack, Iter(2), "ABC", strict=True), Error) + self.assertEqual(l7, [(1, "A"), (0, "B")]) + l8 = self.iter_error(map(pack, Iter(3), "AB", strict=True), ValueError) + self.assertEqual(l8, [(2, "A"), (1, "B")]) + + def test_map_strict_error_handling_stopiteration(self): + + class Iter: + def __init__(self, size): + self.size = size + def __iter__(self): + return self + def __next__(self): + self.size -= 1 + if self.size < 0: + raise StopIteration + return self.size + + l1 = self.iter_error(map(pack, "AB", Iter(1), strict=True), ValueError) + self.assertEqual(l1, [("A", 0)]) + l2 = self.iter_error(map(pack, "AB", Iter(2), "A", strict=True), ValueError) + self.assertEqual(l2, [("A", 1, "A")]) + l3 = self.iter_error(map(pack, "AB", Iter(2), "ABC", strict=True), ValueError) + self.assertEqual(l3, [("A", 1, "A"), ("B", 0, "B")]) + l4 = self.iter_error(map(pack, "AB", Iter(3), strict=True), ValueError) + self.assertEqual(l4, [("A", 2), ("B", 1)]) + l5 = self.iter_error(map(pack, Iter(1), "AB", strict=True), ValueError) + self.assertEqual(l5, [(0, "A")]) + l6 = self.iter_error(map(pack, Iter(2), "A", strict=True), ValueError) + self.assertEqual(l6, [(1, "A")]) + l7 = self.iter_error(map(pack, Iter(2), "ABC", strict=True), ValueError) + self.assertEqual(l7, [(1, "A"), (0, "B")]) + l8 = self.iter_error(map(pack, Iter(3), "AB", strict=True), ValueError) + self.assertEqual(l8, [(2, "A"), (1, "B")]) + def test_max(self): self.assertEqual(max('123123'), '3') self.assertEqual(max(1, 2, 3), 3) diff --git a/Lib/test/test_itertools.py b/Lib/test/test_itertools.py index 8469de998ba014..a52e1d3fa142d9 100644 --- a/Lib/test/test_itertools.py +++ b/Lib/test/test_itertools.py @@ -2433,10 +2433,10 @@ class subclass(cls): subclass(*args, newarg=3) for cls, args, result in testcases: - # Constructors of repeat, zip, compress accept keyword arguments. + # Constructors of repeat, zip, map, compress accept keyword arguments. # Their subclasses need overriding __new__ to support new # keyword arguments. - if cls in [repeat, zip, compress]: + if cls in [repeat, zip, map, compress]: continue with self.subTest(cls): class subclass_with_init(cls): diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-06-13-19-12-49.gh-issue-119793.FDVCDk.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-06-13-19-12-49.gh-issue-119793.FDVCDk.rst new file mode 100644 index 00000000000000..976d6712e4b6af --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-06-13-19-12-49.gh-issue-119793.FDVCDk.rst @@ -0,0 +1,3 @@ +The :func:`map` built-in now has an optional keyword-only *strict* flag +like :func:`zip` to check that all the iterables are of equal length. +Patch by Wannes Boeykens. diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 12f065d4b4f138..85a28de2bb9d13 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -1311,6 +1311,7 @@ typedef struct { PyObject_HEAD PyObject *iters; PyObject *func; + int strict; } mapobject; static PyObject * @@ -1319,10 +1320,21 @@ map_new(PyTypeObject *type, PyObject *args, PyObject *kwds) PyObject *it, *iters, *func; mapobject *lz; Py_ssize_t numargs, i; + int strict = 0; - if ((type == &PyMap_Type || type->tp_init == PyMap_Type.tp_init) && - !_PyArg_NoKeywords("map", kwds)) - return NULL; + if (kwds) { + PyObject *empty = PyTuple_New(0); + if (empty == NULL) { + return NULL; + } + static char *kwlist[] = {"strict", NULL}; + int parsed = PyArg_ParseTupleAndKeywords( + empty, kwds, "|$p:map", kwlist, &strict); + Py_DECREF(empty); + if (!parsed) { + return NULL; + } + } numargs = PyTuple_Size(args); if (numargs < 2) { @@ -1354,6 +1366,7 @@ map_new(PyTypeObject *type, PyObject *args, PyObject *kwds) lz->iters = iters; func = PyTuple_GET_ITEM(args, 0); lz->func = Py_NewRef(func); + lz->strict = strict; return (PyObject *)lz; } @@ -1363,11 +1376,14 @@ map_vectorcall(PyObject *type, PyObject * const*args, size_t nargsf, PyObject *kwnames) { PyTypeObject *tp = _PyType_CAST(type); - if (tp == &PyMap_Type && !_PyArg_NoKwnames("map", kwnames)) { - return NULL; - } Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); + if (kwnames != NULL && PyTuple_GET_SIZE(kwnames) != 0) { + // Fallback to map_new() + PyThreadState *tstate = _PyThreadState_GET(); + return _PyObject_MakeTpCall(tstate, type, args, nargs, kwnames); + } + if (nargs < 2) { PyErr_SetString(PyExc_TypeError, "map() must have at least two arguments."); @@ -1395,6 +1411,7 @@ map_vectorcall(PyObject *type, PyObject * const*args, } lz->iters = iters; lz->func = Py_NewRef(args[0]); + lz->strict = 0; return (PyObject *)lz; } @@ -1419,6 +1436,7 @@ map_traverse(mapobject *lz, visitproc visit, void *arg) static PyObject * map_next(mapobject *lz) { + Py_ssize_t i; PyObject *small_stack[_PY_FASTCALL_SMALL_STACK]; PyObject **stack; PyObject *result = NULL; @@ -1437,10 +1455,13 @@ map_next(mapobject *lz) } Py_ssize_t nargs = 0; - for (Py_ssize_t i=0; i < niters; i++) { + for (i=0; i < niters; i++) { PyObject *it = PyTuple_GET_ITEM(lz->iters, i); PyObject *val = Py_TYPE(it)->tp_iternext(it); if (val == NULL) { + if (lz->strict) { + goto check; + } goto exit; } stack[i] = val; @@ -1450,13 +1471,50 @@ map_next(mapobject *lz) result = _PyObject_VectorcallTstate(tstate, lz->func, stack, nargs, NULL); exit: - for (Py_ssize_t i=0; i < nargs; i++) { + for (i=0; i < nargs; i++) { Py_DECREF(stack[i]); } if (stack != small_stack) { PyMem_Free(stack); } return result; +check: + if (PyErr_Occurred()) { + if (!PyErr_ExceptionMatches(PyExc_StopIteration)) { + // next() on argument i raised an exception (not StopIteration) + return NULL; + } + PyErr_Clear(); + } + if (i) { + // ValueError: map() argument 2 is shorter than argument 1 + // ValueError: map() argument 3 is shorter than arguments 1-2 + const char* plural = i == 1 ? " " : "s 1-"; + return PyErr_Format(PyExc_ValueError, + "map() argument %d is shorter than argument%s%d", + i + 1, plural, i); + } + for (i = 1; i < niters; i++) { + PyObject *it = PyTuple_GET_ITEM(lz->iters, i); + PyObject *val = (*Py_TYPE(it)->tp_iternext)(it); + if (val) { + Py_DECREF(val); + const char* plural = i == 1 ? " " : "s 1-"; + return PyErr_Format(PyExc_ValueError, + "map() argument %d is longer than argument%s%d", + i + 1, plural, i); + } + if (PyErr_Occurred()) { + if (!PyErr_ExceptionMatches(PyExc_StopIteration)) { + // next() on argument i raised an exception (not StopIteration) + return NULL; + } + PyErr_Clear(); + } + // Argument i is exhausted. So far so good... + } + // All arguments are exhausted. Success! + goto exit; } static PyObject * @@ -1473,21 +1531,41 @@ map_reduce(mapobject *lz, PyObject *Py_UNUSED(ignored)) PyTuple_SET_ITEM(args, i+1, Py_NewRef(it)); } + if (lz->strict) { + return Py_BuildValue("ONO", Py_TYPE(lz), args, Py_True); + } return Py_BuildValue("ON", Py_TYPE(lz), args); } +PyDoc_STRVAR(setstate_doc, "Set state information for unpickling."); + +static PyObject * +map_setstate(mapobject *lz, PyObject *state) +{ + int strict = PyObject_IsTrue(state); + if (strict < 0) { + return NULL; + } + lz->strict = strict; + Py_RETURN_NONE; +} + static PyMethodDef map_methods[] = { {"__reduce__", _PyCFunction_CAST(map_reduce), METH_NOARGS, reduce_doc}, + {"__setstate__", _PyCFunction_CAST(map_setstate), METH_O, setstate_doc}, {NULL, NULL} /* sentinel */ }; PyDoc_STRVAR(map_doc, -"map(function, iterable, /, *iterables)\n\ +"map(function, iterable, /, *iterables, strict=False)\n\ --\n\ \n\ Make an iterator that computes the function using arguments from\n\ -each of the iterables. Stops when the shortest iterable is exhausted."); +each of the iterables. Stops when the shortest iterable is exhausted.\n\ +\n\ +If strict is true and one of the arguments is exhausted before the others,\n\ +raise a ValueError."); PyTypeObject PyMap_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) @@ -3068,8 +3146,6 @@ zip_reduce(zipobject *lz, PyObject *Py_UNUSED(ignored)) return PyTuple_Pack(2, Py_TYPE(lz), lz->ittuple); } -PyDoc_STRVAR(setstate_doc, "Set state information for unpickling."); - static PyObject * zip_setstate(zipobject *lz, PyObject *state) { From c806cd5af677c385470001efc68da38a32919196 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Mon, 4 Nov 2024 19:18:21 +0300 Subject: [PATCH 044/219] gh-126220: Adapt `_lsprof` to Argument Clinic (#126233) Co-authored-by: Erlend E. Aasland --- .../pycore_global_objects_fini_generated.h | 3 + Include/internal/pycore_global_strings.h | 3 + .../internal/pycore_runtime_init_generated.h | 3 + .../internal/pycore_unicodeobject_generated.h | 12 + Modules/_lsprof.c | 251 ++++++------ Modules/clinic/_lsprof.c.h | 362 +++++++++++++++++- 6 files changed, 517 insertions(+), 117 deletions(-) diff --git a/Include/internal/pycore_global_objects_fini_generated.h b/Include/internal/pycore_global_objects_fini_generated.h index 2fd7d5d13a98b2..e4f0138e17edfa 100644 --- a/Include/internal/pycore_global_objects_fini_generated.h +++ b/Include/internal/pycore_global_objects_fini_generated.h @@ -1231,6 +1231,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(strict_mode)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(string)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(sub_key)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(subcalls)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(symmetric_difference_update)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(tabsize)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(tag)); @@ -1248,8 +1249,10 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(threading)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(throw)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(timeout)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(timer)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(times)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(timetuple)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(timeunit)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(top)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(trace_callback)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(traceback)); diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h index fc3871570cc49d..e70f11e2a26cd5 100644 --- a/Include/internal/pycore_global_strings.h +++ b/Include/internal/pycore_global_strings.h @@ -720,6 +720,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(strict_mode) STRUCT_FOR_ID(string) STRUCT_FOR_ID(sub_key) + STRUCT_FOR_ID(subcalls) STRUCT_FOR_ID(symmetric_difference_update) STRUCT_FOR_ID(tabsize) STRUCT_FOR_ID(tag) @@ -737,8 +738,10 @@ struct _Py_global_strings { STRUCT_FOR_ID(threading) STRUCT_FOR_ID(throw) STRUCT_FOR_ID(timeout) + STRUCT_FOR_ID(timer) STRUCT_FOR_ID(times) STRUCT_FOR_ID(timetuple) + STRUCT_FOR_ID(timeunit) STRUCT_FOR_ID(top) STRUCT_FOR_ID(trace_callback) STRUCT_FOR_ID(traceback) diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h index 3b80e265b0ca50..5d404c8fd91ca6 100644 --- a/Include/internal/pycore_runtime_init_generated.h +++ b/Include/internal/pycore_runtime_init_generated.h @@ -1229,6 +1229,7 @@ extern "C" { INIT_ID(strict_mode), \ INIT_ID(string), \ INIT_ID(sub_key), \ + INIT_ID(subcalls), \ INIT_ID(symmetric_difference_update), \ INIT_ID(tabsize), \ INIT_ID(tag), \ @@ -1246,8 +1247,10 @@ extern "C" { INIT_ID(threading), \ INIT_ID(throw), \ INIT_ID(timeout), \ + INIT_ID(timer), \ INIT_ID(times), \ INIT_ID(timetuple), \ + INIT_ID(timeunit), \ INIT_ID(top), \ INIT_ID(trace_callback), \ INIT_ID(traceback), \ diff --git a/Include/internal/pycore_unicodeobject_generated.h b/Include/internal/pycore_unicodeobject_generated.h index eb2eca06ec4d4f..d0bc8d7186c053 100644 --- a/Include/internal/pycore_unicodeobject_generated.h +++ b/Include/internal/pycore_unicodeobject_generated.h @@ -2676,6 +2676,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(subcalls); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(symmetric_difference_update); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); @@ -2744,6 +2748,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(timer); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(times); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); @@ -2752,6 +2760,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(timeunit); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(top); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); diff --git a/Modules/_lsprof.c b/Modules/_lsprof.c index 06958a078509d9..4f996c7230e16d 100644 --- a/Modules/_lsprof.c +++ b/Modules/_lsprof.c @@ -606,29 +606,42 @@ setBuiltins(ProfilerObject *pObj, int nvalue) return 0; } -PyObject* pystart_callback(ProfilerObject* self, PyObject *const *args, Py_ssize_t size) +/*[clinic input] +_lsprof.Profiler._pystart_callback + + code: object + instruction_offset: object + / + +[clinic start generated code]*/ + +static PyObject * +_lsprof_Profiler__pystart_callback_impl(ProfilerObject *self, PyObject *code, + PyObject *instruction_offset) +/*[clinic end generated code: output=5fec8b7ad5ed25e8 input=b166e6953c579cda]*/ { - if (size < 2) { - PyErr_Format(PyExc_TypeError, - "_pystart_callback expected 2 arguments, got %zd", - size); - return NULL; - } - PyObject* code = args[0]; - ptrace_enter_call((PyObject*)self, (void *)code, (PyObject *)code); + ptrace_enter_call((PyObject*)self, (void *)code, code); Py_RETURN_NONE; } -PyObject* pyreturn_callback(ProfilerObject* self, PyObject *const *args, Py_ssize_t size) +/*[clinic input] +_lsprof.Profiler._pyreturn_callback + + code: object + instruction_offset: object + retval: object + / + +[clinic start generated code]*/ + +static PyObject * +_lsprof_Profiler__pyreturn_callback_impl(ProfilerObject *self, + PyObject *code, + PyObject *instruction_offset, + PyObject *retval) +/*[clinic end generated code: output=9e2f6fc1b882c51e input=667ffaeb2fa6fd1f]*/ { - if (size < 3) { - PyErr_Format(PyExc_TypeError, - "_pyreturn_callback expected 3 arguments, got %zd", - size); - return NULL; - } - PyObject* code = args[0]; ptrace_leave_call((PyObject*)self, (void *)code); Py_RETURN_NONE; @@ -661,18 +674,24 @@ PyObject* get_cfunc_from_callable(PyObject* callable, PyObject* self_arg, PyObje return NULL; } -PyObject* ccall_callback(ProfilerObject* self, PyObject *const *args, Py_ssize_t size) +/*[clinic input] +_lsprof.Profiler._ccall_callback + + code: object + instruction_offset: object + callable: object + self_arg: object + / + +[clinic start generated code]*/ + +static PyObject * +_lsprof_Profiler__ccall_callback_impl(ProfilerObject *self, PyObject *code, + PyObject *instruction_offset, + PyObject *callable, PyObject *self_arg) +/*[clinic end generated code: output=152db83cabd18cad input=0e66687cfb95c001]*/ { - if (size < 4) { - PyErr_Format(PyExc_TypeError, - "_ccall_callback expected 4 arguments, got %zd", - size); - return NULL; - } if (self->flags & POF_BUILTINS) { - PyObject* callable = args[2]; - PyObject* self_arg = args[3]; - PyObject* cfunc = get_cfunc_from_callable(callable, self_arg, self->missing); if (cfunc) { @@ -685,18 +704,25 @@ PyObject* ccall_callback(ProfilerObject* self, PyObject *const *args, Py_ssize_t Py_RETURN_NONE; } -PyObject* creturn_callback(ProfilerObject* self, PyObject *const *args, Py_ssize_t size) +/*[clinic input] +_lsprof.Profiler._creturn_callback + + code: object + instruction_offset: object + callable: object + self_arg: object + / + +[clinic start generated code]*/ + +static PyObject * +_lsprof_Profiler__creturn_callback_impl(ProfilerObject *self, PyObject *code, + PyObject *instruction_offset, + PyObject *callable, + PyObject *self_arg) +/*[clinic end generated code: output=1e886dde8fed8fb0 input=b18afe023746923a]*/ { - if (size < 4) { - PyErr_Format(PyExc_TypeError, - "_creturn_callback expected 4 arguments, got %zd", - size); - return NULL; - } if (self->flags & POF_BUILTINS) { - PyObject* callable = args[2]; - PyObject* self_arg = args[3]; - PyObject* cfunc = get_cfunc_from_callable(callable, self_arg, self->missing); if (cfunc) { @@ -724,27 +750,27 @@ static const struct { {0, NULL} }; -PyDoc_STRVAR(enable_doc, "\ -enable(subcalls=True, builtins=True)\n\ -\n\ -Start collecting profiling information.\n\ -If 'subcalls' is True, also records for each function\n\ -statistics separated according to its current caller.\n\ -If 'builtins' is True, records the time spent in\n\ -built-in functions separately from their caller.\n\ -"); - -static PyObject* -profiler_enable(ProfilerObject *self, PyObject *args, PyObject *kwds) + +/*[clinic input] +_lsprof.Profiler.enable + + subcalls: bool = True + If True, also records for each function + statistics separated according to its current caller. + + builtins: bool = True + If True, records the time spent in + built-in functions separately from their caller. + +Start collecting profiling information. +[clinic start generated code]*/ + +static PyObject * +_lsprof_Profiler_enable_impl(ProfilerObject *self, int subcalls, + int builtins) +/*[clinic end generated code: output=1e747f9dc1edd571 input=9ab81405107ab7f1]*/ { - int subcalls = -1; - int builtins = -1; - static char *kwlist[] = {"subcalls", "builtins", 0}; int all_events = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|pp:enable", - kwlist, &subcalls, &builtins)) - return NULL; if (setSubcalls(self, subcalls) < 0 || setBuiltins(self, builtins) < 0) { return NULL; } @@ -800,14 +826,16 @@ flush_unmatched(ProfilerObject *pObj) } -PyDoc_STRVAR(disable_doc, "\ -disable()\n\ -\n\ -Stop collecting profiling information.\n\ -"); -static PyObject* -profiler_disable(ProfilerObject *self, PyObject* noarg) +/*[clinic input] +_lsprof.Profiler.disable + +Stop collecting profiling information. +[clinic start generated code]*/ + +static PyObject * +_lsprof_Profiler_disable_impl(ProfilerObject *self) +/*[clinic end generated code: output=838cffef7f651870 input=05700b3fc68d1f50]*/ { if (self->flags & POF_EXT_TIMER) { PyErr_SetString(PyExc_RuntimeError, @@ -858,21 +886,22 @@ profiler_disable(ProfilerObject *self, PyObject* noarg) Py_RETURN_NONE; } -PyDoc_STRVAR(clear_doc, "\ -clear()\n\ -\n\ -Clear all profiling information collected so far.\n\ -"); +/*[clinic input] +_lsprof.Profiler.clear + +Clear all profiling information collected so far. +[clinic start generated code]*/ -static PyObject* -profiler_clear(ProfilerObject *pObj, PyObject* noarg) +static PyObject * +_lsprof_Profiler_clear_impl(ProfilerObject *self) +/*[clinic end generated code: output=dd1c668fb84b1335 input=fbe1f88c28be4f98]*/ { - if (pObj->flags & POF_EXT_TIMER) { + if (self->flags & POF_EXT_TIMER) { PyErr_SetString(PyExc_RuntimeError, "cannot clear profiler in external timer"); return NULL; } - clearEntries(pObj); + clearEntries(self); Py_RETURN_NONE; } @@ -903,33 +932,40 @@ profiler_dealloc(ProfilerObject *op) Py_DECREF(tp); } +/*[clinic input] +_lsprof.Profiler.__init__ as profiler_init + + timer: object(c_default='NULL') = None + timeunit: double = 0.0 + subcalls: bool = True + builtins: bool = True + +Build a profiler object using the specified timer function. + +The default timer is a fast built-in one based on real time. +For custom timer functions returning integers, 'timeunit' can +be a float specifying a scale (that is, how long each integer unit +is, in seconds). +[clinic start generated code]*/ + static int -profiler_init(ProfilerObject *pObj, PyObject *args, PyObject *kw) +profiler_init_impl(ProfilerObject *self, PyObject *timer, double timeunit, + int subcalls, int builtins) +/*[clinic end generated code: output=ac523803ec9f9df2 input=8285ca746f96a414]*/ { - PyObject *timer = NULL; - double timeunit = 0.0; - int subcalls = 1; - int builtins = 1; - static char *kwlist[] = {"timer", "timeunit", - "subcalls", "builtins", 0}; - - if (!PyArg_ParseTupleAndKeywords(args, kw, "|Odpp:Profiler", kwlist, - &timer, &timeunit, - &subcalls, &builtins)) - return -1; - - if (setSubcalls(pObj, subcalls) < 0 || setBuiltins(pObj, builtins) < 0) + if (setSubcalls(self, subcalls) < 0 || setBuiltins(self, builtins) < 0) { return -1; - pObj->externalTimerUnit = timeunit; - Py_XSETREF(pObj->externalTimer, Py_XNewRef(timer)); - pObj->tool_id = PY_MONITORING_PROFILER_ID; + } + self->externalTimerUnit = timeunit; + Py_XSETREF(self->externalTimer, Py_XNewRef(timer)); + self->tool_id = PY_MONITORING_PROFILER_ID; PyObject* monitoring = _PyImport_GetModuleAttrString("sys", "monitoring"); if (!monitoring) { return -1; } - pObj->missing = PyObject_GetAttrString(monitoring, "MISSING"); - if (!pObj->missing) { + self->missing = PyObject_GetAttrString(monitoring, "MISSING"); + if (!self->missing) { Py_DECREF(monitoring); return -1; } @@ -939,35 +975,18 @@ profiler_init(ProfilerObject *pObj, PyObject *args, PyObject *kw) static PyMethodDef profiler_methods[] = { _LSPROF_PROFILER_GETSTATS_METHODDEF - {"enable", _PyCFunction_CAST(profiler_enable), - METH_VARARGS | METH_KEYWORDS, enable_doc}, - {"disable", (PyCFunction)profiler_disable, - METH_NOARGS, disable_doc}, - {"clear", (PyCFunction)profiler_clear, - METH_NOARGS, clear_doc}, - {"_pystart_callback", _PyCFunction_CAST(pystart_callback), - METH_FASTCALL, NULL}, - {"_pyreturn_callback", _PyCFunction_CAST(pyreturn_callback), - METH_FASTCALL, NULL}, - {"_ccall_callback", _PyCFunction_CAST(ccall_callback), - METH_FASTCALL, NULL}, - {"_creturn_callback", _PyCFunction_CAST(creturn_callback), - METH_FASTCALL, NULL}, + _LSPROF_PROFILER_ENABLE_METHODDEF + _LSPROF_PROFILER_DISABLE_METHODDEF + _LSPROF_PROFILER_CLEAR_METHODDEF + _LSPROF_PROFILER__PYSTART_CALLBACK_METHODDEF + _LSPROF_PROFILER__PYRETURN_CALLBACK_METHODDEF + _LSPROF_PROFILER__CCALL_CALLBACK_METHODDEF + _LSPROF_PROFILER__CRETURN_CALLBACK_METHODDEF {NULL, NULL} }; -PyDoc_STRVAR(profiler_doc, "\ -Profiler(timer=None, timeunit=None, subcalls=True, builtins=True)\n\ -\n\ - Builds a profiler object using the specified timer function.\n\ - The default timer is a fast built-in one based on real time.\n\ - For custom timer functions returning integers, timeunit can\n\ - be a float specifying a scale (i.e. how long each integer unit\n\ - is, in seconds).\n\ -"); - static PyType_Slot _lsprof_profiler_type_spec_slots[] = { - {Py_tp_doc, (void *)profiler_doc}, + {Py_tp_doc, (void *)profiler_init__doc__}, {Py_tp_methods, profiler_methods}, {Py_tp_dealloc, profiler_dealloc}, {Py_tp_init, profiler_init}, diff --git a/Modules/clinic/_lsprof.c.h b/Modules/clinic/_lsprof.c.h index b3b7fda5660bfd..234cc9ef3c0eaf 100644 --- a/Modules/clinic/_lsprof.c.h +++ b/Modules/clinic/_lsprof.c.h @@ -2,6 +2,12 @@ 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 +#include "pycore_modsupport.h" // _PyArg_CheckPositional() + PyDoc_STRVAR(_lsprof_Profiler_getstats__doc__, "getstats($self, /)\n" "--\n" @@ -45,4 +51,358 @@ _lsprof_Profiler_getstats(ProfilerObject *self, PyTypeObject *cls, PyObject *con } return _lsprof_Profiler_getstats_impl(self, cls); } -/*[clinic end generated code: output=5c9d87d89863dc83 input=a9049054013a1b77]*/ + +PyDoc_STRVAR(_lsprof_Profiler__pystart_callback__doc__, +"_pystart_callback($self, code, instruction_offset, /)\n" +"--\n" +"\n"); + +#define _LSPROF_PROFILER__PYSTART_CALLBACK_METHODDEF \ + {"_pystart_callback", _PyCFunction_CAST(_lsprof_Profiler__pystart_callback), METH_FASTCALL, _lsprof_Profiler__pystart_callback__doc__}, + +static PyObject * +_lsprof_Profiler__pystart_callback_impl(ProfilerObject *self, PyObject *code, + PyObject *instruction_offset); + +static PyObject * +_lsprof_Profiler__pystart_callback(ProfilerObject *self, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *code; + PyObject *instruction_offset; + + if (!_PyArg_CheckPositional("_pystart_callback", nargs, 2, 2)) { + goto exit; + } + code = args[0]; + instruction_offset = args[1]; + return_value = _lsprof_Profiler__pystart_callback_impl(self, code, instruction_offset); + +exit: + return return_value; +} + +PyDoc_STRVAR(_lsprof_Profiler__pyreturn_callback__doc__, +"_pyreturn_callback($self, code, instruction_offset, retval, /)\n" +"--\n" +"\n"); + +#define _LSPROF_PROFILER__PYRETURN_CALLBACK_METHODDEF \ + {"_pyreturn_callback", _PyCFunction_CAST(_lsprof_Profiler__pyreturn_callback), METH_FASTCALL, _lsprof_Profiler__pyreturn_callback__doc__}, + +static PyObject * +_lsprof_Profiler__pyreturn_callback_impl(ProfilerObject *self, + PyObject *code, + PyObject *instruction_offset, + PyObject *retval); + +static PyObject * +_lsprof_Profiler__pyreturn_callback(ProfilerObject *self, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *code; + PyObject *instruction_offset; + PyObject *retval; + + if (!_PyArg_CheckPositional("_pyreturn_callback", nargs, 3, 3)) { + goto exit; + } + code = args[0]; + instruction_offset = args[1]; + retval = args[2]; + return_value = _lsprof_Profiler__pyreturn_callback_impl(self, code, instruction_offset, retval); + +exit: + return return_value; +} + +PyDoc_STRVAR(_lsprof_Profiler__ccall_callback__doc__, +"_ccall_callback($self, code, instruction_offset, callable, self_arg, /)\n" +"--\n" +"\n"); + +#define _LSPROF_PROFILER__CCALL_CALLBACK_METHODDEF \ + {"_ccall_callback", _PyCFunction_CAST(_lsprof_Profiler__ccall_callback), METH_FASTCALL, _lsprof_Profiler__ccall_callback__doc__}, + +static PyObject * +_lsprof_Profiler__ccall_callback_impl(ProfilerObject *self, PyObject *code, + PyObject *instruction_offset, + PyObject *callable, PyObject *self_arg); + +static PyObject * +_lsprof_Profiler__ccall_callback(ProfilerObject *self, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *code; + PyObject *instruction_offset; + PyObject *callable; + PyObject *self_arg; + + if (!_PyArg_CheckPositional("_ccall_callback", nargs, 4, 4)) { + goto exit; + } + code = args[0]; + instruction_offset = args[1]; + callable = args[2]; + self_arg = args[3]; + return_value = _lsprof_Profiler__ccall_callback_impl(self, code, instruction_offset, callable, self_arg); + +exit: + return return_value; +} + +PyDoc_STRVAR(_lsprof_Profiler__creturn_callback__doc__, +"_creturn_callback($self, code, instruction_offset, callable, self_arg,\n" +" /)\n" +"--\n" +"\n"); + +#define _LSPROF_PROFILER__CRETURN_CALLBACK_METHODDEF \ + {"_creturn_callback", _PyCFunction_CAST(_lsprof_Profiler__creturn_callback), METH_FASTCALL, _lsprof_Profiler__creturn_callback__doc__}, + +static PyObject * +_lsprof_Profiler__creturn_callback_impl(ProfilerObject *self, PyObject *code, + PyObject *instruction_offset, + PyObject *callable, + PyObject *self_arg); + +static PyObject * +_lsprof_Profiler__creturn_callback(ProfilerObject *self, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *code; + PyObject *instruction_offset; + PyObject *callable; + PyObject *self_arg; + + if (!_PyArg_CheckPositional("_creturn_callback", nargs, 4, 4)) { + goto exit; + } + code = args[0]; + instruction_offset = args[1]; + callable = args[2]; + self_arg = args[3]; + return_value = _lsprof_Profiler__creturn_callback_impl(self, code, instruction_offset, callable, self_arg); + +exit: + return return_value; +} + +PyDoc_STRVAR(_lsprof_Profiler_enable__doc__, +"enable($self, /, subcalls=True, builtins=True)\n" +"--\n" +"\n" +"Start collecting profiling information.\n" +"\n" +" subcalls\n" +" If True, also records for each function\n" +" statistics separated according to its current caller.\n" +" builtins\n" +" If True, records the time spent in\n" +" built-in functions separately from their caller."); + +#define _LSPROF_PROFILER_ENABLE_METHODDEF \ + {"enable", _PyCFunction_CAST(_lsprof_Profiler_enable), METH_FASTCALL|METH_KEYWORDS, _lsprof_Profiler_enable__doc__}, + +static PyObject * +_lsprof_Profiler_enable_impl(ProfilerObject *self, int subcalls, + int builtins); + +static PyObject * +_lsprof_Profiler_enable(ProfilerObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(subcalls), &_Py_ID(builtins), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"subcalls", "builtins", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "enable", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + int subcalls = 1; + int builtins = 1; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 2, 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (args[0]) { + subcalls = PyObject_IsTrue(args[0]); + if (subcalls < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + builtins = PyObject_IsTrue(args[1]); + if (builtins < 0) { + goto exit; + } +skip_optional_pos: + return_value = _lsprof_Profiler_enable_impl(self, subcalls, builtins); + +exit: + return return_value; +} + +PyDoc_STRVAR(_lsprof_Profiler_disable__doc__, +"disable($self, /)\n" +"--\n" +"\n" +"Stop collecting profiling information."); + +#define _LSPROF_PROFILER_DISABLE_METHODDEF \ + {"disable", (PyCFunction)_lsprof_Profiler_disable, METH_NOARGS, _lsprof_Profiler_disable__doc__}, + +static PyObject * +_lsprof_Profiler_disable_impl(ProfilerObject *self); + +static PyObject * +_lsprof_Profiler_disable(ProfilerObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _lsprof_Profiler_disable_impl(self); +} + +PyDoc_STRVAR(_lsprof_Profiler_clear__doc__, +"clear($self, /)\n" +"--\n" +"\n" +"Clear all profiling information collected so far."); + +#define _LSPROF_PROFILER_CLEAR_METHODDEF \ + {"clear", (PyCFunction)_lsprof_Profiler_clear, METH_NOARGS, _lsprof_Profiler_clear__doc__}, + +static PyObject * +_lsprof_Profiler_clear_impl(ProfilerObject *self); + +static PyObject * +_lsprof_Profiler_clear(ProfilerObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _lsprof_Profiler_clear_impl(self); +} + +PyDoc_STRVAR(profiler_init__doc__, +"Profiler(timer=None, timeunit=0.0, subcalls=True, builtins=True)\n" +"--\n" +"\n" +"Build a profiler object using the specified timer function.\n" +"\n" +"The default timer is a fast built-in one based on real time.\n" +"For custom timer functions returning integers, \'timeunit\' can\n" +"be a float specifying a scale (that is, how long each integer unit\n" +"is, in seconds)."); + +static int +profiler_init_impl(ProfilerObject *self, PyObject *timer, double timeunit, + int subcalls, int builtins); + +static int +profiler_init(PyObject *self, PyObject *args, PyObject *kwargs) +{ + int return_value = -1; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 4 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(timer), &_Py_ID(timeunit), &_Py_ID(subcalls), &_Py_ID(builtins), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"timer", "timeunit", "subcalls", "builtins", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "Profiler", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[4]; + PyObject * const *fastargs; + Py_ssize_t nargs = PyTuple_GET_SIZE(args); + Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 0; + PyObject *timer = NULL; + double timeunit = 0.0; + int subcalls = 1; + int builtins = 1; + + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 0, 4, 0, argsbuf); + if (!fastargs) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (fastargs[0]) { + timer = fastargs[0]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (fastargs[1]) { + if (PyFloat_CheckExact(fastargs[1])) { + timeunit = PyFloat_AS_DOUBLE(fastargs[1]); + } + else + { + timeunit = PyFloat_AsDouble(fastargs[1]); + if (timeunit == -1.0 && PyErr_Occurred()) { + goto exit; + } + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (fastargs[2]) { + subcalls = PyObject_IsTrue(fastargs[2]); + if (subcalls < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + builtins = PyObject_IsTrue(fastargs[3]); + if (builtins < 0) { + goto exit; + } +skip_optional_pos: + return_value = profiler_init_impl((ProfilerObject *)self, timer, timeunit, subcalls, builtins); + +exit: + return return_value; +} +/*[clinic end generated code: output=0b71f52bee9a7bb1 input=a9049054013a1b77]*/ From eac41c5ddfadf52fbd84ee898ad56aedd5d90a41 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 4 Nov 2024 18:49:59 +0200 Subject: [PATCH 045/219] gh-101865: Docs: Keep co_lnotab deprecation for at least 3.14 (#126392) --- Doc/deprecations/pending-removal-in-3.14.rst | 7 ------- Doc/deprecations/pending-removal-in-3.15.rst | 9 +++++++++ Doc/reference/datamodel.rst | 2 +- Doc/whatsnew/3.12.rst | 4 ++-- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Doc/deprecations/pending-removal-in-3.14.rst b/Doc/deprecations/pending-removal-in-3.14.rst index 0863853339b8b5..1904465b856506 100644 --- a/Doc/deprecations/pending-removal-in-3.14.rst +++ b/Doc/deprecations/pending-removal-in-3.14.rst @@ -103,13 +103,6 @@ Pending removal in Python 3.14 if :ref:`named placeholders ` are used and *parameters* is a sequence instead of a :class:`dict`. -* :class:`types.CodeType`: Accessing :attr:`~codeobject.co_lnotab` was - deprecated in :pep:`626` - since 3.10 and was planned to be removed in 3.12, - but it only got a proper :exc:`DeprecationWarning` in 3.12. - May be removed in 3.14. - (Contributed by Nikita Sobolev in :gh:`101866`.) - * :mod:`typing`: :class:`!typing.ByteString`, deprecated since Python 3.9, now causes a :exc:`DeprecationWarning` to be emitted when it is used. diff --git a/Doc/deprecations/pending-removal-in-3.15.rst b/Doc/deprecations/pending-removal-in-3.15.rst index 17029b8d4773bd..3b03e1f49e6754 100644 --- a/Doc/deprecations/pending-removal-in-3.15.rst +++ b/Doc/deprecations/pending-removal-in-3.15.rst @@ -59,6 +59,15 @@ Pending removal in Python 3.15 but the C version allows any number of positional or keyword arguments, ignoring every argument. +* :mod:`types`: + + * :class:`types.CodeType`: Accessing :attr:`~codeobject.co_lnotab` was + deprecated in :pep:`626` + since 3.10 and was planned to be removed in 3.12, + but it only got a proper :exc:`DeprecationWarning` in 3.12. + May be removed in 3.15. + (Contributed by Nikita Sobolev in :gh:`101866`.) + * :mod:`typing`: * The undocumented keyword argument syntax for creating diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index dfd1addf656a85..41133b92ed88ec 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -1507,7 +1507,7 @@ Special read-only attributes .. deprecated:: 3.12 This attribute of code objects is deprecated, and may be removed in - Python 3.14. + Python 3.15. * - .. attribute:: codeobject.co_stacksize - The required stack size of the code object diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 3640095acbaa2b..d691185cb1ffc5 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -1327,8 +1327,8 @@ Deprecated * Accessing :attr:`~codeobject.co_lnotab` on code objects was deprecated in Python 3.10 via :pep:`626`, - but it only got a proper :exc:`DeprecationWarning` in 3.12, - therefore it will be removed in 3.14. + but it only got a proper :exc:`DeprecationWarning` in 3.12. + May be removed in 3.15. (Contributed by Nikita Sobolev in :gh:`101866`.) .. include:: ../deprecations/pending-removal-in-3.13.rst From e5a4b402ae55f5eeeb44d3e7bc3f3ec39b249846 Mon Sep 17 00:00:00 2001 From: Mikhail Efimov Date: Mon, 4 Nov 2024 20:28:05 +0300 Subject: [PATCH 046/219] Doc: Fix typo in documentation for ``MAKE_FUNCTION`` opcode (#126396) --- Doc/library/dis.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst index cf203a714ba126..ecbe0fae8cd74c 100644 --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -1562,7 +1562,7 @@ iterations of the loop. .. opcode:: MAKE_FUNCTION - Pushes a new function object on the stack built from the code object at ``STACK[1]``. + Pushes a new function object on the stack built from the code object at ``STACK[-1]``. .. versionchanged:: 3.10 Flag value ``0x04`` is a tuple of strings instead of dictionary From 2e95c5ba3bf7e5004c7e2304afda4a8f8e2443a7 Mon Sep 17 00:00:00 2001 From: mpage Date: Mon, 4 Nov 2024 11:13:32 -0800 Subject: [PATCH 047/219] gh-115999: Implement thread-local bytecode and enable specialization for `BINARY_OP` (#123926) Each thread specializes a thread-local copy of the bytecode, created on the first RESUME, in free-threaded builds. All copies of the bytecode for a code object are stored in the co_tlbc array on the code object. Threads reserve a globally unique index identifying its copy of the bytecode in all co_tlbc arrays at thread creation and release the index at thread destruction. The first entry in every co_tlbc array always points to the "main" copy of the bytecode that is stored at the end of the code object. This ensures that no bytecode is copied for programs that do not use threads. Thread-local bytecode can be disabled at runtime by providing either -X tlbc=0 or PYTHON_TLBC=0. Disabling thread-local bytecode also disables specialization. Concurrent modifications to the bytecode made by the specializing interpreter and instrumentation use atomics, with specialization taking care not to overwrite an instruction that was instrumented concurrently. --- Include/cpython/code.h | 19 ++ Include/cpython/initconfig.h | 1 + Include/internal/pycore_ceval.h | 12 + Include/internal/pycore_code.h | 41 ++++ Include/internal/pycore_frame.h | 56 ++++- Include/internal/pycore_gc.h | 4 + Include/internal/pycore_index_pool.h | 56 +++++ Include/internal/pycore_interp.h | 2 + Include/internal/pycore_tstate.h | 4 +- Include/internal/pycore_uop_ids.h | 123 +++++----- Include/internal/pycore_uop_metadata.h | 2 +- Lib/test/support/__init__.py | 5 + Lib/test/test_capi/test_config.py | 1 + Lib/test/test_capi/test_opt.py | 7 +- Lib/test/test_cmd_line.py | 52 ++++ Lib/test/test_dis.py | 8 +- Lib/test/test_embed.py | 1 + Lib/test/test_sys.py | 14 +- Lib/test/test_thread_local_bytecode.py | 198 ++++++++++++++++ Makefile.pre.in | 2 + Modules/_opcode.c | 3 + Modules/_testinternalcapi.c | 46 +++- Objects/codeobject.c | 313 ++++++++++++++++++++++++- Objects/frameobject.c | 14 +- Objects/typeobject.c | 7 +- PCbuild/_freeze_module.vcxproj | 1 + PCbuild/_freeze_module.vcxproj.filters | 3 + PCbuild/pythoncore.vcxproj | 2 + PCbuild/pythoncore.vcxproj.filters | 6 + Python/bytecodes.c | 68 +++--- Python/ceval.c | 23 +- Python/ceval_macros.h | 22 +- Python/executor_cases.c.h | 23 +- Python/frame.c | 3 +- Python/gc_free_threading.c | 12 +- Python/generated_cases.c.h | 100 +++++--- Python/index_pool.c | 193 +++++++++++++++ Python/initconfig.c | 49 +++- Python/instrumentation.c | 159 +++++++------ Python/optimizer_cases.c.h | 2 + Python/pystate.c | 10 + Python/specialize.c | 68 ++++-- Python/sysmodule.c | 5 + Tools/gdb/libpython.py | 23 +- 44 files changed, 1509 insertions(+), 254 deletions(-) create mode 100644 Include/internal/pycore_index_pool.h create mode 100644 Lib/test/test_thread_local_bytecode.py create mode 100644 Python/index_pool.c diff --git a/Include/cpython/code.h b/Include/cpython/code.h index 2561b2b88baacc..370f1d259abe0f 100644 --- a/Include/cpython/code.h +++ b/Include/cpython/code.h @@ -72,6 +72,24 @@ typedef struct { uint8_t *per_instruction_tools; } _PyCoMonitoringData; +#ifdef Py_GIL_DISABLED + +/* Each thread specializes a thread-local copy of the bytecode in free-threaded + * builds. These copies are stored on the code object in a `_PyCodeArray`. The + * first entry in the array always points to the "main" copy of the bytecode + * that is stored at the end of the code object. + */ +typedef struct { + Py_ssize_t size; + char *entries[1]; +} _PyCodeArray; + +#define _PyCode_DEF_THREAD_LOCAL_BYTECODE() \ + _PyCodeArray *co_tlbc; +#else +#define _PyCode_DEF_THREAD_LOCAL_BYTECODE() +#endif + // To avoid repeating ourselves in deepfreeze.py, all PyCodeObject members are // defined in this macro: #define _PyCode_DEF(SIZE) { \ @@ -138,6 +156,7 @@ typedef struct { Type is a void* to keep the format private in codeobject.c to force \ people to go through the proper APIs. */ \ void *co_extra; \ + _PyCode_DEF_THREAD_LOCAL_BYTECODE() \ char co_code_adaptive[(SIZE)]; \ } diff --git a/Include/cpython/initconfig.h b/Include/cpython/initconfig.h index c2cb4e3cdd92fb..f69c586a4f96f3 100644 --- a/Include/cpython/initconfig.h +++ b/Include/cpython/initconfig.h @@ -183,6 +183,7 @@ typedef struct PyConfig { int cpu_count; #ifdef Py_GIL_DISABLED int enable_gil; + int tlbc_enabled; #endif /* --- Path configuration inputs ------------ */ diff --git a/Include/internal/pycore_ceval.h b/Include/internal/pycore_ceval.h index 411bbff106dd69..80bd19a887871c 100644 --- a/Include/internal/pycore_ceval.h +++ b/Include/internal/pycore_ceval.h @@ -174,6 +174,18 @@ _PyEval_IsGILEnabled(PyThreadState *tstate) extern int _PyEval_EnableGILTransient(PyThreadState *tstate); extern int _PyEval_EnableGILPermanent(PyThreadState *tstate); extern int _PyEval_DisableGIL(PyThreadState *state); + + +static inline _Py_CODEUNIT * +_PyEval_GetExecutableCode(PyThreadState *tstate, PyCodeObject *co) +{ + _Py_CODEUNIT *bc = _PyCode_GetTLBCFast(tstate, co); + if (bc != NULL) { + return bc; + } + return _PyCode_GetTLBC(co); +} + #endif extern void _PyEval_DeactivateOpCache(void); diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index 57e0a14bb9b5bd..a0acf76db6f04d 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -11,6 +11,7 @@ extern "C" { #include "pycore_stackref.h" // _PyStackRef #include "pycore_lock.h" // PyMutex #include "pycore_backoff.h" // _Py_BackoffCounter +#include "pycore_tstate.h" // _PyThreadStateImpl /* Each instruction in a code object is a fixed-width value, @@ -313,11 +314,17 @@ extern int _PyLineTable_PreviousAddressRange(PyCodeAddressRange *range); /** API for executors */ extern void _PyCode_Clear_Executors(PyCodeObject *code); + #ifdef Py_GIL_DISABLED // gh-115999 tracks progress on addressing this. #define ENABLE_SPECIALIZATION 0 +// Use this to enable specialization families once they are thread-safe. All +// uses will be replaced with ENABLE_SPECIALIZATION once all families are +// thread-safe. +#define ENABLE_SPECIALIZATION_FT 1 #else #define ENABLE_SPECIALIZATION 1 +#define ENABLE_SPECIALIZATION_FT ENABLE_SPECIALIZATION #endif /* Specialization functions */ @@ -600,6 +607,40 @@ struct _PyCode8 _PyCode_DEF(8); PyAPI_DATA(const struct _PyCode8) _Py_InitCleanup; +#ifdef Py_GIL_DISABLED + +// Return a pointer to the thread-local bytecode for the current thread, if it +// exists. +static inline _Py_CODEUNIT * +_PyCode_GetTLBCFast(PyThreadState *tstate, PyCodeObject *co) +{ + _PyCodeArray *code = _Py_atomic_load_ptr_acquire(&co->co_tlbc); + int32_t idx = ((_PyThreadStateImpl*) tstate)->tlbc_index; + if (idx < code->size && code->entries[idx] != NULL) { + return (_Py_CODEUNIT *) code->entries[idx]; + } + return NULL; +} + +// Return a pointer to the thread-local bytecode for the current thread, +// creating it if necessary. +extern _Py_CODEUNIT *_PyCode_GetTLBC(PyCodeObject *co); + +// Reserve an index for the current thread into thread-local bytecode +// arrays +// +// Returns the reserved index or -1 on error. +extern int32_t _Py_ReserveTLBCIndex(PyInterpreterState *interp); + +// Release the current thread's index into thread-local bytecode arrays +extern void _Py_ClearTLBCIndex(_PyThreadStateImpl *tstate); + +// Free all TLBC copies not associated with live threads. +// +// Returns 0 on success or -1 on error. +extern int _Py_ClearUnusedTLBC(PyInterpreterState *interp); +#endif + #ifdef __cplusplus } #endif diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index c9ac3819d0390b..8c0100390d036e 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -68,6 +68,10 @@ typedef struct _PyInterpreterFrame { PyObject *f_locals; /* Strong reference, may be NULL. Only valid if not on C stack */ PyFrameObject *frame_obj; /* Strong reference, may be NULL. Only valid if not on C stack */ _Py_CODEUNIT *instr_ptr; /* Instruction currently executing (or about to begin) */ +#ifdef Py_GIL_DISABLED + /* Index of thread-local bytecode containing instr_ptr. */ + int32_t tlbc_index; +#endif _PyStackRef *stackpointer; uint16_t return_offset; /* Only relevant during a function call */ char owner; @@ -76,7 +80,7 @@ typedef struct _PyInterpreterFrame { } _PyInterpreterFrame; #define _PyInterpreterFrame_LASTI(IF) \ - ((int)((IF)->instr_ptr - _PyCode_CODE(_PyFrame_GetCode(IF)))) + ((int)((IF)->instr_ptr - _PyFrame_GetBytecode((IF)))) static inline PyCodeObject *_PyFrame_GetCode(_PyInterpreterFrame *f) { PyObject *executable = PyStackRef_AsPyObjectBorrow(f->f_executable); @@ -84,6 +88,19 @@ static inline PyCodeObject *_PyFrame_GetCode(_PyInterpreterFrame *f) { return (PyCodeObject *)executable; } +static inline _Py_CODEUNIT * +_PyFrame_GetBytecode(_PyInterpreterFrame *f) +{ +#ifdef Py_GIL_DISABLED + PyCodeObject *co = _PyFrame_GetCode(f); + _PyCodeArray *tlbc = _Py_atomic_load_ptr_acquire(&co->co_tlbc); + assert(f->tlbc_index >= 0 && f->tlbc_index < tlbc->size); + return (_Py_CODEUNIT *)tlbc->entries[f->tlbc_index]; +#else + return _PyCode_CODE(_PyFrame_GetCode(f)); +#endif +} + static inline PyFunctionObject *_PyFrame_GetFunction(_PyInterpreterFrame *f) { PyObject *func = PyStackRef_AsPyObjectBorrow(f->f_funcobj); assert(PyFunction_Check(func)); @@ -144,13 +161,33 @@ static inline void _PyFrame_Copy(_PyInterpreterFrame *src, _PyInterpreterFrame * #endif } +#ifdef Py_GIL_DISABLED +static inline void +_PyFrame_InitializeTLBC(PyThreadState *tstate, _PyInterpreterFrame *frame, + PyCodeObject *code) +{ + _Py_CODEUNIT *tlbc = _PyCode_GetTLBCFast(tstate, code); + if (tlbc == NULL) { + // No thread-local bytecode exists for this thread yet; use the main + // thread's copy, deferring thread-local bytecode creation to the + // execution of RESUME. + frame->instr_ptr = _PyCode_CODE(code); + frame->tlbc_index = 0; + } + else { + frame->instr_ptr = tlbc; + frame->tlbc_index = ((_PyThreadStateImpl *)tstate)->tlbc_index; + } +} +#endif + /* Consumes reference to func and locals. Does not initialize frame->previous, which happens when frame is linked into the frame stack. */ static inline void _PyFrame_Initialize( - _PyInterpreterFrame *frame, _PyStackRef func, + PyThreadState *tstate, _PyInterpreterFrame *frame, _PyStackRef func, PyObject *locals, PyCodeObject *code, int null_locals_from, _PyInterpreterFrame *previous) { frame->previous = previous; @@ -162,7 +199,12 @@ _PyFrame_Initialize( frame->f_locals = locals; frame->stackpointer = frame->localsplus + code->co_nlocalsplus; frame->frame_obj = NULL; +#ifdef Py_GIL_DISABLED + _PyFrame_InitializeTLBC(tstate, frame, code); +#else + (void)tstate; frame->instr_ptr = _PyCode_CODE(code); +#endif frame->return_offset = 0; frame->owner = FRAME_OWNED_BY_THREAD; @@ -224,7 +266,8 @@ _PyFrame_IsIncomplete(_PyInterpreterFrame *frame) return true; } return frame->owner != FRAME_OWNED_BY_GENERATOR && - frame->instr_ptr < _PyCode_CODE(_PyFrame_GetCode(frame)) + _PyFrame_GetCode(frame)->_co_firsttraceable; + frame->instr_ptr < _PyFrame_GetBytecode(frame) + + _PyFrame_GetCode(frame)->_co_firsttraceable; } static inline _PyInterpreterFrame * @@ -315,7 +358,8 @@ _PyFrame_PushUnchecked(PyThreadState *tstate, _PyStackRef func, int null_locals_ _PyInterpreterFrame *new_frame = (_PyInterpreterFrame *)tstate->datastack_top; tstate->datastack_top += code->co_framesize; assert(tstate->datastack_top < tstate->datastack_limit); - _PyFrame_Initialize(new_frame, func, NULL, code, null_locals_from, previous); + _PyFrame_Initialize(tstate, new_frame, func, NULL, code, null_locals_from, + previous); return new_frame; } @@ -339,7 +383,11 @@ _PyFrame_PushTrampolineUnchecked(PyThreadState *tstate, PyCodeObject *code, int assert(stackdepth <= code->co_stacksize); frame->stackpointer = frame->localsplus + code->co_nlocalsplus + stackdepth; frame->frame_obj = NULL; +#ifdef Py_GIL_DISABLED + _PyFrame_InitializeTLBC(tstate, frame, code); +#else frame->instr_ptr = _PyCode_CODE(code); +#endif frame->owner = FRAME_OWNED_BY_THREAD; frame->return_offset = 0; diff --git a/Include/internal/pycore_gc.h b/Include/internal/pycore_gc.h index b85957df5a6b9f..38a1c56c09d9db 100644 --- a/Include/internal/pycore_gc.h +++ b/Include/internal/pycore_gc.h @@ -389,6 +389,10 @@ extern int _PyGC_VisitStackRef(union _PyStackRef *ref, visitproc visit, void *ar } \ } while (0) +#ifdef Py_GIL_DISABLED +extern void _PyGC_VisitObjectsWorldStopped(PyInterpreterState *interp, + gcvisitobjects_t callback, void *arg); +#endif #ifdef __cplusplus } diff --git a/Include/internal/pycore_index_pool.h b/Include/internal/pycore_index_pool.h new file mode 100644 index 00000000000000..e81bfd4d6ed03d --- /dev/null +++ b/Include/internal/pycore_index_pool.h @@ -0,0 +1,56 @@ +#ifndef Py_INTERNAL_INDEX_POOL_H +#define Py_INTERNAL_INDEX_POOL_H + +#include "Python.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_BUILD_CORE +# error "this header requires Py_BUILD_CORE define" +#endif + +#ifdef Py_GIL_DISABLED + +// This contains code for allocating unique indices in an array. It is used by +// the free-threaded build to assign each thread a globally unique index into +// each code object's thread-local bytecode array. + +// A min-heap of indices +typedef struct _PyIndexHeap { + int32_t *values; + + // Number of items stored in values + Py_ssize_t size; + + // Maximum number of items that can be stored in values + Py_ssize_t capacity; +} _PyIndexHeap; + +// An unbounded pool of indices. Indices are allocated starting from 0. They +// may be released back to the pool once they are no longer in use. +typedef struct _PyIndexPool { + PyMutex mutex; + + // Min heap of indices available for allocation + _PyIndexHeap free_indices; + + // Next index to allocate if no free indices are available + int32_t next_index; +} _PyIndexPool; + +// Allocate the smallest available index. Returns -1 on error. +extern int32_t _PyIndexPool_AllocIndex(_PyIndexPool *indices); + +// Release `index` back to the pool +extern void _PyIndexPool_FreeIndex(_PyIndexPool *indices, int32_t index); + +extern void _PyIndexPool_Fini(_PyIndexPool *indices); + +#endif // Py_GIL_DISABLED + +#ifdef __cplusplus +} +#endif +#endif // !Py_INTERNAL_INDEX_POOL_H diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index 36cd71e5a007d5..9e3b4299693bbc 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -26,6 +26,7 @@ extern "C" { #include "pycore_genobject.h" // _PyGen_FetchStopIterationValue #include "pycore_global_objects.h"// struct _Py_interp_cached_objects #include "pycore_import.h" // struct _import_state +#include "pycore_index_pool.h" // _PyIndexPool #include "pycore_instruments.h" // _PY_MONITORING_EVENTS #include "pycore_list.h" // struct _Py_list_state #include "pycore_mimalloc.h" // struct _mimalloc_interp_state @@ -222,6 +223,7 @@ struct _is { struct _brc_state brc; // biased reference counting state struct _Py_unique_id_pool unique_ids; // object ids for per-thread refcounts PyMutex weakref_locks[NUM_WEAKREF_LIST_LOCKS]; + _PyIndexPool tlbc_indices; #endif // Per-interpreter state for the obmalloc allocator. For the main diff --git a/Include/internal/pycore_tstate.h b/Include/internal/pycore_tstate.h index e0e7d5ebf0912c..b8bea72baeaaf5 100644 --- a/Include/internal/pycore_tstate.h +++ b/Include/internal/pycore_tstate.h @@ -42,6 +42,9 @@ typedef struct _PyThreadStateImpl { int is_finalized; } refcounts; + // Index to use to retrieve thread-local bytecode for this thread + int32_t tlbc_index; + // When >1, code objects do not immortalize their non-string constants. int suppress_co_const_immortalization; #endif @@ -52,7 +55,6 @@ typedef struct _PyThreadStateImpl { } _PyThreadStateImpl; - #ifdef __cplusplus } #endif diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h index de628d240d1c07..55416d2aae1e1a 100644 --- a/Include/internal/pycore_uop_ids.h +++ b/Include/internal/pycore_uop_ids.h @@ -193,106 +193,107 @@ extern "C" { #define _LOAD_ATTR_SLOT_1 423 #define _LOAD_ATTR_WITH_HINT 424 #define _LOAD_BUILD_CLASS LOAD_BUILD_CLASS +#define _LOAD_BYTECODE 425 #define _LOAD_COMMON_CONSTANT LOAD_COMMON_CONSTANT #define _LOAD_CONST LOAD_CONST #define _LOAD_CONST_IMMORTAL LOAD_CONST_IMMORTAL -#define _LOAD_CONST_INLINE 425 -#define _LOAD_CONST_INLINE_BORROW 426 -#define _LOAD_CONST_INLINE_BORROW_WITH_NULL 427 -#define _LOAD_CONST_INLINE_WITH_NULL 428 +#define _LOAD_CONST_INLINE 426 +#define _LOAD_CONST_INLINE_BORROW 427 +#define _LOAD_CONST_INLINE_BORROW_WITH_NULL 428 +#define _LOAD_CONST_INLINE_WITH_NULL 429 #define _LOAD_DEREF LOAD_DEREF -#define _LOAD_FAST 429 -#define _LOAD_FAST_0 430 -#define _LOAD_FAST_1 431 -#define _LOAD_FAST_2 432 -#define _LOAD_FAST_3 433 -#define _LOAD_FAST_4 434 -#define _LOAD_FAST_5 435 -#define _LOAD_FAST_6 436 -#define _LOAD_FAST_7 437 +#define _LOAD_FAST 430 +#define _LOAD_FAST_0 431 +#define _LOAD_FAST_1 432 +#define _LOAD_FAST_2 433 +#define _LOAD_FAST_3 434 +#define _LOAD_FAST_4 435 +#define _LOAD_FAST_5 436 +#define _LOAD_FAST_6 437 +#define _LOAD_FAST_7 438 #define _LOAD_FAST_AND_CLEAR LOAD_FAST_AND_CLEAR #define _LOAD_FAST_CHECK LOAD_FAST_CHECK #define _LOAD_FAST_LOAD_FAST LOAD_FAST_LOAD_FAST #define _LOAD_FROM_DICT_OR_DEREF LOAD_FROM_DICT_OR_DEREF #define _LOAD_FROM_DICT_OR_GLOBALS LOAD_FROM_DICT_OR_GLOBALS -#define _LOAD_GLOBAL 438 -#define _LOAD_GLOBAL_BUILTINS 439 -#define _LOAD_GLOBAL_BUILTINS_FROM_KEYS 440 -#define _LOAD_GLOBAL_MODULE 441 -#define _LOAD_GLOBAL_MODULE_FROM_KEYS 442 +#define _LOAD_GLOBAL 439 +#define _LOAD_GLOBAL_BUILTINS 440 +#define _LOAD_GLOBAL_BUILTINS_FROM_KEYS 441 +#define _LOAD_GLOBAL_MODULE 442 +#define _LOAD_GLOBAL_MODULE_FROM_KEYS 443 #define _LOAD_LOCALS LOAD_LOCALS #define _LOAD_NAME LOAD_NAME -#define _LOAD_SMALL_INT 443 -#define _LOAD_SMALL_INT_0 444 -#define _LOAD_SMALL_INT_1 445 -#define _LOAD_SMALL_INT_2 446 -#define _LOAD_SMALL_INT_3 447 +#define _LOAD_SMALL_INT 444 +#define _LOAD_SMALL_INT_0 445 +#define _LOAD_SMALL_INT_1 446 +#define _LOAD_SMALL_INT_2 447 +#define _LOAD_SMALL_INT_3 448 #define _LOAD_SPECIAL LOAD_SPECIAL #define _LOAD_SUPER_ATTR_ATTR LOAD_SUPER_ATTR_ATTR #define _LOAD_SUPER_ATTR_METHOD LOAD_SUPER_ATTR_METHOD -#define _MAKE_CALLARGS_A_TUPLE 448 +#define _MAKE_CALLARGS_A_TUPLE 449 #define _MAKE_CELL MAKE_CELL #define _MAKE_FUNCTION MAKE_FUNCTION -#define _MAKE_WARM 449 +#define _MAKE_WARM 450 #define _MAP_ADD MAP_ADD #define _MATCH_CLASS MATCH_CLASS #define _MATCH_KEYS MATCH_KEYS #define _MATCH_MAPPING MATCH_MAPPING #define _MATCH_SEQUENCE MATCH_SEQUENCE -#define _MAYBE_EXPAND_METHOD 450 -#define _MAYBE_EXPAND_METHOD_KW 451 -#define _MONITOR_CALL 452 -#define _MONITOR_JUMP_BACKWARD 453 -#define _MONITOR_RESUME 454 +#define _MAYBE_EXPAND_METHOD 451 +#define _MAYBE_EXPAND_METHOD_KW 452 +#define _MONITOR_CALL 453 +#define _MONITOR_JUMP_BACKWARD 454 +#define _MONITOR_RESUME 455 #define _NOP NOP #define _POP_EXCEPT POP_EXCEPT -#define _POP_JUMP_IF_FALSE 455 -#define _POP_JUMP_IF_TRUE 456 +#define _POP_JUMP_IF_FALSE 456 +#define _POP_JUMP_IF_TRUE 457 #define _POP_TOP POP_TOP -#define _POP_TOP_LOAD_CONST_INLINE_BORROW 457 +#define _POP_TOP_LOAD_CONST_INLINE_BORROW 458 #define _PUSH_EXC_INFO PUSH_EXC_INFO -#define _PUSH_FRAME 458 +#define _PUSH_FRAME 459 #define _PUSH_NULL PUSH_NULL -#define _PY_FRAME_GENERAL 459 -#define _PY_FRAME_KW 460 -#define _QUICKEN_RESUME 461 -#define _REPLACE_WITH_TRUE 462 +#define _PY_FRAME_GENERAL 460 +#define _PY_FRAME_KW 461 +#define _QUICKEN_RESUME 462 +#define _REPLACE_WITH_TRUE 463 #define _RESUME_CHECK RESUME_CHECK #define _RETURN_GENERATOR RETURN_GENERATOR #define _RETURN_VALUE RETURN_VALUE -#define _SAVE_RETURN_OFFSET 463 -#define _SEND 464 -#define _SEND_GEN_FRAME 465 +#define _SAVE_RETURN_OFFSET 464 +#define _SEND 465 +#define _SEND_GEN_FRAME 466 #define _SETUP_ANNOTATIONS SETUP_ANNOTATIONS #define _SET_ADD SET_ADD #define _SET_FUNCTION_ATTRIBUTE SET_FUNCTION_ATTRIBUTE #define _SET_UPDATE SET_UPDATE -#define _START_EXECUTOR 466 -#define _STORE_ATTR 467 -#define _STORE_ATTR_INSTANCE_VALUE 468 -#define _STORE_ATTR_SLOT 469 -#define _STORE_ATTR_WITH_HINT 470 +#define _START_EXECUTOR 467 +#define _STORE_ATTR 468 +#define _STORE_ATTR_INSTANCE_VALUE 469 +#define _STORE_ATTR_SLOT 470 +#define _STORE_ATTR_WITH_HINT 471 #define _STORE_DEREF STORE_DEREF -#define _STORE_FAST 471 -#define _STORE_FAST_0 472 -#define _STORE_FAST_1 473 -#define _STORE_FAST_2 474 -#define _STORE_FAST_3 475 -#define _STORE_FAST_4 476 -#define _STORE_FAST_5 477 -#define _STORE_FAST_6 478 -#define _STORE_FAST_7 479 +#define _STORE_FAST 472 +#define _STORE_FAST_0 473 +#define _STORE_FAST_1 474 +#define _STORE_FAST_2 475 +#define _STORE_FAST_3 476 +#define _STORE_FAST_4 477 +#define _STORE_FAST_5 478 +#define _STORE_FAST_6 479 +#define _STORE_FAST_7 480 #define _STORE_FAST_LOAD_FAST STORE_FAST_LOAD_FAST #define _STORE_FAST_STORE_FAST STORE_FAST_STORE_FAST #define _STORE_GLOBAL STORE_GLOBAL #define _STORE_NAME STORE_NAME -#define _STORE_SLICE 480 -#define _STORE_SUBSCR 481 +#define _STORE_SLICE 481 +#define _STORE_SUBSCR 482 #define _STORE_SUBSCR_DICT STORE_SUBSCR_DICT #define _STORE_SUBSCR_LIST_INT STORE_SUBSCR_LIST_INT #define _SWAP SWAP -#define _TIER2_RESUME_CHECK 482 -#define _TO_BOOL 483 +#define _TIER2_RESUME_CHECK 483 +#define _TO_BOOL 484 #define _TO_BOOL_BOOL TO_BOOL_BOOL #define _TO_BOOL_INT TO_BOOL_INT #define _TO_BOOL_LIST TO_BOOL_LIST @@ -302,13 +303,13 @@ extern "C" { #define _UNARY_NEGATIVE UNARY_NEGATIVE #define _UNARY_NOT UNARY_NOT #define _UNPACK_EX UNPACK_EX -#define _UNPACK_SEQUENCE 484 +#define _UNPACK_SEQUENCE 485 #define _UNPACK_SEQUENCE_LIST UNPACK_SEQUENCE_LIST #define _UNPACK_SEQUENCE_TUPLE UNPACK_SEQUENCE_TUPLE #define _UNPACK_SEQUENCE_TWO_TUPLE UNPACK_SEQUENCE_TWO_TUPLE #define _WITH_EXCEPT_START WITH_EXCEPT_START #define _YIELD_VALUE YIELD_VALUE -#define MAX_UOP_ID 484 +#define MAX_UOP_ID 485 #ifdef __cplusplus } diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index 4cfdecec78b0db..ade297201f0ac2 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -289,7 +289,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_FATAL_ERROR] = 0, [_CHECK_VALIDITY_AND_SET_IP] = HAS_DEOPT_FLAG, [_DEOPT] = 0, - [_ERROR_POP_N] = HAS_ARG_FLAG, + [_ERROR_POP_N] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG, [_TIER2_RESUME_CHECK] = HAS_DEOPT_FLAG, }; diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 7c1ef42a4970d7..2ad267e3e08f0f 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -1274,6 +1274,11 @@ def requires_specialization(test): _opcode.ENABLE_SPECIALIZATION, "requires specialization")(test) +def requires_specialization_ft(test): + return unittest.skipUnless( + _opcode.ENABLE_SPECIALIZATION_FT, "requires specialization")(test) + + #======================================================================= # Check for the presence of docstrings. diff --git a/Lib/test/test_capi/test_config.py b/Lib/test/test_capi/test_config.py index 71fb9ae45c7c30..77730ad2f32085 100644 --- a/Lib/test/test_capi/test_config.py +++ b/Lib/test/test_capi/test_config.py @@ -100,6 +100,7 @@ def test_config_get(self): options.append(("run_presite", str | None, None)) if sysconfig.get_config_var('Py_GIL_DISABLED'): options.append(("enable_gil", int, None)) + options.append(("tlbc_enabled", int, None)) if support.MS_WINDOWS: options.extend(( ("legacy_windows_stdio", bool, None), diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index f1ab72180d714d..c352325ff3d08a 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -7,7 +7,8 @@ import _opcode -from test.support import script_helper, requires_specialization, import_helper +from test.support import (script_helper, requires_specialization, + import_helper, Py_GIL_DISABLED) _testinternalcapi = import_helper.import_module("_testinternalcapi") @@ -34,6 +35,7 @@ def clear_executors(func): @requires_specialization +@unittest.skipIf(Py_GIL_DISABLED, "optimizer not yet supported in free-threaded builds") @unittest.skipUnless(hasattr(_testinternalcapi, "get_optimizer"), "Requires optimizer infrastructure") class TestOptimizerAPI(unittest.TestCase): @@ -138,6 +140,7 @@ def get_opnames(ex): @requires_specialization +@unittest.skipIf(Py_GIL_DISABLED, "optimizer not yet supported in free-threaded builds") @unittest.skipUnless(hasattr(_testinternalcapi, "get_optimizer"), "Requires optimizer infrastructure") class TestExecutorInvalidation(unittest.TestCase): @@ -219,6 +222,7 @@ def f(): @requires_specialization +@unittest.skipIf(Py_GIL_DISABLED, "optimizer not yet supported in free-threaded builds") @unittest.skipUnless(hasattr(_testinternalcapi, "get_optimizer"), "Requires optimizer infrastructure") @unittest.skipIf(os.getenv("PYTHON_UOPS_OPTIMIZE") == "0", "Needs uop optimizer to run.") @@ -586,6 +590,7 @@ def testfunc(n): @requires_specialization +@unittest.skipIf(Py_GIL_DISABLED, "optimizer not yet supported in free-threaded builds") @unittest.skipUnless(hasattr(_testinternalcapi, "get_optimizer"), "Requires optimizer infrastructure") @unittest.skipIf(os.getenv("PYTHON_UOPS_OPTIMIZE") == "0", "Needs uop optimizer to run.") diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py index eca9adf9a7dcbc..634efda354407f 100644 --- a/Lib/test/test_cmd_line.py +++ b/Lib/test/test_cmd_line.py @@ -12,6 +12,7 @@ from test import support from test.support import os_helper from test.support import force_not_colorized +from test.support import threading_helper from test.support.script_helper import ( spawn_python, kill_python, assert_python_ok, assert_python_failure, interpreter_requires_environment @@ -1068,6 +1069,57 @@ def res2int(self, res): out = res.out.strip().decode("utf-8") return tuple(int(i) for i in out.split()) + @unittest.skipUnless(support.Py_GIL_DISABLED, + "PYTHON_TLBC and -X tlbc" + " only supported in Py_GIL_DISABLED builds") + @threading_helper.requires_working_threading() + def test_disable_thread_local_bytecode(self): + code = """if 1: + import threading + def test(x, y): + return x + y + t = threading.Thread(target=test, args=(1,2)) + t.start() + t.join()""" + assert_python_ok("-W", "always", "-X", "tlbc=0", "-c", code) + assert_python_ok("-W", "always", "-c", code, PYTHON_TLBC="0") + + @unittest.skipUnless(support.Py_GIL_DISABLED, + "PYTHON_TLBC and -X tlbc" + " only supported in Py_GIL_DISABLED builds") + @threading_helper.requires_working_threading() + def test_enable_thread_local_bytecode(self): + code = """if 1: + import threading + def test(x, y): + return x + y + t = threading.Thread(target=test, args=(1,2)) + t.start() + t.join()""" + # The functionality of thread-local bytecode is tested more extensively + # in test_thread_local_bytecode + assert_python_ok("-W", "always", "-X", "tlbc=1", "-c", code) + assert_python_ok("-W", "always", "-c", code, PYTHON_TLBC="1") + + @unittest.skipUnless(support.Py_GIL_DISABLED, + "PYTHON_TLBC and -X tlbc" + " only supported in Py_GIL_DISABLED builds") + def test_invalid_thread_local_bytecode(self): + rc, out, err = assert_python_failure("-X", "tlbc") + self.assertIn(b"tlbc=n: n is missing or invalid", err) + rc, out, err = assert_python_failure("-X", "tlbc=foo") + self.assertIn(b"tlbc=n: n is missing or invalid", err) + rc, out, err = assert_python_failure("-X", "tlbc=-1") + self.assertIn(b"tlbc=n: n is missing or invalid", err) + rc, out, err = assert_python_failure("-X", "tlbc=2") + self.assertIn(b"tlbc=n: n is missing or invalid", err) + rc, out, err = assert_python_failure(PYTHON_TLBC="foo") + self.assertIn(b"PYTHON_TLBC=N: N is missing or invalid", err) + rc, out, err = assert_python_failure(PYTHON_TLBC="-1") + self.assertIn(b"PYTHON_TLBC=N: N is missing or invalid", err) + rc, out, err = assert_python_failure(PYTHON_TLBC="2") + self.assertIn(b"PYTHON_TLBC=N: N is missing or invalid", err) + @unittest.skipIf(interpreter_requires_environment(), 'Cannot run -I tests when PYTHON env vars are required.') diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 3c6570afa50d45..a991c67fca46be 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -10,7 +10,8 @@ import types import unittest from test.support import (captured_stdout, requires_debug_ranges, - requires_specialization, cpython_only) + requires_specialization, requires_specialization_ft, + cpython_only) from test.support.bytecode_helper import BytecodeTestCase import opcode @@ -1261,7 +1262,7 @@ def test_super_instructions(self): self.do_disassembly_compare(got, dis_load_test_quickened_code) @cpython_only - @requires_specialization + @requires_specialization_ft def test_binary_specialize(self): binary_op_quicken = """\ 0 RESUME_CHECK 0 @@ -1281,6 +1282,9 @@ def test_binary_specialize(self): got = self.get_disassembly(co_unicode, adaptive=True) self.do_disassembly_compare(got, binary_op_quicken % "BINARY_OP_ADD_UNICODE 0 (+)") + @cpython_only + @requires_specialization + def test_binary_subscr_specialize(self): binary_subscr_quicken = """\ 0 RESUME_CHECK 0 diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py index 5e886b6c8c38ec..bf861ef06ee2d3 100644 --- a/Lib/test/test_embed.py +++ b/Lib/test/test_embed.py @@ -644,6 +644,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase): CONFIG_COMPAT['run_presite'] = None if support.Py_GIL_DISABLED: CONFIG_COMPAT['enable_gil'] = -1 + CONFIG_COMPAT['tlbc_enabled'] = GET_DEFAULT_CONFIG if MS_WINDOWS: CONFIG_COMPAT.update({ 'legacy_windows_stdio': False, diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index c0862d7d15f39e..d839893d2c657e 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1094,7 +1094,14 @@ def test_getallocatedblocks(self): # While we could imagine a Python session where the number of # multiple buffer objects would exceed the sharing of references, # it is unlikely to happen in a normal test run. - self.assertLess(a, sys.gettotalrefcount()) + # + # In free-threaded builds each code object owns an array of + # pointers to copies of the bytecode. When the number of + # code objects is a large fraction of the total number of + # references, this can cause the total number of allocated + # blocks to exceed the total number of references. + if not support.Py_GIL_DISABLED: + self.assertLess(a, sys.gettotalrefcount()) except AttributeError: # gettotalrefcount() not available pass @@ -1613,7 +1620,10 @@ class C(object): pass def func(): return sys._getframe() x = func() - INTERPRETER_FRAME = '9PhcP' + if support.Py_GIL_DISABLED: + INTERPRETER_FRAME = '10PhcP' + else: + INTERPRETER_FRAME = '9PhcP' check(x, size('3PiccPP' + INTERPRETER_FRAME + 'P')) # function def func(): pass diff --git a/Lib/test/test_thread_local_bytecode.py b/Lib/test/test_thread_local_bytecode.py new file mode 100644 index 00000000000000..7a8809c5ae7697 --- /dev/null +++ b/Lib/test/test_thread_local_bytecode.py @@ -0,0 +1,198 @@ +"""Tests for thread-local bytecode.""" +import dis +import textwrap +import unittest + +from test import support +from test.support import cpython_only, import_helper, requires_specialization_ft +from test.support.script_helper import assert_python_ok +from test.support.threading_helper import requires_working_threading + +# Skip this test if the _testinternalcapi module isn't available +_testinternalcapi = import_helper.import_module("_testinternalcapi") + + +@cpython_only +@requires_working_threading() +@unittest.skipUnless(support.Py_GIL_DISABLED, "only in free-threaded builds") +class TLBCTests(unittest.TestCase): + @requires_specialization_ft + def test_new_threads_start_with_unspecialized_code(self): + code = textwrap.dedent(""" + import dis + import queue + import threading + + from _testinternalcapi import get_tlbc + + def all_opnames(bc): + return {i.opname for i in dis._get_instructions_bytes(bc)} + + def f(a, b, q=None): + if q is not None: + q.put(get_tlbc(f)) + return a + b + + for _ in range(100): + # specialize + f(1, 2) + + q = queue.Queue() + t = threading.Thread(target=f, args=('a', 'b', q)) + t.start() + t.join() + + assert "BINARY_OP_ADD_INT" in all_opnames(get_tlbc(f)) + assert "BINARY_OP_ADD_INT" not in all_opnames(q.get()) + """) + assert_python_ok("-X", "tlbc=1", "-c", code) + + @requires_specialization_ft + def test_threads_specialize_independently(self): + code = textwrap.dedent(""" + import dis + import queue + import threading + + from _testinternalcapi import get_tlbc + + def all_opnames(bc): + return {i.opname for i in dis._get_instructions_bytes(bc)} + + def f(a, b): + return a + b + + def g(a, b, q=None): + for _ in range(100): + f(a, b) + if q is not None: + q.put(get_tlbc(f)) + + # specialize in main thread + g(1, 2) + + # specialize in other thread + q = queue.Queue() + t = threading.Thread(target=g, args=('a', 'b', q)) + t.start() + t.join() + + assert "BINARY_OP_ADD_INT" in all_opnames(get_tlbc(f)) + t_opnames = all_opnames(q.get()) + assert "BINARY_OP_ADD_INT" not in t_opnames + assert "BINARY_OP_ADD_UNICODE" in t_opnames + """) + assert_python_ok("-X", "tlbc=1", "-c", code) + + def test_reuse_tlbc_across_threads_different_lifetimes(self): + code = textwrap.dedent(""" + import queue + import threading + + from _testinternalcapi import get_tlbc_id + + def f(a, b, q=None): + if q is not None: + q.put(get_tlbc_id(f)) + return a + b + + q = queue.Queue() + tlbc_ids = [] + for _ in range(3): + t = threading.Thread(target=f, args=('a', 'b', q)) + t.start() + t.join() + tlbc_ids.append(q.get()) + + assert tlbc_ids[0] == tlbc_ids[1] + assert tlbc_ids[1] == tlbc_ids[2] + """) + assert_python_ok("-X", "tlbc=1", "-c", code) + + def test_no_copies_if_tlbc_disabled(self): + code = textwrap.dedent(""" + import queue + import threading + + from _testinternalcapi import get_tlbc_id + + def f(a, b, q=None): + if q is not None: + q.put(get_tlbc_id(f)) + return a + b + + q = queue.Queue() + threads = [] + for _ in range(3): + t = threading.Thread(target=f, args=('a', 'b', q)) + t.start() + threads.append(t) + + tlbc_ids = [] + for t in threads: + t.join() + tlbc_ids.append(q.get()) + + main_tlbc_id = get_tlbc_id(f) + assert main_tlbc_id is not None + assert tlbc_ids[0] == main_tlbc_id + assert tlbc_ids[1] == main_tlbc_id + assert tlbc_ids[2] == main_tlbc_id + """) + assert_python_ok("-X", "tlbc=0", "-c", code) + + def test_no_specialization_if_tlbc_disabled(self): + code = textwrap.dedent(""" + import dis + import queue + import threading + + from _testinternalcapi import get_tlbc + + def all_opnames(f): + bc = get_tlbc(f) + return {i.opname for i in dis._get_instructions_bytes(bc)} + + def f(a, b): + return a + b + + for _ in range(100): + f(1, 2) + + assert "BINARY_OP_ADD_INT" not in all_opnames(f) + """) + assert_python_ok("-X", "tlbc=0", "-c", code) + + def test_generator_throw(self): + code = textwrap.dedent(""" + import queue + import threading + + from _testinternalcapi import get_tlbc_id + + def g(): + try: + yield + except: + yield get_tlbc_id(g) + + def f(q): + gen = g() + next(gen) + q.put(gen.throw(ValueError)) + + q = queue.Queue() + t = threading.Thread(target=f, args=(q,)) + t.start() + t.join() + + gen = g() + next(gen) + main_id = gen.throw(ValueError) + assert main_id != q.get() + """) + assert_python_ok("-X", "tlbc=1", "-c", code) + + +if __name__ == "__main__": + unittest.main() diff --git a/Makefile.pre.in b/Makefile.pre.in index 1a9191ec0ce48f..c650ecaf7be137 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -460,6 +460,7 @@ PYTHON_OBJS= \ Python/hashtable.o \ Python/import.o \ Python/importdl.o \ + Python/index_pool.o \ Python/initconfig.o \ Python/interpconfig.o \ Python/instrumentation.o \ @@ -1228,6 +1229,7 @@ PYTHON_HEADERS= \ $(srcdir)/Include/internal/pycore_hashtable.h \ $(srcdir)/Include/internal/pycore_import.h \ $(srcdir)/Include/internal/pycore_importdl.h \ + $(srcdir)/Include/internal/pycore_index_pool.h \ $(srcdir)/Include/internal/pycore_initconfig.h \ $(srcdir)/Include/internal/pycore_instruments.h \ $(srcdir)/Include/internal/pycore_instruction_sequence.h \ diff --git a/Modules/_opcode.c b/Modules/_opcode.c index dc93063aee7e54..7ccf7af6bf908f 100644 --- a/Modules/_opcode.c +++ b/Modules/_opcode.c @@ -422,6 +422,9 @@ _opcode_exec(PyObject *m) { if (PyModule_AddIntMacro(m, ENABLE_SPECIALIZATION) < 0) { return -1; } + if (PyModule_AddIntMacro(m, ENABLE_SPECIALIZATION_FT) < 0) { + return -1; + } return 0; } diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index eb98b433c6c6af..883f32599fbc99 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -14,6 +14,7 @@ #include "pycore_bitutils.h" // _Py_bswap32() #include "pycore_bytesobject.h" // _PyBytes_Find() #include "pycore_ceval.h" // _PyEval_AddPendingCall() +#include "pycore_code.h" // _PyCode_GetTLBCFast() #include "pycore_compile.h" // _PyCompile_CodeGen() #include "pycore_context.h" // _PyContext_NewHamtForTests() #include "pycore_dict.h" // _PyManagedDictPointer_GetValues() @@ -1963,6 +1964,48 @@ get_py_thread_id(PyObject *self, PyObject *Py_UNUSED(ignored)) Py_BUILD_ASSERT(sizeof(unsigned long long) >= sizeof(tid)); return PyLong_FromUnsignedLongLong(tid); } + +static PyCodeObject * +get_code(PyObject *obj) +{ + if (PyCode_Check(obj)) { + return (PyCodeObject *)obj; + } + else if (PyFunction_Check(obj)) { + return (PyCodeObject *)PyFunction_GetCode(obj); + } + return (PyCodeObject *)PyErr_Format( + PyExc_TypeError, "expected function or code object, got %s", + Py_TYPE(obj)->tp_name); +} + +static PyObject * +get_tlbc(PyObject *Py_UNUSED(module), PyObject *obj) +{ + PyCodeObject *code = get_code(obj); + if (code == NULL) { + return NULL; + } + _Py_CODEUNIT *bc = _PyCode_GetTLBCFast(PyThreadState_GET(), code); + if (bc == NULL) { + Py_RETURN_NONE; + } + return PyBytes_FromStringAndSize((const char *)bc, _PyCode_NBYTES(code)); +} + +static PyObject * +get_tlbc_id(PyObject *Py_UNUSED(module), PyObject *obj) +{ + PyCodeObject *code = get_code(obj); + if (code == NULL) { + return NULL; + } + _Py_CODEUNIT *bc = _PyCode_GetTLBCFast(PyThreadState_GET(), code); + if (bc == NULL) { + Py_RETURN_NONE; + } + return PyLong_FromVoidPtr(bc); +} #endif static PyObject * @@ -2022,7 +2065,6 @@ identify_type_slot_wrappers(PyObject *self, PyObject *Py_UNUSED(ignored)) return _PyType_GetSlotWrapperNames(); } - static PyMethodDef module_functions[] = { {"get_configs", get_configs, METH_NOARGS}, {"get_recursion_depth", get_recursion_depth, METH_NOARGS}, @@ -2110,6 +2152,8 @@ static PyMethodDef module_functions[] = { #ifdef Py_GIL_DISABLED {"py_thread_id", get_py_thread_id, METH_NOARGS}, + {"get_tlbc", get_tlbc, METH_O, NULL}, + {"get_tlbc_id", get_tlbc_id, METH_O, NULL}, #endif #ifdef _Py_TIER2 {"uop_symbols_test", _Py_uop_symbols_test, METH_NOARGS}, diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 775ea7aca824c4..1cf9740af9a209 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -6,17 +6,22 @@ #include "pycore_code.h" // _PyCodeConstructor #include "pycore_frame.h" // FRAME_SPECIALS_SIZE #include "pycore_hashtable.h" // _Py_hashtable_t +#include "pycore_index_pool.h" // _PyIndexPool #include "pycore_initconfig.h" // _PyStatus_OK() #include "pycore_interp.h" // PyInterpreterState.co_extra_freefuncs #include "pycore_object.h" // _PyObject_SetDeferredRefcount +#include "pycore_object_stack.h" #include "pycore_opcode_metadata.h" // _PyOpcode_Deopt, _PyOpcode_Caches #include "pycore_opcode_utils.h" // RESUME_AT_FUNC_START +#include "pycore_pymem.h" // _PyMem_FreeDelayed #include "pycore_pystate.h" // _PyInterpreterState_GET() #include "pycore_setobject.h" // _PySet_NextEntry() #include "pycore_tuple.h" // _PyTuple_ITEMS() #include "pycore_uniqueid.h" // _PyObject_AssignUniqueId() #include "clinic/codeobject.c.h" +#define INITIAL_SPECIALIZED_CODE_SIZE 16 + static const char * code_event_name(PyCodeEvent event) { switch (event) { @@ -440,9 +445,15 @@ _PyCode_Validate(struct _PyCodeConstructor *con) return 0; } -extern void _PyCode_Quicken(PyCodeObject *code); +extern void +_PyCode_Quicken(_Py_CODEUNIT *instructions, Py_ssize_t size, PyObject *consts, + int enable_counters); -static void +#ifdef Py_GIL_DISABLED +static _PyCodeArray * _PyCodeArray_New(Py_ssize_t size); +#endif + +static int init_code(PyCodeObject *co, struct _PyCodeConstructor *con) { int nlocalsplus = (int)PyTuple_GET_SIZE(con->localsplusnames); @@ -505,14 +516,27 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con) memcpy(_PyCode_CODE(co), PyBytes_AS_STRING(con->code), PyBytes_GET_SIZE(con->code)); +#ifdef Py_GIL_DISABLED + co->co_tlbc = _PyCodeArray_New(INITIAL_SPECIALIZED_CODE_SIZE); + if (co->co_tlbc == NULL) { + return -1; + } + co->co_tlbc->entries[0] = co->co_code_adaptive; +#endif int entry_point = 0; while (entry_point < Py_SIZE(co) && _PyCode_CODE(co)[entry_point].op.code != RESUME) { entry_point++; } co->_co_firsttraceable = entry_point; - _PyCode_Quicken(co); +#ifdef Py_GIL_DISABLED + _PyCode_Quicken(_PyCode_CODE(co), Py_SIZE(co), co->co_consts, + interp->config.tlbc_enabled); +#else + _PyCode_Quicken(_PyCode_CODE(co), Py_SIZE(co), co->co_consts, 1); +#endif notify_code_watchers(PY_CODE_EVENT_CREATE, co); + return 0; } static int @@ -667,7 +691,12 @@ _PyCode_New(struct _PyCodeConstructor *con) PyErr_NoMemory(); return NULL; } - init_code(co, con); + + if (init_code(co, con) < 0) { + Py_DECREF(co); + return NULL; + } + #ifdef Py_GIL_DISABLED co->_co_unique_id = _PyObject_AssignUniqueId((PyObject *)co); _PyObject_GC_TRACK(co); @@ -1871,6 +1900,17 @@ code_dealloc(PyCodeObject *co) PyObject_ClearWeakRefs((PyObject*)co); } free_monitoring_data(co->_co_monitoring); +#ifdef Py_GIL_DISABLED + // The first element always points to the mutable bytecode at the end of + // the code object, which will be freed when the code object is freed. + for (Py_ssize_t i = 1; i < co->co_tlbc->size; i++) { + char *entry = co->co_tlbc->entries[i]; + if (entry != NULL) { + PyMem_Free(entry); + } + } + PyMem_Free(co->co_tlbc); +#endif PyObject_Free(co); } @@ -2646,5 +2686,270 @@ _PyCode_Fini(PyInterpreterState *interp) _Py_hashtable_destroy(state->constants); state->constants = NULL; } + _PyIndexPool_Fini(&interp->tlbc_indices); #endif } + +#ifdef Py_GIL_DISABLED + +// Thread-local bytecode (TLBC) +// +// Each thread specializes a thread-local copy of the bytecode, created on the +// first RESUME, in free-threaded builds. All copies of the bytecode for a code +// object are stored in the `co_tlbc` array. Threads reserve a globally unique +// index identifying its copy of the bytecode in all `co_tlbc` arrays at thread +// creation and release the index at thread destruction. The first entry in +// every `co_tlbc` array always points to the "main" copy of the bytecode that +// is stored at the end of the code object. This ensures that no bytecode is +// copied for programs that do not use threads. +// +// Thread-local bytecode can be disabled at runtime by providing either `-X +// tlbc=0` or `PYTHON_TLBC=0`. Disabling thread-local bytecode also disables +// specialization. All threads share the main copy of the bytecode when +// thread-local bytecode is disabled. +// +// Concurrent modifications to the bytecode made by the specializing +// interpreter and instrumentation use atomics, with specialization taking care +// not to overwrite an instruction that was instrumented concurrently. + +int32_t +_Py_ReserveTLBCIndex(PyInterpreterState *interp) +{ + if (interp->config.tlbc_enabled) { + return _PyIndexPool_AllocIndex(&interp->tlbc_indices); + } + // All threads share the main copy of the bytecode when TLBC is disabled + return 0; +} + +void +_Py_ClearTLBCIndex(_PyThreadStateImpl *tstate) +{ + PyInterpreterState *interp = ((PyThreadState *)tstate)->interp; + if (interp->config.tlbc_enabled) { + _PyIndexPool_FreeIndex(&interp->tlbc_indices, tstate->tlbc_index); + } +} + +static _PyCodeArray * +_PyCodeArray_New(Py_ssize_t size) +{ + _PyCodeArray *arr = PyMem_Calloc( + 1, offsetof(_PyCodeArray, entries) + sizeof(void *) * size); + if (arr == NULL) { + PyErr_NoMemory(); + return NULL; + } + arr->size = size; + return arr; +} + +static void +copy_code(_Py_CODEUNIT *dst, PyCodeObject *co) +{ + int code_len = (int) Py_SIZE(co); + for (int i = 0; i < code_len; i += _PyInstruction_GetLength(co, i)) { + dst[i] = _Py_GetBaseCodeUnit(co, i); + } + _PyCode_Quicken(dst, code_len, co->co_consts, 1); +} + +static Py_ssize_t +get_pow2_greater(Py_ssize_t initial, Py_ssize_t limit) +{ + // initial must be a power of two + assert(!(initial & (initial - 1))); + Py_ssize_t res = initial; + while (res && res < limit) { + res <<= 1; + } + return res; +} + +static _Py_CODEUNIT * +create_tlbc_lock_held(PyCodeObject *co, Py_ssize_t idx) +{ + _PyCodeArray *tlbc = co->co_tlbc; + if (idx >= tlbc->size) { + Py_ssize_t new_size = get_pow2_greater(tlbc->size, idx + 1); + if (!new_size) { + PyErr_NoMemory(); + return NULL; + } + _PyCodeArray *new_tlbc = _PyCodeArray_New(new_size); + if (new_tlbc == NULL) { + return NULL; + } + memcpy(new_tlbc->entries, tlbc->entries, tlbc->size * sizeof(void *)); + _Py_atomic_store_ptr_release(&co->co_tlbc, new_tlbc); + _PyMem_FreeDelayed(tlbc); + tlbc = new_tlbc; + } + char *bc = PyMem_Calloc(1, _PyCode_NBYTES(co)); + if (bc == NULL) { + PyErr_NoMemory(); + return NULL; + } + copy_code((_Py_CODEUNIT *) bc, co); + assert(tlbc->entries[idx] == NULL); + tlbc->entries[idx] = bc; + return (_Py_CODEUNIT *) bc; +} + +static _Py_CODEUNIT * +get_tlbc_lock_held(PyCodeObject *co) +{ + _PyCodeArray *tlbc = co->co_tlbc; + _PyThreadStateImpl *tstate = (_PyThreadStateImpl *)PyThreadState_GET(); + int32_t idx = tstate->tlbc_index; + if (idx < tlbc->size && tlbc->entries[idx] != NULL) { + return (_Py_CODEUNIT *)tlbc->entries[idx]; + } + return create_tlbc_lock_held(co, idx); +} + +_Py_CODEUNIT * +_PyCode_GetTLBC(PyCodeObject *co) +{ + _Py_CODEUNIT *result; + Py_BEGIN_CRITICAL_SECTION(co); + result = get_tlbc_lock_held(co); + Py_END_CRITICAL_SECTION(); + return result; +} + +// My kingdom for a bitset +struct flag_set { + uint8_t *flags; + Py_ssize_t size; +}; + +static inline int +flag_is_set(struct flag_set *flags, Py_ssize_t idx) +{ + assert(idx >= 0); + return (idx < flags->size) && flags->flags[idx]; +} + +// Set the flag for each tlbc index in use +static int +get_indices_in_use(PyInterpreterState *interp, struct flag_set *in_use) +{ + assert(interp->stoptheworld.world_stopped); + assert(in_use->flags == NULL); + int32_t max_index = 0; + for (PyThreadState *p = interp->threads.head; p != NULL; p = p->next) { + int32_t idx = ((_PyThreadStateImpl *) p)->tlbc_index; + if (idx > max_index) { + max_index = idx; + } + } + in_use->size = (size_t) max_index + 1; + in_use->flags = PyMem_Calloc(in_use->size, sizeof(*in_use->flags)); + if (in_use->flags == NULL) { + return -1; + } + for (PyThreadState *p = interp->threads.head; p != NULL; p = p->next) { + in_use->flags[((_PyThreadStateImpl *) p)->tlbc_index] = 1; + } + return 0; +} + +struct get_code_args { + _PyObjectStack code_objs; + struct flag_set indices_in_use; + int err; +}; + +static void +clear_get_code_args(struct get_code_args *args) +{ + if (args->indices_in_use.flags != NULL) { + PyMem_Free(args->indices_in_use.flags); + args->indices_in_use.flags = NULL; + } + _PyObjectStack_Clear(&args->code_objs); +} + +static inline int +is_bytecode_unused(_PyCodeArray *tlbc, Py_ssize_t idx, + struct flag_set *indices_in_use) +{ + assert(idx > 0 && idx < tlbc->size); + return tlbc->entries[idx] != NULL && !flag_is_set(indices_in_use, idx); +} + +static int +get_code_with_unused_tlbc(PyObject *obj, struct get_code_args *args) +{ + if (!PyCode_Check(obj)) { + return 1; + } + PyCodeObject *co = (PyCodeObject *) obj; + _PyCodeArray *tlbc = co->co_tlbc; + // The first index always points at the main copy of the bytecode embedded + // in the code object. + for (Py_ssize_t i = 1; i < tlbc->size; i++) { + if (is_bytecode_unused(tlbc, i, &args->indices_in_use)) { + if (_PyObjectStack_Push(&args->code_objs, obj) < 0) { + args->err = -1; + return 0; + } + return 1; + } + } + return 1; +} + +static void +free_unused_bytecode(PyCodeObject *co, struct flag_set *indices_in_use) +{ + _PyCodeArray *tlbc = co->co_tlbc; + // The first index always points at the main copy of the bytecode embedded + // in the code object. + for (Py_ssize_t i = 1; i < tlbc->size; i++) { + if (is_bytecode_unused(tlbc, i, indices_in_use)) { + PyMem_Free(tlbc->entries[i]); + tlbc->entries[i] = NULL; + } + } +} + +int +_Py_ClearUnusedTLBC(PyInterpreterState *interp) +{ + struct get_code_args args = { + .code_objs = {NULL}, + .indices_in_use = {NULL, 0}, + .err = 0, + }; + _PyEval_StopTheWorld(interp); + // Collect in-use tlbc indices + if (get_indices_in_use(interp, &args.indices_in_use) < 0) { + goto err; + } + // Collect code objects that have bytecode not in use by any thread + _PyGC_VisitObjectsWorldStopped( + interp, (gcvisitobjects_t)get_code_with_unused_tlbc, &args); + if (args.err < 0) { + goto err; + } + // Free unused bytecode. This must happen outside of gc_visit_heaps; it is + // unsafe to allocate or free any mimalloc managed memory when it's + // running. + PyObject *obj; + while ((obj = _PyObjectStack_Pop(&args.code_objs)) != NULL) { + free_unused_bytecode((PyCodeObject*) obj, &args.indices_in_use); + } + _PyEval_StartTheWorld(interp); + clear_get_code_args(&args); + return 0; + +err: + _PyEval_StartTheWorld(interp); + clear_get_code_args(&args); + PyErr_NoMemory(); + return -1; +} + +#endif diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 55394afa523213..c743c254848d3a 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -1651,7 +1651,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore } /* Finally set the new lasti and return OK. */ f->f_lineno = 0; - f->f_frame->instr_ptr = _PyCode_CODE(code) + best_addr; + f->f_frame->instr_ptr = _PyFrame_GetBytecode(f->f_frame) + best_addr; return 0; } @@ -1867,10 +1867,11 @@ PyTypeObject PyFrame_Type = { }; static void -init_frame(_PyInterpreterFrame *frame, PyFunctionObject *func, PyObject *locals) +init_frame(PyThreadState *tstate, _PyInterpreterFrame *frame, + PyFunctionObject *func, PyObject *locals) { PyCodeObject *code = (PyCodeObject *)func->func_code; - _PyFrame_Initialize(frame, PyStackRef_FromPyObjectNew(func), + _PyFrame_Initialize(tstate, frame, PyStackRef_FromPyObjectNew(func), Py_XNewRef(locals), code, 0, NULL); } @@ -1922,7 +1923,7 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, Py_DECREF(func); return NULL; } - init_frame((_PyInterpreterFrame *)f->_f_frame_data, func, locals); + init_frame(tstate, (_PyInterpreterFrame *)f->_f_frame_data, func, locals); f->f_frame = (_PyInterpreterFrame *)f->_f_frame_data; f->f_frame->owner = FRAME_OWNED_BY_FRAME_OBJECT; // This frame needs to be "complete", so pretend that the first RESUME ran: @@ -1941,7 +1942,8 @@ frame_init_get_vars(_PyInterpreterFrame *frame) // here: PyCodeObject *co = _PyFrame_GetCode(frame); int lasti = _PyInterpreterFrame_LASTI(frame); - if (!(lasti < 0 && _PyCode_CODE(co)->op.code == COPY_FREE_VARS + if (!(lasti < 0 + && _PyFrame_GetBytecode(frame)->op.code == COPY_FREE_VARS && PyStackRef_FunctionCheck(frame->f_funcobj))) { /* Free vars are initialized */ @@ -1957,7 +1959,7 @@ frame_init_get_vars(_PyInterpreterFrame *frame) frame->localsplus[offset + i] = PyStackRef_FromPyObjectNew(o); } // COPY_FREE_VARS doesn't have inline CACHEs, either: - frame->instr_ptr = _PyCode_CODE(_PyFrame_GetCode(frame)); + frame->instr_ptr = _PyFrame_GetBytecode(frame); } diff --git a/Objects/typeobject.c b/Objects/typeobject.c index b4a11195613d74..40225313a8a33b 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -11638,9 +11638,10 @@ super_descr_get(PyObject *self, PyObject *obj, PyObject *type) } static int -super_init_without_args(_PyInterpreterFrame *cframe, PyCodeObject *co, - PyTypeObject **type_p, PyObject **obj_p) +super_init_without_args(_PyInterpreterFrame *cframe, PyTypeObject **type_p, + PyObject **obj_p) { + PyCodeObject *co = _PyFrame_GetCode(cframe); if (co->co_argcount == 0) { PyErr_SetString(PyExc_RuntimeError, "super(): no arguments"); @@ -11740,7 +11741,7 @@ super_init_impl(PyObject *self, PyTypeObject *type, PyObject *obj) { "super(): no current frame"); return -1; } - int res = super_init_without_args(frame, _PyFrame_GetCode(frame), &type, &obj); + int res = super_init_without_args(frame, &type, &obj); if (res < 0) { return -1; diff --git a/PCbuild/_freeze_module.vcxproj b/PCbuild/_freeze_module.vcxproj index a3c2d32c454e04..51b493f8a84c6f 100644 --- a/PCbuild/_freeze_module.vcxproj +++ b/PCbuild/_freeze_module.vcxproj @@ -222,6 +222,7 @@ + diff --git a/PCbuild/_freeze_module.vcxproj.filters b/PCbuild/_freeze_module.vcxproj.filters index 91b1d75fb8df5e..09a5f4d30ef490 100644 --- a/PCbuild/_freeze_module.vcxproj.filters +++ b/PCbuild/_freeze_module.vcxproj.filters @@ -232,6 +232,9 @@ Source Files + + Source Files + Source Files diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index a4881e9256e4dd..f840e7fd61f985 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -255,6 +255,7 @@ + @@ -614,6 +615,7 @@ + diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index 740790cc5e1119..a930cd0b0b10c6 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -687,6 +687,9 @@ Include\internal + + Include\internal + Include\internal @@ -1373,6 +1376,9 @@ Python + + Python + Python diff --git a/Python/bytecodes.c b/Python/bytecodes.c index fa98af12c69aef..2c78cb9931733d 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -168,11 +168,11 @@ dummy_func( } op(_QUICKEN_RESUME, (--)) { - #if ENABLE_SPECIALIZATION + #if ENABLE_SPECIALIZATION_FT if (tstate->tracing == 0 && this_instr->op.code == RESUME) { FT_ATOMIC_STORE_UINT8_RELAXED(this_instr->op.code, RESUME_CHECK); } - #endif /* ENABLE_SPECIALIZATION */ + #endif /* ENABLE_SPECIALIZATION_FT */ } tier1 op(_MAYBE_INSTRUMENT, (--)) { @@ -190,7 +190,26 @@ dummy_func( } } + op(_LOAD_BYTECODE, (--)) { + #ifdef Py_GIL_DISABLED + if (frame->tlbc_index != + ((_PyThreadStateImpl *)tstate)->tlbc_index) { + _Py_CODEUNIT *bytecode = + _PyEval_GetExecutableCode(tstate, _PyFrame_GetCode(frame)); + ERROR_IF(bytecode == NULL, error); + int off = this_instr - _PyFrame_GetBytecode(frame); + frame->tlbc_index = ((_PyThreadStateImpl *)tstate)->tlbc_index; + frame->instr_ptr = bytecode + off; + // Make sure this_instr gets reset correctley for any uops that + // follow + next_instr = frame->instr_ptr; + DISPATCH(); + } + #endif + } + macro(RESUME) = + _LOAD_BYTECODE + _MAYBE_INSTRUMENT + _QUICKEN_RESUME + _CHECK_PERIODIC_IF_NOT_YIELD_FROM; @@ -204,6 +223,10 @@ dummy_func( uintptr_t version = FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(_PyFrame_GetCode(frame)->_co_instrumentation_version); assert((version & _PY_EVAL_EVENTS_MASK) == 0); DEOPT_IF(eval_breaker != version); + #ifdef Py_GIL_DISABLED + DEOPT_IF(frame->tlbc_index != + ((_PyThreadStateImpl *)tstate)->tlbc_index); + #endif } op(_MONITOR_RESUME, (--)) { @@ -217,6 +240,7 @@ dummy_func( } macro(INSTRUMENTED_RESUME) = + _LOAD_BYTECODE + _MAYBE_INSTRUMENT + _CHECK_PERIODIC_IF_NOT_YIELD_FROM + _MONITOR_RESUME; @@ -682,8 +706,8 @@ dummy_func( }; specializing op(_SPECIALIZE_BINARY_SUBSCR, (counter/1, container, sub -- container, sub)) { - assert(frame->stackpointer == NULL); #if ENABLE_SPECIALIZATION + assert(frame->stackpointer == NULL); if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { next_instr = this_instr; _Py_Specialize_BinarySubscr(container, sub, next_instr); @@ -1236,7 +1260,7 @@ dummy_func( if (oparg) { PyObject *lasti = PyStackRef_AsPyObjectBorrow(values[0]); if (PyLong_Check(lasti)) { - frame->instr_ptr = _PyCode_CODE(_PyFrame_GetCode(frame)) + PyLong_AsLong(lasti); + frame->instr_ptr = _PyFrame_GetBytecode(frame) + PyLong_AsLong(lasti); assert(!_PyErr_Occurred(tstate)); } else { @@ -2671,9 +2695,7 @@ dummy_func( assert(PyStackRef_BoolCheck(cond)); int flag = PyStackRef_Is(cond, PyStackRef_False); DEAD(cond); - #if ENABLE_SPECIALIZATION - this_instr[1].cache = (this_instr[1].cache << 1) | flag; - #endif + RECORD_BRANCH_TAKEN(this_instr[1].cache, flag); JUMPBY(oparg * flag); } @@ -2681,9 +2703,7 @@ dummy_func( assert(PyStackRef_BoolCheck(cond)); int flag = PyStackRef_Is(cond, PyStackRef_True); DEAD(cond); - #if ENABLE_SPECIALIZATION - this_instr[1].cache = (this_instr[1].cache << 1) | flag; - #endif + RECORD_BRANCH_TAKEN(this_instr[1].cache, flag); JUMPBY(oparg * flag); } @@ -3697,7 +3717,7 @@ dummy_func( op(_CREATE_INIT_FRAME, (init[1], self[1], args[oparg] -- init_frame: _PyInterpreterFrame *)) { _PyInterpreterFrame *shim = _PyFrame_PushTrampolineUnchecked( tstate, (PyCodeObject *)&_Py_InitCleanup, 1, frame); - assert(_PyCode_CODE(_PyFrame_GetCode(shim))[0].op.code == EXIT_INIT_CHECK); + assert(_PyFrame_GetBytecode(shim)[0].op.code == EXIT_INIT_CHECK); /* Push self onto stack of shim */ shim->localsplus[0] = PyStackRef_DUP(self[0]); DEAD(init); @@ -4593,7 +4613,7 @@ dummy_func( } specializing op(_SPECIALIZE_BINARY_OP, (counter/1, lhs, rhs -- lhs, rhs)) { - #if ENABLE_SPECIALIZATION + #if ENABLE_SPECIALIZATION_FT if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { next_instr = this_instr; _Py_Specialize_BinaryOp(lhs, rhs, next_instr, oparg, LOCALS_ARRAY); @@ -4601,7 +4621,7 @@ dummy_func( } OPCODE_DEFERRED_INC(BINARY_OP); ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); - #endif /* ENABLE_SPECIALIZATION */ + #endif /* ENABLE_SPECIALIZATION_FT */ assert(NB_ADD <= oparg); assert(oparg <= NB_INPLACE_XOR); } @@ -4632,7 +4652,7 @@ dummy_func( int original_opcode = 0; if (tstate->tracing) { PyCodeObject *code = _PyFrame_GetCode(frame); - original_opcode = code->_co_monitoring->lines[(int)(this_instr - _PyCode_CODE(code))].original_opcode; + original_opcode = code->_co_monitoring->lines[(int)(this_instr - _PyFrame_GetBytecode(frame))].original_opcode; next_instr = this_instr; } else { original_opcode = _Py_call_instrumentation_line( @@ -4687,9 +4707,7 @@ dummy_func( assert(PyStackRef_BoolCheck(cond)); int flag = PyStackRef_Is(cond, PyStackRef_True); int offset = flag * oparg; - #if ENABLE_SPECIALIZATION - this_instr[1].cache = (this_instr[1].cache << 1) | flag; - #endif + RECORD_BRANCH_TAKEN(this_instr[1].cache, flag); INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH); } @@ -4698,9 +4716,7 @@ dummy_func( assert(PyStackRef_BoolCheck(cond)); int flag = PyStackRef_Is(cond, PyStackRef_False); int offset = flag * oparg; - #if ENABLE_SPECIALIZATION - this_instr[1].cache = (this_instr[1].cache << 1) | flag; - #endif + RECORD_BRANCH_TAKEN(this_instr[1].cache, flag); INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH); } @@ -4715,9 +4731,7 @@ dummy_func( PyStackRef_CLOSE(value_stackref); offset = 0; } - #if ENABLE_SPECIALIZATION - this_instr[1].cache = (this_instr[1].cache << 1) | flag; - #endif + RECORD_BRANCH_TAKEN(this_instr[1].cache, flag); INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH); } @@ -4815,7 +4829,7 @@ dummy_func( tier2 op(_EXIT_TRACE, (exit_p/4 --)) { _PyExitData *exit = (_PyExitData *)exit_p; PyCodeObject *code = _PyFrame_GetCode(frame); - _Py_CODEUNIT *target = _PyCode_CODE(code) + exit->target; + _Py_CODEUNIT *target = _PyFrame_GetBytecode(frame) + exit->target; #if defined(Py_DEBUG) && !defined(_Py_JIT) OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); if (lltrace >= 2) { @@ -4823,7 +4837,7 @@ dummy_func( _PyUOpPrint(&next_uop[-1]); printf(", exit %u, temp %d, target %d -> %s]\n", exit - current_executor->exits, exit->temperature.value_and_backoff, - (int)(target - _PyCode_CODE(code)), + (int)(target - _PyFrame_GetBytecode(frame)), _PyOpcode_OpName[target->op.code]); } #endif @@ -4933,7 +4947,7 @@ dummy_func( _PyUOpPrint(&next_uop[-1]); printf(", exit %u, temp %d, target %d -> %s]\n", exit - current_executor->exits, exit->temperature.value_and_backoff, - (int)(target - _PyCode_CODE(_PyFrame_GetCode(frame))), + (int)(target - _PyFrame_GetBytecode(frame)), _PyOpcode_OpName[target->op.code]); } #endif @@ -4995,7 +5009,7 @@ dummy_func( } tier2 op(_ERROR_POP_N, (target/2, unused[oparg] --)) { - frame->instr_ptr = ((_Py_CODEUNIT *)_PyFrame_GetCode(frame)->co_code_adaptive) + target; + frame->instr_ptr = _PyFrame_GetBytecode(frame) + target; SYNC_SP(); GOTO_UNWIND(); } diff --git a/Python/ceval.c b/Python/ceval.c index beee5325cd6259..9a608f06966688 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -189,7 +189,7 @@ lltrace_instruction(_PyInterpreterFrame *frame, dump_stack(frame, stack_pointer); const char *opname = _PyOpcode_OpName[opcode]; assert(opname != NULL); - int offset = (int)(next_instr - _PyCode_CODE(_PyFrame_GetCode(frame))); + int offset = (int)(next_instr - _PyFrame_GetBytecode(frame)); if (OPCODE_HAS_ARG((int)_PyOpcode_Deopt[opcode])) { printf("%d: %s %d\n", offset * 2, opname, oparg); } @@ -841,6 +841,19 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } /* Because this avoids the RESUME, * we need to update instrumentation */ +#ifdef Py_GIL_DISABLED + /* Load thread-local bytecode */ + if (frame->tlbc_index != ((_PyThreadStateImpl *)tstate)->tlbc_index) { + _Py_CODEUNIT *bytecode = + _PyEval_GetExecutableCode(tstate, _PyFrame_GetCode(frame)); + if (bytecode == NULL) { + goto error; + } + ptrdiff_t off = frame->instr_ptr - _PyFrame_GetBytecode(frame); + frame->tlbc_index = ((_PyThreadStateImpl *)tstate)->tlbc_index; + frame->instr_ptr = bytecode + off; + } +#endif _Py_Instrument(_PyFrame_GetCode(frame), tstate->interp); monitor_throw(tstate, frame, frame->instr_ptr); /* TO DO -- Monitor throw entry. */ @@ -983,7 +996,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int Python main loop. */ PyObject *exc = _PyErr_GetRaisedException(tstate); PUSH(PyStackRef_FromPyObjectSteal(exc)); - next_instr = _PyCode_CODE(_PyFrame_GetCode(frame)) + handler; + next_instr = _PyFrame_GetBytecode(frame) + handler; if (monitor_handled(tstate, frame, next_instr, exc) < 0) { goto exception_unwind; @@ -1045,6 +1058,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int #undef ENABLE_SPECIALIZATION #define ENABLE_SPECIALIZATION 0 +#undef ENABLE_SPECIALIZATION_FT +#define ENABLE_SPECIALIZATION_FT 0 #ifdef Py_DEBUG #define DPRINTF(level, ...) \ @@ -1139,7 +1154,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int goto goto_to_tier1; exit_to_tier1: assert(next_uop[-1].format == UOP_FORMAT_TARGET); - next_instr = next_uop[-1].target + _PyCode_CODE(_PyFrame_GetCode(frame)); + next_instr = next_uop[-1].target + _PyFrame_GetBytecode(frame); goto_to_tier1: #ifdef Py_DEBUG if (lltrace >= 2) { @@ -1764,7 +1779,7 @@ _PyEvalFramePushAndInit(PyThreadState *tstate, _PyStackRef func, if (frame == NULL) { goto fail; } - _PyFrame_Initialize(frame, func, locals, code, 0, previous); + _PyFrame_Initialize(tstate, frame, func, locals, code, 0, previous); if (initialize_locals(tstate, func_obj, frame->localsplus, args, argcount, kwnames)) { assert(frame->owner == FRAME_OWNED_BY_THREAD); clear_thread_frame(tstate, frame); diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h index 6674c4ccf9f693..5df55813a0ddeb 100644 --- a/Python/ceval_macros.h +++ b/Python/ceval_macros.h @@ -151,7 +151,7 @@ GETITEM(PyObject *v, Py_ssize_t i) { /* Code access macros */ /* The integer overflow is checked by an assertion below. */ -#define INSTR_OFFSET() ((int)(next_instr - _PyCode_CODE(_PyFrame_GetCode(frame)))) +#define INSTR_OFFSET() ((int)(next_instr - _PyFrame_GetBytecode(frame))) #define NEXTOPARG() do { \ _Py_CODEUNIT word = {.cache = FT_ATOMIC_LOAD_UINT16_RELAXED(*(uint16_t*)next_instr)}; \ opcode = word.op.code; \ @@ -301,14 +301,6 @@ GETITEM(PyObject *v, Py_ssize_t i) { #define ADAPTIVE_COUNTER_TRIGGERS(COUNTER) \ backoff_counter_triggers(forge_backoff_counter((COUNTER))) -#ifdef Py_GIL_DISABLED -#define ADVANCE_ADAPTIVE_COUNTER(COUNTER) \ - do { \ - /* gh-115999 tracks progress on addressing this. */ \ - static_assert(0, "The specializing interpreter is not yet thread-safe"); \ - } while (0); -#define PAUSE_ADAPTIVE_COUNTER(COUNTER) ((void)COUNTER) -#else #define ADVANCE_ADAPTIVE_COUNTER(COUNTER) \ do { \ (COUNTER) = advance_backoff_counter((COUNTER)); \ @@ -318,6 +310,18 @@ GETITEM(PyObject *v, Py_ssize_t i) { do { \ (COUNTER) = pause_backoff_counter((COUNTER)); \ } while (0); + +#ifdef ENABLE_SPECIALIZATION_FT +/* Multiple threads may execute these concurrently if thread-local bytecode is + * disabled and they all execute the main copy of the bytecode. Specialization + * is disabled in that case so the value is unused, but the RMW cycle should be + * free of data races. + */ +#define RECORD_BRANCH_TAKEN(bitset, flag) \ + FT_ATOMIC_STORE_UINT16_RELAXED( \ + bitset, (FT_ATOMIC_LOAD_UINT16_RELAXED(bitset) << 1) | (flag)) +#else +#define RECORD_BRANCH_TAKEN(bitset, flag) #endif #define UNBOUNDLOCAL_ERROR_MSG \ diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index ff4a0a52a0b445..9fac4e881b81e2 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -41,6 +41,8 @@ /* _QUICKEN_RESUME is not a viable micro-op for tier 2 because it uses the 'this_instr' variable */ + /* _LOAD_BYTECODE is not a viable micro-op for tier 2 because it uses the 'this_instr' variable */ + case _RESUME_CHECK: { #if defined(__EMSCRIPTEN__) if (_Py_emscripten_signal_clock == 0) { @@ -56,6 +58,13 @@ UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); } + #ifdef Py_GIL_DISABLED + if (frame->tlbc_index != + ((_PyThreadStateImpl *)tstate)->tlbc_index) { + UOP_STAT_INC(uopcode, miss); + JUMP_TO_JUMP_TARGET(); + } + #endif break; } @@ -4480,8 +4489,8 @@ _PyFrame_SetStackPointer(frame, stack_pointer); _PyInterpreterFrame *shim = _PyFrame_PushTrampolineUnchecked( tstate, (PyCodeObject *)&_Py_InitCleanup, 1, frame); + assert(_PyFrame_GetBytecode(shim)[0].op.code == EXIT_INIT_CHECK); stack_pointer = _PyFrame_GetStackPointer(frame); - assert(_PyCode_CODE(_PyFrame_GetCode(shim))[0].op.code == EXIT_INIT_CHECK); /* Push self onto stack of shim */ shim->localsplus[0] = PyStackRef_DUP(self[0]); _PyFrame_SetStackPointer(frame, stack_pointer); @@ -5683,7 +5692,9 @@ PyObject *exit_p = (PyObject *)CURRENT_OPERAND(); _PyExitData *exit = (_PyExitData *)exit_p; PyCodeObject *code = _PyFrame_GetCode(frame); - _Py_CODEUNIT *target = _PyCode_CODE(code) + exit->target; + _PyFrame_SetStackPointer(frame, stack_pointer); + _Py_CODEUNIT *target = _PyFrame_GetBytecode(frame) + exit->target; + stack_pointer = _PyFrame_GetStackPointer(frame); #if defined(Py_DEBUG) && !defined(_Py_JIT) OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); if (lltrace >= 2) { @@ -5692,7 +5703,7 @@ _PyUOpPrint(&next_uop[-1]); printf(", exit %u, temp %d, target %d -> %s]\n", exit - current_executor->exits, exit->temperature.value_and_backoff, - (int)(target - _PyCode_CODE(code)), + (int)(target - _PyFrame_GetBytecode(frame)), _PyOpcode_OpName[target->op.code]); stack_pointer = _PyFrame_GetStackPointer(frame); } @@ -5878,7 +5889,7 @@ _PyUOpPrint(&next_uop[-1]); printf(", exit %u, temp %d, target %d -> %s]\n", exit - current_executor->exits, exit->temperature.value_and_backoff, - (int)(target - _PyCode_CODE(_PyFrame_GetCode(frame))), + (int)(target - _PyFrame_GetBytecode(frame)), _PyOpcode_OpName[target->op.code]); stack_pointer = _PyFrame_GetStackPointer(frame); } @@ -5956,9 +5967,11 @@ case _ERROR_POP_N: { oparg = CURRENT_OPARG(); uint32_t target = (uint32_t)CURRENT_OPERAND(); - frame->instr_ptr = ((_Py_CODEUNIT *)_PyFrame_GetCode(frame)->co_code_adaptive) + target; stack_pointer += -oparg; assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + frame->instr_ptr = _PyFrame_GetBytecode(frame) + target; + stack_pointer = _PyFrame_GetStackPointer(frame); GOTO_UNWIND(); break; } diff --git a/Python/frame.c b/Python/frame.c index 35e6c2d0a93333..9a865e57d97cc6 100644 --- a/Python/frame.c +++ b/Python/frame.c @@ -63,7 +63,8 @@ take_ownership(PyFrameObject *f, _PyInterpreterFrame *frame) // This may be a newly-created generator or coroutine frame. Since it's // dead anyways, just pretend that the first RESUME ran: PyCodeObject *code = _PyFrame_GetCode(frame); - frame->instr_ptr = _PyCode_CODE(code) + code->_co_firsttraceable + 1; + frame->instr_ptr = + _PyFrame_GetBytecode(frame) + code->_co_firsttraceable + 1; } assert(!_PyFrame_IsIncomplete(frame)); assert(f->f_back == NULL); diff --git a/Python/gc_free_threading.c b/Python/gc_free_threading.c index 1969ed608ea524..986d80c18d36c8 100644 --- a/Python/gc_free_threading.c +++ b/Python/gc_free_threading.c @@ -1953,16 +1953,22 @@ custom_visitor_wrapper(const mi_heap_t *heap, const mi_heap_area_t *area, } void -PyUnstable_GC_VisitObjects(gcvisitobjects_t callback, void *arg) +_PyGC_VisitObjectsWorldStopped(PyInterpreterState *interp, + gcvisitobjects_t callback, void *arg) { - PyInterpreterState *interp = _PyInterpreterState_GET(); struct custom_visitor_args wrapper = { .callback = callback, .arg = arg, }; + gc_visit_heaps(interp, &custom_visitor_wrapper, &wrapper.base); +} +void +PyUnstable_GC_VisitObjects(gcvisitobjects_t callback, void *arg) +{ + PyInterpreterState *interp = _PyInterpreterState_GET(); _PyEval_StopTheWorld(interp); - gc_visit_heaps(interp, &custom_visitor_wrapper, &wrapper.base); + _PyGC_VisitObjectsWorldStopped(interp, callback, arg); _PyEval_StartTheWorld(interp); } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 632cbc7790a4d8..eff246f1997276 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -25,7 +25,7 @@ lhs = stack_pointer[-2]; uint16_t counter = read_u16(&this_instr[1].cache); (void)counter; - #if ENABLE_SPECIALIZATION + #if ENABLE_SPECIALIZATION_FT if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { next_instr = this_instr; _PyFrame_SetStackPointer(frame, stack_pointer); @@ -35,7 +35,7 @@ } OPCODE_DEFERRED_INC(BINARY_OP); ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); - #endif /* ENABLE_SPECIALIZATION */ + #endif /* ENABLE_SPECIALIZATION_FT */ assert(NB_ADD <= oparg); assert(oparg <= NB_INPLACE_XOR); } @@ -435,8 +435,8 @@ container = stack_pointer[-2]; uint16_t counter = read_u16(&this_instr[1].cache); (void)counter; - assert(frame->stackpointer == NULL); #if ENABLE_SPECIALIZATION + assert(frame->stackpointer == NULL); if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { next_instr = this_instr; _PyFrame_SetStackPointer(frame, stack_pointer); @@ -1066,8 +1066,8 @@ _PyFrame_SetStackPointer(frame, stack_pointer); _PyInterpreterFrame *shim = _PyFrame_PushTrampolineUnchecked( tstate, (PyCodeObject *)&_Py_InitCleanup, 1, frame); + assert(_PyFrame_GetBytecode(shim)[0].op.code == EXIT_INIT_CHECK); stack_pointer = _PyFrame_GetStackPointer(frame); - assert(_PyCode_CODE(_PyFrame_GetCode(shim))[0].op.code == EXIT_INIT_CHECK); /* Push self onto stack of shim */ shim->localsplus[0] = PyStackRef_DUP(self[0]); _PyFrame_SetStackPointer(frame, stack_pointer); @@ -4711,7 +4711,9 @@ int original_opcode = 0; if (tstate->tracing) { PyCodeObject *code = _PyFrame_GetCode(frame); - original_opcode = code->_co_monitoring->lines[(int)(this_instr - _PyCode_CODE(code))].original_opcode; + _PyFrame_SetStackPointer(frame, stack_pointer); + original_opcode = code->_co_monitoring->lines[(int)(this_instr - _PyFrame_GetBytecode(frame))].original_opcode; + stack_pointer = _PyFrame_GetStackPointer(frame); next_instr = this_instr; } else { _PyFrame_SetStackPointer(frame, stack_pointer); @@ -4759,9 +4761,7 @@ assert(PyStackRef_BoolCheck(cond)); int flag = PyStackRef_Is(cond, PyStackRef_False); int offset = flag * oparg; - #if ENABLE_SPECIALIZATION - this_instr[1].cache = (this_instr[1].cache << 1) | flag; - #endif + RECORD_BRANCH_TAKEN(this_instr[1].cache, flag); INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH); DISPATCH(); } @@ -4782,9 +4782,7 @@ PyStackRef_CLOSE(value_stackref); offset = 0; } - #if ENABLE_SPECIALIZATION - this_instr[1].cache = (this_instr[1].cache << 1) | flag; - #endif + RECORD_BRANCH_TAKEN(this_instr[1].cache, flag); INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH); DISPATCH(); } @@ -4822,9 +4820,7 @@ assert(PyStackRef_BoolCheck(cond)); int flag = PyStackRef_Is(cond, PyStackRef_True); int offset = flag * oparg; - #if ENABLE_SPECIALIZATION - this_instr[1].cache = (this_instr[1].cache << 1) | flag; - #endif + RECORD_BRANCH_TAKEN(this_instr[1].cache, flag); INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH); DISPATCH(); } @@ -4834,6 +4830,28 @@ (void)this_instr; next_instr += 1; INSTRUCTION_STATS(INSTRUMENTED_RESUME); + // _LOAD_BYTECODE + { + #ifdef Py_GIL_DISABLED + if (frame->tlbc_index != + ((_PyThreadStateImpl *)tstate)->tlbc_index) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _Py_CODEUNIT *bytecode = + _PyEval_GetExecutableCode(tstate, _PyFrame_GetCode(frame)); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (bytecode == NULL) goto error; + _PyFrame_SetStackPointer(frame, stack_pointer); + int off = this_instr - _PyFrame_GetBytecode(frame); + stack_pointer = _PyFrame_GetStackPointer(frame); + frame->tlbc_index = ((_PyThreadStateImpl *)tstate)->tlbc_index; + frame->instr_ptr = bytecode + off; + // Make sure this_instr gets reset correctley for any uops that + // follow + next_instr = frame->instr_ptr; + DISPATCH(); + } + #endif + } // _MAYBE_INSTRUMENT { if (tstate->tracing == 0) { @@ -6646,9 +6664,7 @@ cond = stack_pointer[-1]; assert(PyStackRef_BoolCheck(cond)); int flag = PyStackRef_Is(cond, PyStackRef_False); - #if ENABLE_SPECIALIZATION - this_instr[1].cache = (this_instr[1].cache << 1) | flag; - #endif + RECORD_BRANCH_TAKEN(this_instr[1].cache, flag); JUMPBY(oparg * flag); stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); @@ -6680,9 +6696,7 @@ cond = b; assert(PyStackRef_BoolCheck(cond)); int flag = PyStackRef_Is(cond, PyStackRef_True); - #if ENABLE_SPECIALIZATION - this_instr[1].cache = (this_instr[1].cache << 1) | flag; - #endif + RECORD_BRANCH_TAKEN(this_instr[1].cache, flag); JUMPBY(oparg * flag); } stack_pointer += -1; @@ -6715,9 +6729,7 @@ cond = b; assert(PyStackRef_BoolCheck(cond)); int flag = PyStackRef_Is(cond, PyStackRef_False); - #if ENABLE_SPECIALIZATION - this_instr[1].cache = (this_instr[1].cache << 1) | flag; - #endif + RECORD_BRANCH_TAKEN(this_instr[1].cache, flag); JUMPBY(oparg * flag); } stack_pointer += -1; @@ -6735,9 +6747,7 @@ cond = stack_pointer[-1]; assert(PyStackRef_BoolCheck(cond)); int flag = PyStackRef_Is(cond, PyStackRef_True); - #if ENABLE_SPECIALIZATION - this_instr[1].cache = (this_instr[1].cache << 1) | flag; - #endif + RECORD_BRANCH_TAKEN(this_instr[1].cache, flag); JUMPBY(oparg * flag); stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); @@ -6832,7 +6842,11 @@ if (oparg) { PyObject *lasti = PyStackRef_AsPyObjectBorrow(values[0]); if (PyLong_Check(lasti)) { - frame->instr_ptr = _PyCode_CODE(_PyFrame_GetCode(frame)) + PyLong_AsLong(lasti); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + frame->instr_ptr = _PyFrame_GetBytecode(frame) + PyLong_AsLong(lasti); + stack_pointer = _PyFrame_GetStackPointer(frame); assert(!_PyErr_Occurred(tstate)); } else { @@ -6844,6 +6858,8 @@ Py_DECREF(exc); goto error; } + stack_pointer += 1; + assert(WITHIN_STACK_BOUNDS()); } assert(exc && PyExceptionInstance_Check(exc)); stack_pointer += -1; @@ -6871,6 +6887,28 @@ PREDICTED(RESUME); _Py_CODEUNIT* const this_instr = next_instr - 1; (void)this_instr; + // _LOAD_BYTECODE + { + #ifdef Py_GIL_DISABLED + if (frame->tlbc_index != + ((_PyThreadStateImpl *)tstate)->tlbc_index) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _Py_CODEUNIT *bytecode = + _PyEval_GetExecutableCode(tstate, _PyFrame_GetCode(frame)); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (bytecode == NULL) goto error; + _PyFrame_SetStackPointer(frame, stack_pointer); + int off = this_instr - _PyFrame_GetBytecode(frame); + stack_pointer = _PyFrame_GetStackPointer(frame); + frame->tlbc_index = ((_PyThreadStateImpl *)tstate)->tlbc_index; + frame->instr_ptr = bytecode + off; + // Make sure this_instr gets reset correctley for any uops that + // follow + next_instr = frame->instr_ptr; + DISPATCH(); + } + #endif + } // _MAYBE_INSTRUMENT { if (tstate->tracing == 0) { @@ -6890,11 +6928,11 @@ } // _QUICKEN_RESUME { - #if ENABLE_SPECIALIZATION + #if ENABLE_SPECIALIZATION_FT if (tstate->tracing == 0 && this_instr->op.code == RESUME) { FT_ATOMIC_STORE_UINT8_RELAXED(this_instr->op.code, RESUME_CHECK); } - #endif /* ENABLE_SPECIALIZATION */ + #endif /* ENABLE_SPECIALIZATION_FT */ } // _CHECK_PERIODIC_IF_NOT_YIELD_FROM { @@ -6925,6 +6963,10 @@ uintptr_t version = FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(_PyFrame_GetCode(frame)->_co_instrumentation_version); assert((version & _PY_EVAL_EVENTS_MASK) == 0); DEOPT_IF(eval_breaker != version, RESUME); + #ifdef Py_GIL_DISABLED + DEOPT_IF(frame->tlbc_index != + ((_PyThreadStateImpl *)tstate)->tlbc_index, RESUME); + #endif DISPATCH(); } diff --git a/Python/index_pool.c b/Python/index_pool.c new file mode 100644 index 00000000000000..526eccff74af00 --- /dev/null +++ b/Python/index_pool.c @@ -0,0 +1,193 @@ +#include + +#include "Python.h" + +#include "pycore_index_pool.h" +#include "pycore_lock.h" + +#ifdef Py_GIL_DISABLED + +static inline void +swap(int32_t *values, Py_ssize_t i, Py_ssize_t j) +{ + int32_t tmp = values[i]; + values[i] = values[j]; + values[j] = tmp; +} + +static bool +heap_try_swap(_PyIndexHeap *heap, Py_ssize_t i, Py_ssize_t j) +{ + if (i < 0 || i >= heap->size) { + return 0; + } + if (j < 0 || j >= heap->size) { + return 0; + } + if (i <= j) { + if (heap->values[i] <= heap->values[j]) { + return 0; + } + } + else if (heap->values[j] <= heap->values[i]) { + return 0; + } + swap(heap->values, i, j); + return 1; +} + +static inline Py_ssize_t +parent(Py_ssize_t i) +{ + return (i - 1) / 2; +} + +static inline Py_ssize_t +left_child(Py_ssize_t i) +{ + return 2 * i + 1; +} + +static inline Py_ssize_t +right_child(Py_ssize_t i) +{ + return 2 * i + 2; +} + +static void +heap_add(_PyIndexHeap *heap, int32_t val) +{ + assert(heap->size < heap->capacity); + // Add val to end + heap->values[heap->size] = val; + heap->size++; + // Sift up + for (Py_ssize_t cur = heap->size - 1; cur > 0; cur = parent(cur)) { + if (!heap_try_swap(heap, cur, parent(cur))) { + break; + } + } +} + +static Py_ssize_t +heap_min_child(_PyIndexHeap *heap, Py_ssize_t i) +{ + if (left_child(i) < heap->size) { + if (right_child(i) < heap->size) { + Py_ssize_t lval = heap->values[left_child(i)]; + Py_ssize_t rval = heap->values[right_child(i)]; + return lval < rval ? left_child(i) : right_child(i); + } + return left_child(i); + } + else if (right_child(i) < heap->size) { + return right_child(i); + } + return -1; +} + +static int32_t +heap_pop(_PyIndexHeap *heap) +{ + assert(heap->size > 0); + // Pop smallest and replace with the last element + int32_t result = heap->values[0]; + heap->values[0] = heap->values[heap->size - 1]; + heap->size--; + // Sift down + for (Py_ssize_t cur = 0; cur < heap->size;) { + Py_ssize_t min_child = heap_min_child(heap, cur); + if (min_child > -1 && heap_try_swap(heap, cur, min_child)) { + cur = min_child; + } + else { + break; + } + } + return result; +} + +static int +heap_ensure_capacity(_PyIndexHeap *heap, Py_ssize_t limit) +{ + assert(limit > 0); + if (heap->capacity > limit) { + return 0; + } + Py_ssize_t new_capacity = heap->capacity ? heap->capacity : 1024; + while (new_capacity && new_capacity < limit) { + new_capacity <<= 1; + } + if (!new_capacity) { + return -1; + } + int32_t *new_values = PyMem_RawCalloc(new_capacity, sizeof(int32_t)); + if (new_values == NULL) { + return -1; + } + if (heap->values != NULL) { + memcpy(new_values, heap->values, heap->capacity); + PyMem_RawFree(heap->values); + } + heap->values = new_values; + heap->capacity = new_capacity; + return 0; +} + +static void +heap_fini(_PyIndexHeap *heap) +{ + if (heap->values != NULL) { + PyMem_RawFree(heap->values); + heap->values = NULL; + } + heap->size = -1; + heap->capacity = -1; +} + +#define LOCK_POOL(pool) PyMutex_LockFlags(&pool->mutex, _Py_LOCK_DONT_DETACH) +#define UNLOCK_POOL(pool) PyMutex_Unlock(&pool->mutex) + +int32_t +_PyIndexPool_AllocIndex(_PyIndexPool *pool) +{ + LOCK_POOL(pool); + int32_t index; + _PyIndexHeap *free_indices = &pool->free_indices; + if (free_indices->size == 0) { + // No free indices. Make sure the heap can always store all of the + // indices that have been allocated to avoid having to allocate memory + // (which can fail) when freeing an index. Freeing indices happens when + // threads are being destroyed, which makes error handling awkward / + // impossible. This arrangement shifts handling of allocation failures + // to when indices are allocated, which happens at thread creation, + // where we are better equipped to deal with failure. + if (heap_ensure_capacity(free_indices, pool->next_index + 1) < 0) { + UNLOCK_POOL(pool); + PyErr_NoMemory(); + return -1; + } + index = pool->next_index++; + } + else { + index = heap_pop(free_indices); + } + UNLOCK_POOL(pool); + return index; +} + +void +_PyIndexPool_FreeIndex(_PyIndexPool *pool, int32_t index) +{ + LOCK_POOL(pool); + heap_add(&pool->free_indices, index); + UNLOCK_POOL(pool); +} + +void +_PyIndexPool_Fini(_PyIndexPool *pool) +{ + heap_fini(&pool->free_indices); +} + +#endif // Py_GIL_DISABLED diff --git a/Python/initconfig.c b/Python/initconfig.c index c142438b02bfd9..438f8a5c1cf1ce 100644 --- a/Python/initconfig.c +++ b/Python/initconfig.c @@ -134,6 +134,7 @@ static const PyConfigSpec PYCONFIG_SPEC[] = { SPEC(dump_refs_file, WSTR_OPT, READ_ONLY, NO_SYS), #ifdef Py_GIL_DISABLED SPEC(enable_gil, INT, READ_ONLY, NO_SYS), + SPEC(tlbc_enabled, INT, READ_ONLY, NO_SYS), #endif SPEC(faulthandler, BOOL, READ_ONLY, NO_SYS), SPEC(filesystem_encoding, WSTR, READ_ONLY, NO_SYS), @@ -315,8 +316,13 @@ The following implementation-specific options are available:\n\ "\ -X showrefcount: output the total reference count and number of used\n\ memory blocks when the program finishes or after each statement in\n\ - the interactive interpreter; only works on debug builds\n\ --X tracemalloc[=N]: trace Python memory allocations; N sets a traceback limit\n\ + the interactive interpreter; only works on debug builds\n" +#ifdef Py_GIL_DISABLED +"-X tlbc=[0|1]: enable (1) or disable (0) thread-local bytecode. Also\n\ + PYTHON_TLBC\n" +#endif +"\ +-X tracemalloc[=N]: trace Python memory allocations; N sets a traceback limit\n \ of N frames (default: 1); also PYTHONTRACEMALLOC=N\n\ -X utf8[=0|1]: enable (1) or disable (0) UTF-8 mode; also PYTHONUTF8\n\ -X warn_default_encoding: enable opt-in EncodingWarning for 'encoding=None';\n\ @@ -400,6 +406,9 @@ static const char usage_envvars[] = #ifdef Py_STATS "PYTHONSTATS : turns on statistics gathering (-X pystats)\n" #endif +#ifdef Py_GIL_DISABLED +"PYTHON_TLBC : when set to 0, disables thread-local bytecode (-X tlbc)\n" +#endif "PYTHONTRACEMALLOC: trace Python memory allocations (-X tracemalloc)\n" "PYTHONUNBUFFERED: disable stdout/stderr buffering (-u)\n" "PYTHONUTF8 : control the UTF-8 mode (-X utf8)\n" @@ -979,6 +988,7 @@ _PyConfig_InitCompatConfig(PyConfig *config) config->cpu_count = -1; #ifdef Py_GIL_DISABLED config->enable_gil = _PyConfig_GIL_DEFAULT; + config->tlbc_enabled = 1; #endif } @@ -1862,6 +1872,36 @@ config_init_cpu_count(PyConfig *config) "n must be greater than 0"); } +static PyStatus +config_init_tlbc(PyConfig *config) +{ +#ifdef Py_GIL_DISABLED + const char *env = config_get_env(config, "PYTHON_TLBC"); + if (env) { + int enabled; + if (_Py_str_to_int(env, &enabled) < 0 || (enabled < 0) || (enabled > 1)) { + return _PyStatus_ERR( + "PYTHON_TLBC=N: N is missing or invalid"); + } + config->tlbc_enabled = enabled; + } + + const wchar_t *xoption = config_get_xoption(config, L"tlbc"); + if (xoption) { + int enabled; + const wchar_t *sep = wcschr(xoption, L'='); + if (!sep || (config_wstr_to_int(sep + 1, &enabled) < 0) || (enabled < 0) || (enabled > 1)) { + return _PyStatus_ERR( + "-X tlbc=n: n is missing or invalid"); + } + config->tlbc_enabled = enabled; + } + return _PyStatus_OK(); +#else + return _PyStatus_OK(); +#endif +} + static PyStatus config_init_perf_profiling(PyConfig *config) { @@ -2111,6 +2151,11 @@ config_read_complex_options(PyConfig *config) } #endif + status = config_init_tlbc(config); + if (_PyStatus_EXCEPTION(status)) { + return status; + } + return _PyStatus_OK(); } diff --git a/Python/instrumentation.c b/Python/instrumentation.c index d4568764117563..87c2addaf809eb 100644 --- a/Python/instrumentation.c +++ b/Python/instrumentation.c @@ -44,10 +44,24 @@ #define UNLOCK_CODE() Py_END_CRITICAL_SECTION() +#define MODIFY_BYTECODE(code, func, ...) \ + do { \ + PyCodeObject *co = (code); \ + for (Py_ssize_t i = 0; i < code->co_tlbc->size; i++) { \ + char *bc = co->co_tlbc->entries[i]; \ + if (bc == NULL) { \ + continue; \ + } \ + (func)((_Py_CODEUNIT *)bc, __VA_ARGS__); \ + } \ + } while (0) + #else #define LOCK_CODE(code) #define UNLOCK_CODE() +#define MODIFY_BYTECODE(code, func, ...) \ + (func)(_PyCode_CODE(code), __VA_ARGS__) #endif @@ -309,7 +323,8 @@ _PyInstruction_GetLength(PyCodeObject *code, int offset) { ASSERT_WORLD_STOPPED_OR_LOCKED(code); - int opcode = _PyCode_CODE(code)[offset].op.code; + int opcode = + FT_ATOMIC_LOAD_UINT8_RELAXED(_PyCode_CODE(code)[offset].op.code); assert(opcode != 0); assert(opcode != RESERVED); if (opcode == INSTRUMENTED_LINE) { @@ -578,7 +593,9 @@ sanity_check_instrumentation(PyCodeObject *code) _Py_CODEUNIT _Py_GetBaseCodeUnit(PyCodeObject *code, int i) { - _Py_CODEUNIT inst = _PyCode_CODE(code)[i]; + _Py_CODEUNIT *src_instr = _PyCode_CODE(code) + i; + _Py_CODEUNIT inst = { + .cache = FT_ATOMIC_LOAD_UINT16_RELAXED(*(uint16_t *)src_instr)}; int opcode = inst.op.code; if (opcode < MIN_INSTRUMENTED_OPCODE) { inst.op.code = _PyOpcode_Deopt[opcode]; @@ -614,21 +631,22 @@ _Py_GetBaseCodeUnit(PyCodeObject *code, int i) } static void -de_instrument(PyCodeObject *code, int i, int event) +de_instrument(_Py_CODEUNIT *bytecode, _PyCoMonitoringData *monitoring, int i, + int event) { assert(event != PY_MONITORING_EVENT_INSTRUCTION); assert(event != PY_MONITORING_EVENT_LINE); - _Py_CODEUNIT *instr = &_PyCode_CODE(code)[i]; + _Py_CODEUNIT *instr = &bytecode[i]; uint8_t *opcode_ptr = &instr->op.code; int opcode = *opcode_ptr; assert(opcode != ENTER_EXECUTOR); if (opcode == INSTRUMENTED_LINE) { - opcode_ptr = &code->_co_monitoring->lines[i].original_opcode; + opcode_ptr = &monitoring->lines[i].original_opcode; opcode = *opcode_ptr; } if (opcode == INSTRUMENTED_INSTRUCTION) { - opcode_ptr = &code->_co_monitoring->per_instruction_opcodes[i]; + opcode_ptr = &monitoring->per_instruction_opcodes[i]; opcode = *opcode_ptr; } int deinstrumented = DE_INSTRUMENT[opcode]; @@ -644,65 +662,68 @@ de_instrument(PyCodeObject *code, int i, int event) } static void -de_instrument_line(PyCodeObject *code, int i) +de_instrument_line(_Py_CODEUNIT *bytecode, _PyCoMonitoringData *monitoring, + int i) { - _Py_CODEUNIT *instr = &_PyCode_CODE(code)[i]; + _Py_CODEUNIT *instr = &bytecode[i]; int opcode = instr->op.code; if (opcode != INSTRUMENTED_LINE) { return; } - _PyCoLineInstrumentationData *lines = &code->_co_monitoring->lines[i]; + _PyCoLineInstrumentationData *lines = &monitoring->lines[i]; int original_opcode = lines->original_opcode; if (original_opcode == INSTRUMENTED_INSTRUCTION) { - lines->original_opcode = code->_co_monitoring->per_instruction_opcodes[i]; + lines->original_opcode = monitoring->per_instruction_opcodes[i]; } CHECK(original_opcode != 0); CHECK(original_opcode == _PyOpcode_Deopt[original_opcode]); - instr->op.code = original_opcode; + FT_ATOMIC_STORE_UINT8(instr->op.code, original_opcode); if (_PyOpcode_Caches[original_opcode]) { - instr[1].counter = adaptive_counter_warmup(); + FT_ATOMIC_STORE_UINT16_RELAXED(instr[1].counter.value_and_backoff, + adaptive_counter_warmup().value_and_backoff); } assert(instr->op.code != INSTRUMENTED_LINE); } static void -de_instrument_per_instruction(PyCodeObject *code, int i) +de_instrument_per_instruction(_Py_CODEUNIT *bytecode, + _PyCoMonitoringData *monitoring, int i) { - _Py_CODEUNIT *instr = &_PyCode_CODE(code)[i]; + _Py_CODEUNIT *instr = &bytecode[i]; uint8_t *opcode_ptr = &instr->op.code; int opcode = *opcode_ptr; if (opcode == INSTRUMENTED_LINE) { - opcode_ptr = &code->_co_monitoring->lines[i].original_opcode; + opcode_ptr = &monitoring->lines[i].original_opcode; opcode = *opcode_ptr; } if (opcode != INSTRUMENTED_INSTRUCTION) { return; } - int original_opcode = code->_co_monitoring->per_instruction_opcodes[i]; + int original_opcode = monitoring->per_instruction_opcodes[i]; CHECK(original_opcode != 0); CHECK(original_opcode == _PyOpcode_Deopt[original_opcode]); - *opcode_ptr = original_opcode; + FT_ATOMIC_STORE_UINT8_RELAXED(*opcode_ptr, original_opcode); if (_PyOpcode_Caches[original_opcode]) { - instr[1].counter = adaptive_counter_warmup(); + FT_ATOMIC_STORE_UINT16_RELAXED(instr[1].counter.value_and_backoff, + adaptive_counter_warmup().value_and_backoff); } assert(*opcode_ptr != INSTRUMENTED_INSTRUCTION); assert(instr->op.code != INSTRUMENTED_INSTRUCTION); } - static void -instrument(PyCodeObject *code, int i) +instrument(_Py_CODEUNIT *bytecode, _PyCoMonitoringData *monitoring, int i) { - _Py_CODEUNIT *instr = &_PyCode_CODE(code)[i]; + _Py_CODEUNIT *instr = &bytecode[i]; uint8_t *opcode_ptr = &instr->op.code; int opcode =*opcode_ptr; if (opcode == INSTRUMENTED_LINE) { - _PyCoLineInstrumentationData *lines = &code->_co_monitoring->lines[i]; + _PyCoLineInstrumentationData *lines = &monitoring->lines[i]; opcode_ptr = &lines->original_opcode; opcode = *opcode_ptr; } if (opcode == INSTRUMENTED_INSTRUCTION) { - opcode_ptr = &code->_co_monitoring->per_instruction_opcodes[i]; + opcode_ptr = &monitoring->per_instruction_opcodes[i]; opcode = *opcode_ptr; CHECK(opcode != INSTRUMENTED_INSTRUCTION && opcode != INSTRUMENTED_LINE); CHECK(opcode == _PyOpcode_Deopt[opcode]); @@ -716,52 +737,52 @@ instrument(PyCodeObject *code, int i) if (_PyOpcode_Caches[deopt]) { FT_ATOMIC_STORE_UINT16_RELAXED(instr[1].counter.value_and_backoff, adaptive_counter_warmup().value_and_backoff); - instr[1].counter = adaptive_counter_warmup(); } } } static void -instrument_line(PyCodeObject *code, int i) +instrument_line(_Py_CODEUNIT *bytecode, _PyCoMonitoringData *monitoring, int i) { - uint8_t *opcode_ptr = &_PyCode_CODE(code)[i].op.code; + uint8_t *opcode_ptr = &bytecode[i].op.code; int opcode = *opcode_ptr; if (opcode == INSTRUMENTED_LINE) { return; } - _PyCoLineInstrumentationData *lines = &code->_co_monitoring->lines[i]; + _PyCoLineInstrumentationData *lines = &monitoring->lines[i]; lines->original_opcode = _PyOpcode_Deopt[opcode]; CHECK(lines->original_opcode > 0); - *opcode_ptr = INSTRUMENTED_LINE; + FT_ATOMIC_STORE_UINT8_RELAXED(*opcode_ptr, INSTRUMENTED_LINE); } static void -instrument_per_instruction(PyCodeObject *code, int i) +instrument_per_instruction(_Py_CODEUNIT *bytecode, + _PyCoMonitoringData *monitoring, int i) { - _Py_CODEUNIT *instr = &_PyCode_CODE(code)[i]; + _Py_CODEUNIT *instr = &bytecode[i]; uint8_t *opcode_ptr = &instr->op.code; int opcode = *opcode_ptr; if (opcode == INSTRUMENTED_LINE) { - _PyCoLineInstrumentationData *lines = &code->_co_monitoring->lines[i]; + _PyCoLineInstrumentationData *lines = &monitoring->lines[i]; opcode_ptr = &lines->original_opcode; opcode = *opcode_ptr; } if (opcode == INSTRUMENTED_INSTRUCTION) { - assert(code->_co_monitoring->per_instruction_opcodes[i] > 0); + assert(monitoring->per_instruction_opcodes[i] > 0); return; } CHECK(opcode != 0); if (is_instrumented(opcode)) { - code->_co_monitoring->per_instruction_opcodes[i] = opcode; + monitoring->per_instruction_opcodes[i] = opcode; } else { assert(opcode != 0); assert(_PyOpcode_Deopt[opcode] != 0); assert(_PyOpcode_Deopt[opcode] != RESUME); - code->_co_monitoring->per_instruction_opcodes[i] = _PyOpcode_Deopt[opcode]; + monitoring->per_instruction_opcodes[i] = _PyOpcode_Deopt[opcode]; } - assert(code->_co_monitoring->per_instruction_opcodes[i] > 0); - *opcode_ptr = INSTRUMENTED_INSTRUCTION; + assert(monitoring->per_instruction_opcodes[i] > 0); + FT_ATOMIC_STORE_UINT8_RELAXED(*opcode_ptr, INSTRUMENTED_INSTRUCTION); } static void @@ -773,19 +794,19 @@ remove_tools(PyCodeObject * code, int offset, int event, int tools) assert(PY_MONITORING_IS_INSTRUMENTED_EVENT(event)); assert(opcode_has_event(_Py_GetBaseCodeUnit(code, offset).op.code)); _PyCoMonitoringData *monitoring = code->_co_monitoring; + bool should_de_instrument; if (monitoring && monitoring->tools) { monitoring->tools[offset] &= ~tools; - if (monitoring->tools[offset] == 0) { - de_instrument(code, offset, event); - } + should_de_instrument = (monitoring->tools[offset] == 0); } else { /* Single tool */ uint8_t single_tool = code->_co_monitoring->active_monitors.tools[event]; assert(_Py_popcount32(single_tool) <= 1); - if (((single_tool & tools) == single_tool)) { - de_instrument(code, offset, event); - } + should_de_instrument = ((single_tool & tools) == single_tool); + } + if (should_de_instrument) { + MODIFY_BYTECODE(code, de_instrument, monitoring, offset, event); } } @@ -804,22 +825,23 @@ remove_line_tools(PyCodeObject * code, int offset, int tools) { ASSERT_WORLD_STOPPED_OR_LOCKED(code); - assert(code->_co_monitoring); - if (code->_co_monitoring->line_tools) + _PyCoMonitoringData *monitoring = code->_co_monitoring; + assert(monitoring); + bool should_de_instrument; + if (monitoring->line_tools) { - uint8_t *toolsptr = &code->_co_monitoring->line_tools[offset]; + uint8_t *toolsptr = &monitoring->line_tools[offset]; *toolsptr &= ~tools; - if (*toolsptr == 0 ) { - de_instrument_line(code, offset); - } + should_de_instrument = (*toolsptr == 0); } else { /* Single tool */ - uint8_t single_tool = code->_co_monitoring->active_monitors.tools[PY_MONITORING_EVENT_LINE]; + uint8_t single_tool = monitoring->active_monitors.tools[PY_MONITORING_EVENT_LINE]; assert(_Py_popcount32(single_tool) <= 1); - if (((single_tool & tools) == single_tool)) { - de_instrument_line(code, offset); - } + should_de_instrument = ((single_tool & tools) == single_tool); + } + if (should_de_instrument) { + MODIFY_BYTECODE(code, de_instrument_line, monitoring, offset); } } @@ -841,7 +863,7 @@ add_tools(PyCodeObject * code, int offset, int event, int tools) assert(_Py_popcount32(tools) == 1); assert(tools_is_subset_for_event(code, event, tools)); } - instrument(code, offset); + MODIFY_BYTECODE(code, instrument, code->_co_monitoring, offset); } static void @@ -858,7 +880,7 @@ add_line_tools(PyCodeObject * code, int offset, int tools) /* Single tool */ assert(_Py_popcount32(tools) == 1); } - instrument_line(code, offset); + MODIFY_BYTECODE(code, instrument_line, code->_co_monitoring, offset); } @@ -876,7 +898,7 @@ add_per_instruction_tools(PyCodeObject * code, int offset, int tools) /* Single tool */ assert(_Py_popcount32(tools) == 1); } - instrument_per_instruction(code, offset); + MODIFY_BYTECODE(code, instrument_per_instruction, code->_co_monitoring, offset); } @@ -885,21 +907,22 @@ remove_per_instruction_tools(PyCodeObject * code, int offset, int tools) { ASSERT_WORLD_STOPPED_OR_LOCKED(code); + _PyCoMonitoringData *monitoring = code->_co_monitoring; assert(code->_co_monitoring); + bool should_de_instrument; if (code->_co_monitoring->per_instruction_tools) { uint8_t *toolsptr = &code->_co_monitoring->per_instruction_tools[offset]; *toolsptr &= ~tools; - if (*toolsptr == 0) { - de_instrument_per_instruction(code, offset); - } + should_de_instrument = (*toolsptr == 0); } else { /* Single tool */ uint8_t single_tool = code->_co_monitoring->active_monitors.tools[PY_MONITORING_EVENT_INSTRUCTION]; assert(_Py_popcount32(single_tool) <= 1); - if (((single_tool & tools) == single_tool)) { - de_instrument_per_instruction(code, offset); - } + should_de_instrument = ((single_tool & tools) == single_tool); + } + if (should_de_instrument) { + MODIFY_BYTECODE(code, de_instrument_per_instruction, monitoring, offset); } } @@ -1087,7 +1110,7 @@ call_instrumentation_vector( PyCodeObject *code = _PyFrame_GetCode(frame); assert(args[1] == NULL); args[1] = (PyObject *)code; - int offset = (int)(instr - _PyCode_CODE(code)); + int offset = (int)(instr - _PyFrame_GetBytecode(frame)); /* Offset visible to user should be the offset in bytes, as that is the * convention for APIs involving code offsets. */ int bytes_offset = offset * (int)sizeof(_Py_CODEUNIT); @@ -1173,8 +1196,7 @@ _Py_call_instrumentation_jump( assert(event == PY_MONITORING_EVENT_JUMP || event == PY_MONITORING_EVENT_BRANCH); assert(frame->instr_ptr == instr); - PyCodeObject *code = _PyFrame_GetCode(frame); - int to = (int)(target - _PyCode_CODE(code)); + int to = (int)(target - _PyFrame_GetBytecode(frame)); PyObject *to_obj = PyLong_FromLong(to * (int)sizeof(_Py_CODEUNIT)); if (to_obj == NULL) { return NULL; @@ -1240,7 +1262,8 @@ _Py_call_instrumentation_line(PyThreadState *tstate, _PyInterpreterFrame* frame, PyCodeObject *code = _PyFrame_GetCode(frame); assert(tstate->tracing == 0); assert(debug_check_sanity(tstate->interp, code)); - int i = (int)(instr - _PyCode_CODE(code)); + _Py_CODEUNIT *bytecode = _PyFrame_GetBytecode(frame); + int i = (int)(instr - bytecode); _PyCoMonitoringData *monitoring = code->_co_monitoring; _PyCoLineInstrumentationData *line_data = &monitoring->lines[i]; @@ -1256,10 +1279,10 @@ _Py_call_instrumentation_line(PyThreadState *tstate, _PyInterpreterFrame* frame, line = compute_line(code, i, line_delta); assert(line >= 0); assert(prev != NULL); - int prev_index = (int)(prev - _PyCode_CODE(code)); + int prev_index = (int)(prev - bytecode); int prev_line = _Py_Instrumentation_GetLine(code, prev_index); if (prev_line == line) { - int prev_opcode = _PyCode_CODE(code)[prev_index].op.code; + int prev_opcode = bytecode[prev_index].op.code; /* RESUME and INSTRUMENTED_RESUME are needed for the operation of * instrumentation, so must never be hidden by an INSTRUMENTED_LINE. */ @@ -1359,7 +1382,7 @@ int _Py_call_instrumentation_instruction(PyThreadState *tstate, _PyInterpreterFrame* frame, _Py_CODEUNIT *instr) { PyCodeObject *code = _PyFrame_GetCode(frame); - int offset = (int)(instr - _PyCode_CODE(code)); + int offset = (int)(instr - _PyFrame_GetBytecode(frame)); _PyCoMonitoringData *instrumentation_data = code->_co_monitoring; assert(instrumentation_data->per_instruction_opcodes); int next_opcode = instrumentation_data->per_instruction_opcodes[offset]; diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 0a7e44ef78dda9..54821b23716eeb 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -17,6 +17,8 @@ /* _QUICKEN_RESUME is not a viable micro-op for tier 2 */ + /* _LOAD_BYTECODE is not a viable micro-op for tier 2 */ + case _RESUME_CHECK: { break; } diff --git a/Python/pystate.c b/Python/pystate.c index 36b31f3b9e4200..ded5fde9c4bb51 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1513,6 +1513,11 @@ new_threadstate(PyInterpreterState *interp, int whence) PyMem_RawFree(new_tstate); return NULL; } + int32_t tlbc_idx = _Py_ReserveTLBCIndex(interp); + if (tlbc_idx < 0) { + PyMem_RawFree(new_tstate); + return NULL; + } #endif /* We serialize concurrent creation to protect global state. */ @@ -1555,6 +1560,7 @@ new_threadstate(PyInterpreterState *interp, int whence) #ifdef Py_GIL_DISABLED // Must be called with lock unlocked to avoid lock ordering deadlocks. _Py_qsbr_register(tstate, interp, qsbr_idx); + tstate->tlbc_index = tlbc_idx; #endif return (PyThreadState *)tstate; @@ -1706,6 +1712,10 @@ PyThreadState_Clear(PyThreadState *tstate) // Remove ourself from the biased reference counting table of threads. _Py_brc_remove_thread(tstate); + + // Release our thread-local copies of the bytecode for reuse by another + // thread + _Py_ClearTLBCIndex((_PyThreadStateImpl *)tstate); #endif // Merge our queue of pointers to be freed into the interpreter queue. diff --git a/Python/specialize.c b/Python/specialize.c index ae47809305a300..86cb997ca2ced3 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -24,6 +24,25 @@ extern const char *_PyUOpName(int index); * ./adaptive.md */ +#ifdef Py_GIL_DISABLED +#define SET_OPCODE_OR_RETURN(instr, opcode) \ + do { \ + uint8_t old_op = _Py_atomic_load_uint8_relaxed(&(instr)->op.code); \ + if (old_op >= MIN_INSTRUMENTED_OPCODE) { \ + /* Lost race with instrumentation */ \ + return; \ + } \ + if (!_Py_atomic_compare_exchange_uint8(&(instr)->op.code, &old_op, \ + (opcode))) { \ + /* Lost race with instrumentation */ \ + assert(old_op >= MIN_INSTRUMENTED_OPCODE); \ + return; \ + } \ + } while (0) +#else +#define SET_OPCODE_OR_RETURN(instr, opcode) (instr)->op.code = (opcode) +#endif + #ifdef Py_STATS GCStats _py_gc_stats[NUM_GENERATIONS] = { 0 }; static PyStats _Py_stats_struct = { .gc_stats = _py_gc_stats }; @@ -436,16 +455,25 @@ do { \ # define SPECIALIZATION_FAIL(opcode, kind) ((void)0) #endif -// Initialize warmup counters and insert superinstructions. This cannot fail. +// Initialize warmup counters and optimize instructions. This cannot fail. void -_PyCode_Quicken(PyCodeObject *code) +_PyCode_Quicken(_Py_CODEUNIT *instructions, Py_ssize_t size, PyObject *consts, + int enable_counters) { - #if ENABLE_SPECIALIZATION + #if ENABLE_SPECIALIZATION_FT + _Py_BackoffCounter jump_counter, adaptive_counter; + if (enable_counters) { + jump_counter = initial_jump_backoff_counter(); + adaptive_counter = adaptive_counter_warmup(); + } + else { + jump_counter = initial_unreachable_backoff_counter(); + adaptive_counter = initial_unreachable_backoff_counter(); + } int opcode = 0; int oparg = 0; - _Py_CODEUNIT *instructions = _PyCode_CODE(code); /* The last code unit cannot have a cache, so we don't need to check it */ - for (int i = 0; i < Py_SIZE(code)-1; i++) { + for (Py_ssize_t i = 0; i < size-1; i++) { opcode = instructions[i].op.code; int caches = _PyOpcode_Caches[opcode]; oparg = (oparg << 8) | instructions[i].op.arg; @@ -453,7 +481,7 @@ _PyCode_Quicken(PyCodeObject *code) // The initial value depends on the opcode switch (opcode) { case JUMP_BACKWARD: - instructions[i + 1].counter = initial_jump_backoff_counter(); + instructions[i + 1].counter = jump_counter; break; case POP_JUMP_IF_FALSE: case POP_JUMP_IF_TRUE: @@ -462,7 +490,7 @@ _PyCode_Quicken(PyCodeObject *code) instructions[i + 1].cache = 0x5555; // Alternating 0, 1 bits break; default: - instructions[i + 1].counter = adaptive_counter_warmup(); + instructions[i + 1].counter = adaptive_counter; break; } i += caches; @@ -471,7 +499,7 @@ _PyCode_Quicken(PyCodeObject *code) /* We can't do this in the bytecode compiler as * marshalling can intern strings and make them immortal. */ - PyObject *obj = PyTuple_GET_ITEM(code->co_consts, oparg); + PyObject *obj = PyTuple_GET_ITEM(consts, oparg); if (_Py_IsImmortal(obj)) { instructions[i].op.code = LOAD_CONST_IMMORTAL; } @@ -480,7 +508,7 @@ _PyCode_Quicken(PyCodeObject *code) oparg = 0; } } - #endif /* ENABLE_SPECIALIZATION */ + #endif /* ENABLE_SPECIALIZATION_FT */ } #define SIMPLE_FUNCTION 0 @@ -2243,9 +2271,10 @@ _Py_Specialize_BinaryOp(_PyStackRef lhs_st, _PyStackRef rhs_st, _Py_CODEUNIT *in { PyObject *lhs = PyStackRef_AsPyObjectBorrow(lhs_st); PyObject *rhs = PyStackRef_AsPyObjectBorrow(rhs_st); - assert(ENABLE_SPECIALIZATION); + assert(ENABLE_SPECIALIZATION_FT); assert(_PyOpcode_Caches[BINARY_OP] == INLINE_CACHE_ENTRIES_BINARY_OP); _PyBinaryOpCache *cache = (_PyBinaryOpCache *)(instr + 1); + uint8_t specialized_op; switch (oparg) { case NB_ADD: case NB_INPLACE_ADD: @@ -2256,18 +2285,18 @@ _Py_Specialize_BinaryOp(_PyStackRef lhs_st, _PyStackRef rhs_st, _Py_CODEUNIT *in _Py_CODEUNIT next = instr[INLINE_CACHE_ENTRIES_BINARY_OP + 1]; bool to_store = (next.op.code == STORE_FAST); if (to_store && PyStackRef_AsPyObjectBorrow(locals[next.op.arg]) == lhs) { - instr->op.code = BINARY_OP_INPLACE_ADD_UNICODE; + specialized_op = BINARY_OP_INPLACE_ADD_UNICODE; goto success; } - instr->op.code = BINARY_OP_ADD_UNICODE; + specialized_op = BINARY_OP_ADD_UNICODE; goto success; } if (PyLong_CheckExact(lhs)) { - instr->op.code = BINARY_OP_ADD_INT; + specialized_op = BINARY_OP_ADD_INT; goto success; } if (PyFloat_CheckExact(lhs)) { - instr->op.code = BINARY_OP_ADD_FLOAT; + specialized_op = BINARY_OP_ADD_FLOAT; goto success; } break; @@ -2277,11 +2306,11 @@ _Py_Specialize_BinaryOp(_PyStackRef lhs_st, _PyStackRef rhs_st, _Py_CODEUNIT *in break; } if (PyLong_CheckExact(lhs)) { - instr->op.code = BINARY_OP_MULTIPLY_INT; + specialized_op = BINARY_OP_MULTIPLY_INT; goto success; } if (PyFloat_CheckExact(lhs)) { - instr->op.code = BINARY_OP_MULTIPLY_FLOAT; + specialized_op = BINARY_OP_MULTIPLY_FLOAT; goto success; } break; @@ -2291,22 +2320,23 @@ _Py_Specialize_BinaryOp(_PyStackRef lhs_st, _PyStackRef rhs_st, _Py_CODEUNIT *in break; } if (PyLong_CheckExact(lhs)) { - instr->op.code = BINARY_OP_SUBTRACT_INT; + specialized_op = BINARY_OP_SUBTRACT_INT; goto success; } if (PyFloat_CheckExact(lhs)) { - instr->op.code = BINARY_OP_SUBTRACT_FLOAT; + specialized_op = BINARY_OP_SUBTRACT_FLOAT; goto success; } break; } SPECIALIZATION_FAIL(BINARY_OP, binary_op_fail_kind(oparg, lhs, rhs)); STAT_INC(BINARY_OP, failure); - instr->op.code = BINARY_OP; + SET_OPCODE_OR_RETURN(instr, BINARY_OP); cache->counter = adaptive_counter_backoff(cache->counter); return; success: STAT_INC(BINARY_OP, success); + SET_OPCODE_OR_RETURN(instr, specialized_op); cache->counter = adaptive_counter_cooldown(); } diff --git a/Python/sysmodule.c b/Python/sysmodule.c index a4abd7c3c45709..a086bb979efa9c 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -2174,6 +2174,11 @@ sys__clear_internal_caches_impl(PyObject *module) #ifdef _Py_TIER2 PyInterpreterState *interp = _PyInterpreterState_GET(); _Py_Executors_InvalidateAll(interp, 0); +#endif +#ifdef Py_GIL_DISABLED + if (_Py_ClearUnusedTLBC(_PyInterpreterState_GET()) < 0) { + return NULL; + } #endif PyType_ClearCache(); Py_RETURN_NONE; diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py index 946af4be1a7589..ed254152d7da41 100755 --- a/Tools/gdb/libpython.py +++ b/Tools/gdb/libpython.py @@ -77,6 +77,10 @@ def _managed_dict_offset(): else: return -3 * _sizeof_void_p() +def _interp_frame_has_tlbc_index(): + interp_frame = gdb.lookup_type("_PyInterpreterFrame") + return any(field.name == "tlbc_index" for field in interp_frame.fields()) + Py_TPFLAGS_INLINE_VALUES = (1 << 2) Py_TPFLAGS_MANAGED_DICT = (1 << 4) @@ -105,6 +109,8 @@ def _managed_dict_offset(): UNABLE_READ_INFO_PYTHON_FRAME = 'Unable to read information on python frame' EVALFRAME = '_PyEval_EvalFrameDefault' +INTERP_FRAME_HAS_TLBC_INDEX = _interp_frame_has_tlbc_index() + class NullPyObjectPtr(RuntimeError): pass @@ -693,6 +699,16 @@ def parse_location_table(firstlineno, linetable): yield addr, end_addr, line addr = end_addr + +class PyCodeArrayPtr: + def __init__(self, gdbval): + self._gdbval = gdbval + + def get_entry(self, index): + assert (index >= 0) and (index < self._gdbval["size"]) + return self._gdbval["entries"][index] + + class PyCodeObjectPtr(PyObjectPtr): """ Class wrapping a gdb.Value that's a PyCodeObject* i.e. a instance @@ -1085,7 +1101,12 @@ def _f_nlocalsplus(self): def _f_lasti(self): codeunit_p = gdb.lookup_type("_Py_CODEUNIT").pointer() instr_ptr = self._gdbval["instr_ptr"] - first_instr = self._f_code().field("co_code_adaptive").cast(codeunit_p) + if INTERP_FRAME_HAS_TLBC_INDEX: + tlbc_index = self._gdbval["tlbc_index"] + code_arr = PyCodeArrayPtr(self._f_code().field("co_tlbc")) + first_instr = code_arr.get_entry(tlbc_index).cast(codeunit_p) + else: + first_instr = self._f_code().field("co_code_adaptive").cast(codeunit_p) return int(instr_ptr - first_instr) def is_shim(self): From 9b7294c3a560f43f1e26a0f48c258829076d6464 Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Mon, 4 Nov 2024 19:29:57 +0000 Subject: [PATCH 048/219] GH-126363: Speed up pattern parsing in `pathlib.Path.glob()` (#126364) The implementation of `Path.glob()` does rather a hacky thing: it calls `self.with_segments()` to convert the given pattern to a `Path` object, and then peeks at the private `_raw_path` attribute to see if pathlib removed a trailing slash from the pattern. In this patch, we make `glob()` use a new `_parse_pattern()` classmethod that splits the pattern into parts while preserving information about any trailing slash. This skips the cost of creating a `Path` object, and avoids some path anchor normalization, which makes `Path.glob()` slightly faster. But mostly it's about making the code less naughty. Co-authored-by: Tomas R. --- Lib/pathlib/_local.py | 41 ++++++++++++------- ...-11-03-14-43-51.gh-issue-126363.Xus7vU.rst | 2 + 2 files changed, 29 insertions(+), 14 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-11-03-14-43-51.gh-issue-126363.Xus7vU.rst diff --git a/Lib/pathlib/_local.py b/Lib/pathlib/_local.py index ef072b83d96904..99474e1f71a307 100644 --- a/Lib/pathlib/_local.py +++ b/Lib/pathlib/_local.py @@ -274,6 +274,31 @@ def _parse_path(cls, path): root = sep return drv, root, [x for x in rel.split(sep) if x and x != '.'] + @classmethod + def _parse_pattern(cls, pattern): + """Parse a glob pattern to a list of parts. This is much like + _parse_path, except: + + - Rather than normalizing and returning the drive and root, we raise + NotImplementedError if either are present. + - If the path has no real parts, we raise ValueError. + - If the path ends in a slash, then a final empty part is added. + """ + drv, root, rel = cls.parser.splitroot(pattern) + if root or drv: + raise NotImplementedError("Non-relative patterns are unsupported") + sep = cls.parser.sep + altsep = cls.parser.altsep + if altsep: + rel = rel.replace(altsep, sep) + parts = [x for x in rel.split(sep) if x and x != '.'] + if not parts: + raise ValueError(f"Unacceptable pattern: {str(pattern)!r}") + elif rel.endswith(sep): + # GH-65238: preserve trailing slash in glob patterns. + parts.append('') + return parts + @property def _raw_path(self): """The joined but unnormalized path.""" @@ -641,17 +666,7 @@ def glob(self, pattern, *, case_sensitive=None, recurse_symlinks=False): kind, including directories) matching the given relative pattern. """ sys.audit("pathlib.Path.glob", self, pattern) - if not isinstance(pattern, PurePath): - pattern = self.with_segments(pattern) - if pattern.anchor: - raise NotImplementedError("Non-relative patterns are unsupported") - parts = pattern._tail.copy() - if not parts: - raise ValueError("Unacceptable pattern: {!r}".format(pattern)) - raw = pattern._raw_path - if raw[-1] in (self.parser.sep, self.parser.altsep): - # GH-65238: pathlib doesn't preserve trailing slash. Add it back. - parts.append('') + parts = self._parse_pattern(pattern) select = self._glob_selector(parts[::-1], case_sensitive, recurse_symlinks) root = str(self) paths = select(root) @@ -672,9 +687,7 @@ def rglob(self, pattern, *, case_sensitive=None, recurse_symlinks=False): this subtree. """ sys.audit("pathlib.Path.rglob", self, pattern) - if not isinstance(pattern, PurePath): - pattern = self.with_segments(pattern) - pattern = '**' / pattern + pattern = self.parser.join('**', pattern) return self.glob(pattern, case_sensitive=case_sensitive, recurse_symlinks=recurse_symlinks) def walk(self, top_down=True, on_error=None, follow_symlinks=False): diff --git a/Misc/NEWS.d/next/Library/2024-11-03-14-43-51.gh-issue-126363.Xus7vU.rst b/Misc/NEWS.d/next/Library/2024-11-03-14-43-51.gh-issue-126363.Xus7vU.rst new file mode 100644 index 00000000000000..20fea9b9ef99a0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-11-03-14-43-51.gh-issue-126363.Xus7vU.rst @@ -0,0 +1,2 @@ +Speed up pattern parsing in :meth:`pathlib.Path.glob` by skipping creation +of a :class:`pathlib.Path` object for the pattern. From 532fc08102d62c04d55f5b8aac00bd9e7e12ff4b Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Mon, 4 Nov 2024 21:48:09 +0100 Subject: [PATCH 049/219] gh-89640: Hardcode WASM float word ordering as little endian (#126387) --- ...4-11-04-09-42-04.gh-issue-89640.QBv05o.rst | 1 + configure | 47 ++++++++----------- configure.ac | 42 ++++++++--------- pyconfig.h.in | 4 -- 4 files changed, 41 insertions(+), 53 deletions(-) create mode 100644 Misc/NEWS.d/next/Build/2024-11-04-09-42-04.gh-issue-89640.QBv05o.rst diff --git a/Misc/NEWS.d/next/Build/2024-11-04-09-42-04.gh-issue-89640.QBv05o.rst b/Misc/NEWS.d/next/Build/2024-11-04-09-42-04.gh-issue-89640.QBv05o.rst new file mode 100644 index 00000000000000..4fa44a1d6493b4 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2024-11-04-09-42-04.gh-issue-89640.QBv05o.rst @@ -0,0 +1 @@ +Hard-code float word ordering as little endian on WASM. diff --git a/configure b/configure index 1097747e055179..e529527214da29 100755 --- a/configure +++ b/configure @@ -24227,41 +24227,34 @@ printf "%s\n" "$ax_cv_c_float_words_bigendian" >&6; } case $ax_cv_c_float_words_bigendian in yes) -printf "%s\n" "#define FLOAT_WORDS_BIGENDIAN 1" >>confdefs.h +printf "%s\n" "#define DOUBLE_IS_BIG_ENDIAN_IEEE754 1" >>confdefs.h ;; no) - ;; - *) - as_fn_error $? " - -Unknown float word ordering. You need to manually preset -ax_cv_c_float_words_bigendian=no (or yes) according to your system. - - " "$LINENO" 5 ;; -esac +printf "%s\n" "#define DOUBLE_IS_LITTLE_ENDIAN_IEEE754 1" >>confdefs.h + ;; + *) + case $host_cpu in #( + *arm*) : + # Some ARM platforms use a mixed-endian representation for + # doubles. While Python doesn't currently have full support + # for these platforms (see e.g., issue 1762561), we can at + # least make sure that float <-> string conversions work. + # FLOAT_WORDS_BIGENDIAN doesn't actually detect this case, + # but if it's not big or little, then it must be this? -if test "$ax_cv_c_float_words_bigendian" = "yes" -then - -printf "%s\n" "#define DOUBLE_IS_BIG_ENDIAN_IEEE754 1" >>confdefs.h - -elif test "$ax_cv_c_float_words_bigendian" = "no" -then +printf "%s\n" "#define DOUBLE_IS_ARM_MIXED_ENDIAN_IEEE754 1" >>confdefs.h + ;; #( + wasm*) : printf "%s\n" "#define DOUBLE_IS_LITTLE_ENDIAN_IEEE754 1" >>confdefs.h + ;; #( + *) : + ;; +esac ;; +esac -else - # Some ARM platforms use a mixed-endian representation for doubles. - # While Python doesn't currently have full support for these platforms - # (see e.g., issue 1762561), we can at least make sure that float <-> string - # conversions work. - # FLOAT_WORDS_BIGENDIAN doesn't actually detect this case, but if it's not big - # or little, then it must be this? - -printf "%s\n" "#define DOUBLE_IS_ARM_MIXED_ENDIAN_IEEE754 1" >>confdefs.h -fi # The short float repr introduced in Python 3.1 requires the # correctly-rounded string <-> double conversion functions from diff --git a/configure.ac b/configure.ac index 6d514705e91ce5..bc67a0596ac2b4 100644 --- a/configure.ac +++ b/configure.ac @@ -5946,28 +5946,26 @@ AS_VAR_IF([ac_cv_gcc_asm_for_x64], [yes], [ # * Check for various properties of floating point * # ************************************************** -AX_C_FLOAT_WORDS_BIGENDIAN -if test "$ax_cv_c_float_words_bigendian" = "yes" -then - AC_DEFINE([DOUBLE_IS_BIG_ENDIAN_IEEE754], [1], - [Define if C doubles are 64-bit IEEE 754 binary format, stored - with the most significant byte first]) -elif test "$ax_cv_c_float_words_bigendian" = "no" -then - AC_DEFINE([DOUBLE_IS_LITTLE_ENDIAN_IEEE754], [1], - [Define if C doubles are 64-bit IEEE 754 binary format, stored - with the least significant byte first]) -else - # Some ARM platforms use a mixed-endian representation for doubles. - # While Python doesn't currently have full support for these platforms - # (see e.g., issue 1762561), we can at least make sure that float <-> string - # conversions work. - # FLOAT_WORDS_BIGENDIAN doesn't actually detect this case, but if it's not big - # or little, then it must be this? - AC_DEFINE([DOUBLE_IS_ARM_MIXED_ENDIAN_IEEE754], [1], - [Define if C doubles are 64-bit IEEE 754 binary format, stored - in ARM mixed-endian order (byte order 45670123)]) -fi +AX_C_FLOAT_WORDS_BIGENDIAN( + [AC_DEFINE([DOUBLE_IS_BIG_ENDIAN_IEEE754], [1], + [Define if C doubles are 64-bit IEEE 754 binary format, + stored with the most significant byte first])], + [AC_DEFINE([DOUBLE_IS_LITTLE_ENDIAN_IEEE754], [1], + [Define if C doubles are 64-bit IEEE 754 binary format, + stored with the least significant byte first])], + [AS_CASE([$host_cpu], + [*arm*], [# Some ARM platforms use a mixed-endian representation for + # doubles. While Python doesn't currently have full support + # for these platforms (see e.g., issue 1762561), we can at + # least make sure that float <-> string conversions work. + # FLOAT_WORDS_BIGENDIAN doesn't actually detect this case, + # but if it's not big or little, then it must be this? + AC_DEFINE([DOUBLE_IS_ARM_MIXED_ENDIAN_IEEE754], [1], + [Define if C doubles are 64-bit IEEE 754 binary format, + stored in ARM mixed-endian order (byte order 45670123)])], + [wasm*], [AC_DEFINE([DOUBLE_IS_LITTLE_ENDIAN_IEEE754], [1], + [Define if C doubles are 64-bit IEEE 754 binary format, + stored with the least significant byte first])])]) # The short float repr introduced in Python 3.1 requires the # correctly-rounded string <-> double conversion functions from diff --git a/pyconfig.h.in b/pyconfig.h.in index fcb8a965b1e476..924d86627b0e9b 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -47,10 +47,6 @@ /* Define if --enable-ipv6 is specified */ #undef ENABLE_IPV6 -/* Define to 1 if your system stores words within floats with the most - significant word first */ -#undef FLOAT_WORDS_BIGENDIAN - /* Define if getpgrp() must be called as getpgrp(0). */ #undef GETPGRP_HAVE_ARG From 78015818c2601db842d101cad6ce2319c921935f Mon Sep 17 00:00:00 2001 From: Kirill Podoprigora Date: Tue, 5 Nov 2024 04:12:31 +0200 Subject: [PATCH 050/219] gh-126415: Fix conversion warning in `Python/bytecodes.c` (#126416) Fix conversion warning in bytecodes Co-authored-by: mpage --- Python/bytecodes.c | 2 +- Python/generated_cases.c.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 2c78cb9931733d..81b527e8c050b9 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -197,7 +197,7 @@ dummy_func( _Py_CODEUNIT *bytecode = _PyEval_GetExecutableCode(tstate, _PyFrame_GetCode(frame)); ERROR_IF(bytecode == NULL, error); - int off = this_instr - _PyFrame_GetBytecode(frame); + ptrdiff_t off = this_instr - _PyFrame_GetBytecode(frame); frame->tlbc_index = ((_PyThreadStateImpl *)tstate)->tlbc_index; frame->instr_ptr = bytecode + off; // Make sure this_instr gets reset correctley for any uops that diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index eff246f1997276..c6b8fbc50f388a 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -4841,7 +4841,7 @@ stack_pointer = _PyFrame_GetStackPointer(frame); if (bytecode == NULL) goto error; _PyFrame_SetStackPointer(frame, stack_pointer); - int off = this_instr - _PyFrame_GetBytecode(frame); + ptrdiff_t off = this_instr - _PyFrame_GetBytecode(frame); stack_pointer = _PyFrame_GetStackPointer(frame); frame->tlbc_index = ((_PyThreadStateImpl *)tstate)->tlbc_index; frame->instr_ptr = bytecode + off; @@ -6898,7 +6898,7 @@ stack_pointer = _PyFrame_GetStackPointer(frame); if (bytecode == NULL) goto error; _PyFrame_SetStackPointer(frame, stack_pointer); - int off = this_instr - _PyFrame_GetBytecode(frame); + ptrdiff_t off = this_instr - _PyFrame_GetBytecode(frame); stack_pointer = _PyFrame_GetStackPointer(frame); frame->tlbc_index = ((_PyThreadStateImpl *)tstate)->tlbc_index; frame->instr_ptr = bytecode + off; From d9602265479bcd96dc377d92a34556baf34ac3cd Mon Sep 17 00:00:00 2001 From: lit Date: Tue, 5 Nov 2024 10:58:15 +0800 Subject: [PATCH 051/219] gh-125436: Doc: Add missing ``allow_unnamed_section`` parameter to ``ConfigParser`` documentation (#125437) Add missing ``allow_unnamed_section`` parameter to ``ConfigParser`` doc, as well as to it's parent ``RawConfigParser``. Split too long line on ``ConfigParser`` signature. Add some sections about when some of ``RawConfigParser`` parameters were added. --- Doc/library/configparser.rst | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/Doc/library/configparser.rst b/Doc/library/configparser.rst index 3aad6f7b5d2d20..ac0f3fca3d72fd 100644 --- a/Doc/library/configparser.rst +++ b/Doc/library/configparser.rst @@ -942,7 +942,13 @@ interpolation if an option used is not defined elsewhere. :: ConfigParser Objects -------------------- -.. class:: ConfigParser(defaults=None, dict_type=dict, allow_no_value=False, delimiters=('=', ':'), comment_prefixes=('#', ';'), inline_comment_prefixes=None, strict=True, empty_lines_in_values=True, default_section=configparser.DEFAULTSECT, interpolation=BasicInterpolation(), converters={}) +.. class:: ConfigParser(defaults=None, dict_type=dict, allow_no_value=False, *, \ + delimiters=('=', ':'), comment_prefixes=('#', ';'), \ + inline_comment_prefixes=None, strict=True, \ + empty_lines_in_values=True, \ + default_section=configparser.DEFAULTSECT, \ + interpolation=BasicInterpolation(), converters={}, \ + allow_unnamed_section=False) The main configuration parser. When *defaults* is given, it is initialized into the dictionary of intrinsic defaults. When *dict_type* is given, it @@ -990,6 +996,10 @@ ConfigParser Objects converter gets its own corresponding :meth:`!get*` method on the parser object and section proxies. + When *allow_unnamed_section* is ``True`` (default: ``False``), + the first section name can be omitted. See the + `"Unnamed Sections" section <#unnamed-sections>`_. + It is possible to read several configurations into a single :class:`ConfigParser`, where the most recently added configuration has the highest priority. Any conflicting keys are taken from the more recent @@ -1039,6 +1049,9 @@ ConfigParser Objects Raise a :exc:`MultilineContinuationError` when *allow_no_value* is ``True``, and a key without a value is continued with an indented line. + .. versionchanged:: 3.13 + The *allow_unnamed_section* argument was added. + .. method:: defaults() Return a dictionary containing the instance-wide defaults. @@ -1295,18 +1308,30 @@ RawConfigParser Objects comment_prefixes=('#', ';'), \ inline_comment_prefixes=None, strict=True, \ empty_lines_in_values=True, \ - default_section=configparser.DEFAULTSECT[, \ - interpolation]) + default_section=configparser.DEFAULTSECT, \ + interpolation=BasicInterpolation(), converters={}, \ + allow_unnamed_section=False) Legacy variant of the :class:`ConfigParser`. It has interpolation disabled by default and allows for non-string section names, option names, and values via its unsafe ``add_section`` and ``set`` methods, as well as the legacy ``defaults=`` keyword argument handling. + .. versionchanged:: 3.2 + *allow_no_value*, *delimiters*, *comment_prefixes*, *strict*, + *empty_lines_in_values*, *default_section* and *interpolation* were + added. + + .. versionchanged:: 3.5 + The *converters* argument was added. + .. versionchanged:: 3.8 The default *dict_type* is :class:`dict`, since it now preserves insertion order. + .. versionchanged:: 3.13 + The *allow_unnamed_section* argument was added. + .. note:: Consider using :class:`ConfigParser` instead which checks types of the values to be stored internally. If you don't want interpolation, you From d3840503b0f590ee574fbdf3c96626ff8b3c45f6 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 5 Nov 2024 08:23:17 +0200 Subject: [PATCH 052/219] gh-126303: Fix pickling and copying of os.sched_param objects (GH-126336) --- Include/internal/pycore_typeobject.h | 1 + Lib/test/test_posix.py | 21 +++++++++++++++++++ ...-11-02-19-20-44.gh-issue-126303.yVvyWB.rst | 1 + Modules/posixmodule.c | 17 +++++++++++++++ Objects/typeobject.c | 6 ++++++ 5 files changed, 46 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2024-11-02-19-20-44.gh-issue-126303.yVvyWB.rst diff --git a/Include/internal/pycore_typeobject.h b/Include/internal/pycore_typeobject.h index 118bc98b35d5e3..e72592b8e98ef8 100644 --- a/Include/internal/pycore_typeobject.h +++ b/Include/internal/pycore_typeobject.h @@ -243,6 +243,7 @@ extern PyObject* _PyType_GetFullyQualifiedName(PyTypeObject *type, char sep); // self->tp_flags = (self->tp_flags & ~mask) | flags; extern void _PyType_SetFlags(PyTypeObject *self, unsigned long mask, unsigned long flags); +extern int _PyType_AddMethod(PyTypeObject *, PyMethodDef *); // Like _PyType_SetFlags(), but apply the operation to self and any of its // subclasses without Py_TPFLAGS_IMMUTABLETYPE set. diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py index 35016b83a477fc..ef9d617f66feec 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -6,12 +6,14 @@ from test.support import warnings_helper from test.support.script_helper import assert_python_ok +import copy import errno import sys import signal import time import os import platform +import pickle import stat import tempfile import unittest @@ -1317,6 +1319,25 @@ def test_get_and_set_scheduler_and_param(self): param = posix.sched_param(sched_priority=-large) self.assertRaises(OverflowError, posix.sched_setparam, 0, param) + @requires_sched + def test_sched_param(self): + param = posix.sched_param(1) + for proto in range(pickle.HIGHEST_PROTOCOL+1): + newparam = pickle.loads(pickle.dumps(param, proto)) + self.assertEqual(newparam, param) + newparam = copy.copy(param) + self.assertIsNot(newparam, param) + self.assertEqual(newparam, param) + newparam = copy.deepcopy(param) + self.assertIsNot(newparam, param) + self.assertEqual(newparam, param) + newparam = copy.replace(param) + self.assertIsNot(newparam, param) + self.assertEqual(newparam, param) + newparam = copy.replace(param, sched_priority=0) + self.assertNotEqual(newparam, param) + self.assertEqual(newparam.sched_priority, 0) + @unittest.skipUnless(hasattr(posix, "sched_rr_get_interval"), "no function") def test_sched_rr_get_interval(self): try: diff --git a/Misc/NEWS.d/next/Library/2024-11-02-19-20-44.gh-issue-126303.yVvyWB.rst b/Misc/NEWS.d/next/Library/2024-11-02-19-20-44.gh-issue-126303.yVvyWB.rst new file mode 100644 index 00000000000000..0072c97338c251 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-11-02-19-20-44.gh-issue-126303.yVvyWB.rst @@ -0,0 +1 @@ +Fix pickling and copying of :class:`os.sched_param` objects. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index bb5077cc7f0f09..1ce2baecb8a964 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -24,6 +24,7 @@ #include "pycore_pystate.h" // _PyInterpreterState_GET() #include "pycore_signal.h" // Py_NSIG #include "pycore_time.h" // _PyLong_FromTime_t() +#include "pycore_typeobject.h" // _PyType_AddMethod() #ifdef HAVE_UNISTD_H # include // symlink() @@ -8210,6 +8211,16 @@ os_sched_param_impl(PyTypeObject *type, PyObject *sched_priority) return res; } +static PyObject * +os_sched_param_reduce(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return Py_BuildValue("(O(N))", Py_TYPE(self), PyStructSequence_GetItem(self, 0)); +} + +static PyMethodDef os_sched_param_reduce_method = { + "__reduce__", (PyCFunction)os_sched_param_reduce, METH_NOARGS|METH_COEXIST, NULL, +}; + PyDoc_VAR(os_sched_param__doc__); static PyStructSequence_Field sched_param_fields[] = { @@ -18033,6 +18044,12 @@ posixmodule_exec(PyObject *m) return -1; } ((PyTypeObject *)state->SchedParamType)->tp_new = os_sched_param; + if (_PyType_AddMethod((PyTypeObject *)state->SchedParamType, + &os_sched_param_reduce_method) < 0) + { + return -1; + } + PyType_Modified((PyTypeObject *)state->SchedParamType); #endif /* initialize TerminalSize_info */ diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 40225313a8a33b..88db29e14b0d44 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -7656,6 +7656,12 @@ type_add_method(PyTypeObject *type, PyMethodDef *meth) return 0; } +int +_PyType_AddMethod(PyTypeObject *type, PyMethodDef *meth) +{ + return type_add_method(type, meth); +} + /* Add the methods from tp_methods to the __dict__ in a type object */ static int From 4a0d574273819b2b5006decb661da05b3baa8a4b Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 5 Nov 2024 08:43:34 +0100 Subject: [PATCH 053/219] gh-120057: Add os.reload_environ() function (#126268) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the os.environ.refresh() method with a new os.reload_environ() function. Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Doc/library/os.rst | 33 ++++++++++++++----- Doc/whatsnew/3.14.rst | 7 ++-- Lib/os.py | 25 +++++++------- Lib/test/test_os.py | 16 ++++----- ...-11-01-10-35-49.gh-issue-120057.YWy81Q.rst | 2 ++ 5 files changed, 52 insertions(+), 31 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-11-01-10-35-49.gh-issue-120057.YWy81Q.rst diff --git a/Doc/library/os.rst b/Doc/library/os.rst index f9cded40c2c755..c0354b2280c45c 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -193,10 +193,6 @@ process and user. to the environment made after this time are not reflected in :data:`os.environ`, except for changes made by modifying :data:`os.environ` directly. - The :meth:`!os.environ.refresh` method updates :data:`os.environ` with - changes to the environment made by :func:`os.putenv`, by - :func:`os.unsetenv`, or made outside Python in the same process. - This mapping may be used to modify the environment as well as query the environment. :func:`putenv` will be called automatically when the mapping is modified. @@ -226,12 +222,13 @@ process and user. :data:`os.environ`, and when one of the :meth:`pop` or :meth:`clear` methods is called. + .. seealso:: + + The :func:`os.reload_environ` function. + .. versionchanged:: 3.9 Updated to support :pep:`584`'s merge (``|``) and update (``|=``) operators. - .. versionchanged:: 3.14 - Added the :meth:`!os.environ.refresh` method. - .. data:: environb @@ -249,6 +246,24 @@ process and user. Updated to support :pep:`584`'s merge (``|``) and update (``|=``) operators. +.. function:: reload_environ() + + The :data:`os.environ` and :data:`os.environb` mappings are a cache of + environment variables at the time that Python started. + As such, changes to the current process environment are not reflected + if made outside Python, or by :func:`os.putenv` or :func:`os.unsetenv`. + Use :func:`!os.reload_environ` to update :data:`os.environ` and :data:`os.environb` + with any such changes to the current process environment. + + .. warning:: + This function is not thread-safe. Calling it while the environment is + being modified in an other thread is an undefined behavior. Reading from + :data:`os.environ` or :data:`os.environb`, or calling :func:`os.getenv` + while reloading, may return an empty result. + + .. versionadded:: next + + .. function:: chdir(path) fchdir(fd) getcwd() @@ -568,7 +583,7 @@ process and user. of :data:`os.environ`. This also applies to :func:`getenv` and :func:`getenvb`, which respectively use :data:`os.environ` and :data:`os.environb` in their implementations. - See also the :data:`os.environ.refresh() ` method. + See also the :func:`os.reload_environ` function. .. note:: @@ -818,7 +833,7 @@ process and user. don't update :data:`os.environ`, so it is actually preferable to delete items of :data:`os.environ`. - See also the :data:`os.environ.refresh() ` method. + See also the :func:`os.reload_environ` function. .. audit-event:: os.unsetenv key os.unsetenv diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 80c1a93b95a6af..9300de440cdc30 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -365,9 +365,10 @@ operator os -- -* Add the :data:`os.environ.refresh() ` method to update - :data:`os.environ` with changes to the environment made by :func:`os.putenv`, - by :func:`os.unsetenv`, or made outside Python in the same process. +* Add the :func:`os.reload_environ` function to update :data:`os.environ` and + :data:`os.environb` with changes to the environment made by + :func:`os.putenv`, by :func:`os.unsetenv`, or made outside Python in the + same process. (Contributed by Victor Stinner in :gh:`120057`.) diff --git a/Lib/os.py b/Lib/os.py index aaa758d955fe4c..9c2258e6ccf5ba 100644 --- a/Lib/os.py +++ b/Lib/os.py @@ -765,17 +765,6 @@ def __ror__(self, other): new.update(self) return new - if _exists("_create_environ"): - def refresh(self): - data = _create_environ() - if name == 'nt': - data = {self.encodekey(key): value - for key, value in data.items()} - - # modify in-place to keep os.environb in sync - self._data.clear() - self._data.update(data) - def _create_environ_mapping(): if name == 'nt': # Where Env Var Names Must Be UPPERCASE @@ -810,6 +799,20 @@ def decode(value): del _create_environ_mapping +if _exists("_create_environ"): + def reload_environ(): + data = _create_environ() + if name == 'nt': + encodekey = environ.encodekey + data = {encodekey(key): value + for key, value in data.items()} + + # modify in-place to keep os.environb in sync + env_data = environ._data + env_data.clear() + env_data.update(data) + + def getenv(key, default=None): """Get an environment variable, return None if it doesn't exist. The optional second argument can specify an alternate default. diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 307f0f11ddc33f..9a4be78556c648 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -1298,8 +1298,8 @@ def test_ror_operator(self): self._test_underlying_process_env('_A_', '') self._test_underlying_process_env(overridden_key, original_value) - def test_refresh(self): - # Test os.environ.refresh() + def test_reload_environ(self): + # Test os.reload_environ() has_environb = hasattr(os, 'environb') # Test with putenv() which doesn't update os.environ @@ -1309,7 +1309,7 @@ def test_refresh(self): if has_environb: self.assertEqual(os.environb[b'test_env'], b'python_value') - os.environ.refresh() + os.reload_environ() self.assertEqual(os.environ['test_env'], 'new_value') if has_environb: self.assertEqual(os.environb[b'test_env'], b'new_value') @@ -1320,28 +1320,28 @@ def test_refresh(self): if has_environb: self.assertEqual(os.environb[b'test_env'], b'new_value') - os.environ.refresh() + os.reload_environ() self.assertNotIn('test_env', os.environ) if has_environb: self.assertNotIn(b'test_env', os.environb) if has_environb: - # test os.environb.refresh() with putenv() + # test reload_environ() on os.environb with putenv() os.environb[b'test_env'] = b'python_value2' os.putenv("test_env", "new_value2") self.assertEqual(os.environb[b'test_env'], b'python_value2') self.assertEqual(os.environ['test_env'], 'python_value2') - os.environb.refresh() + os.reload_environ() self.assertEqual(os.environb[b'test_env'], b'new_value2') self.assertEqual(os.environ['test_env'], 'new_value2') - # test os.environb.refresh() with unsetenv() + # test reload_environ() on os.environb with unsetenv() os.unsetenv('test_env') self.assertEqual(os.environb[b'test_env'], b'new_value2') self.assertEqual(os.environ['test_env'], 'new_value2') - os.environb.refresh() + os.reload_environ() self.assertNotIn(b'test_env', os.environb) self.assertNotIn('test_env', os.environ) diff --git a/Misc/NEWS.d/next/Library/2024-11-01-10-35-49.gh-issue-120057.YWy81Q.rst b/Misc/NEWS.d/next/Library/2024-11-01-10-35-49.gh-issue-120057.YWy81Q.rst new file mode 100644 index 00000000000000..ded60a3f57bca3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-11-01-10-35-49.gh-issue-120057.YWy81Q.rst @@ -0,0 +1,2 @@ +Replace the ``os.environ.refresh()`` method with a new +:func:`os.reload_environ` function. Patch by Victor Stinner. From 3d4fda2165e7c97116e69d6efef187873b57d01f Mon Sep 17 00:00:00 2001 From: Lukas Geiger Date: Tue, 5 Nov 2024 07:53:32 +0000 Subject: [PATCH 054/219] gh-119793: Prefer `map(..., strict=True)` over starmap/zip in examples (#126407) --- Doc/library/math.rst | 2 +- Modules/clinic/mathmodule.c.h | 4 ++-- Modules/mathmodule.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Doc/library/math.rst b/Doc/library/math.rst index 2ecee89a7db165..5ce2ad2d6aec47 100644 --- a/Doc/library/math.rst +++ b/Doc/library/math.rst @@ -406,7 +406,7 @@ Number-theoretic and representation functions Roughly equivalent to:: - sum(itertools.starmap(operator.mul, zip(p, q, strict=True))) + sum(map(operator.mul, p, q, strict=True)) For float and mixed int/float inputs, the intermediate products and sums are computed with extended precision. diff --git a/Modules/clinic/mathmodule.c.h b/Modules/clinic/mathmodule.c.h index 7d0b98d5502267..e4bda8a3e62aba 100644 --- a/Modules/clinic/mathmodule.c.h +++ b/Modules/clinic/mathmodule.c.h @@ -457,7 +457,7 @@ PyDoc_STRVAR(math_sumprod__doc__, "\n" "Roughly equivalent to:\n" "\n" -" sum(itertools.starmap(operator.mul, zip(p, q, strict=True)))\n" +" sum(map(operator.mul, p, q, strict=True))\n" "\n" "For float and mixed int/float inputs, the intermediate products\n" "and sums are computed with extended precision."); @@ -1109,4 +1109,4 @@ math_ulp(PyObject *module, PyObject *arg) exit: return return_value; } -/*[clinic end generated code: output=ee0a2f6bd1220061 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=ff99a737c18d9210 input=a9049054013a1b77]*/ diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 7e8d8b3f5bafa2..77f50a2001634b 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -2710,7 +2710,7 @@ Return the sum of products of values from two iterables p and q. Roughly equivalent to: - sum(itertools.starmap(operator.mul, zip(p, q, strict=True))) + sum(map(operator.mul, p, q, strict=True)) For float and mixed int/float inputs, the intermediate products and sums are computed with extended precision. @@ -2718,7 +2718,7 @@ and sums are computed with extended precision. static PyObject * math_sumprod_impl(PyObject *module, PyObject *p, PyObject *q) -/*[clinic end generated code: output=6722dbfe60664554 input=82be54fe26f87e30]*/ +/*[clinic end generated code: output=6722dbfe60664554 input=a2880317828c61d2]*/ { PyObject *p_i = NULL, *q_i = NULL, *term_i = NULL, *new_total = NULL; PyObject *p_it, *q_it, *total; From 407c0366d9ccd2a36c6cc8bf92324856b16fd604 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Tue, 5 Nov 2024 04:48:46 -0500 Subject: [PATCH 055/219] Doc: C API: Delete claim that `PyObject_Init` is GC-aware (#126418) --- Doc/c-api/allocation.rst | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Doc/c-api/allocation.rst b/Doc/c-api/allocation.rst index 0d53b18ea87d5e..b7e0f22b52c57e 100644 --- a/Doc/c-api/allocation.rst +++ b/Doc/c-api/allocation.rst @@ -15,10 +15,8 @@ Allocating Objects on the Heap .. c:function:: PyObject* PyObject_Init(PyObject *op, PyTypeObject *type) Initialize a newly allocated object *op* with its type and initial - reference. Returns the initialized object. If *type* indicates that the - object participates in the cyclic garbage detector, it is added to the - detector's set of observed objects. Other fields of the object are not - affected. + reference. Returns the initialized object. Other fields of the object are + not affected. .. c:function:: PyVarObject* PyObject_InitVar(PyVarObject *op, PyTypeObject *type, Py_ssize_t size) From 1371295e678f00a7c89dc5bb2ab61ede9adbc094 Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Tue, 5 Nov 2024 04:56:36 -0500 Subject: [PATCH 056/219] gh-126366: Fix crash if `__iter__` raises an exception during `yield from` (#126369) --- Lib/test/test_yield_from.py | 13 +++++++++++++ .../2024-11-03-15-15-36.gh-issue-126366.8BBdGU.rst | 2 ++ Python/bytecodes.c | 5 +++-- Python/executor_cases.c.h | 5 +++-- Python/generated_cases.c.h | 5 +++-- Tools/jit/ignore-tests-emulated-linux.txt | 1 + 6 files changed, 25 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-11-03-15-15-36.gh-issue-126366.8BBdGU.rst diff --git a/Lib/test/test_yield_from.py b/Lib/test/test_yield_from.py index 1a60357a1bcd62..b90e15e20027dc 100644 --- a/Lib/test/test_yield_from.py +++ b/Lib/test/test_yield_from.py @@ -1576,6 +1576,19 @@ def outer(): self.assertIsNone(caught.exception.__context__) self.assert_stop_iteration(g) + def test_throws_in_iter(self): + # See GH-126366: NULL pointer dereference if __iter__ + # threw an exception. + class Silly: + def __iter__(self): + raise RuntimeError("nobody expects the spanish inquisition") + + def my_generator(): + yield from Silly() + + with self.assertRaisesRegex(RuntimeError, "nobody expects the spanish inquisition"): + next(iter(my_generator())) + if __name__ == '__main__': unittest.main() diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-03-15-15-36.gh-issue-126366.8BBdGU.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-03-15-15-36.gh-issue-126366.8BBdGU.rst new file mode 100644 index 00000000000000..a47233602e4eff --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-03-15-15-36.gh-issue-126366.8BBdGU.rst @@ -0,0 +1,2 @@ +Fix crash when using ``yield from`` on an object that raises an exception in +its ``__iter__``. diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 81b527e8c050b9..8c52db6ab68436 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2811,11 +2811,12 @@ dummy_func( } else { /* `iterable` is not a generator. */ - iter = PyStackRef_FromPyObjectSteal(PyObject_GetIter(iterable_o)); + PyObject *iter_o = PyObject_GetIter(iterable_o); DEAD(iterable); - if (PyStackRef_IsNull(iter)) { + if (iter_o == NULL) { ERROR_NO_POP(); } + iter = PyStackRef_FromPyObjectSteal(iter_o); DECREF_INPUTS(); } } diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 9fac4e881b81e2..1d63402214db5d 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -3437,11 +3437,12 @@ else { /* `iterable` is not a generator. */ _PyFrame_SetStackPointer(frame, stack_pointer); - iter = PyStackRef_FromPyObjectSteal(PyObject_GetIter(iterable_o)); + PyObject *iter_o = PyObject_GetIter(iterable_o); stack_pointer = _PyFrame_GetStackPointer(frame); - if (PyStackRef_IsNull(iter)) { + if (iter_o == NULL) { JUMP_TO_ERROR(); } + iter = PyStackRef_FromPyObjectSteal(iter_o); PyStackRef_CLOSE(iterable); } } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index c6b8fbc50f388a..d346875ea4455f 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -4304,11 +4304,12 @@ else { /* `iterable` is not a generator. */ _PyFrame_SetStackPointer(frame, stack_pointer); - iter = PyStackRef_FromPyObjectSteal(PyObject_GetIter(iterable_o)); + PyObject *iter_o = PyObject_GetIter(iterable_o); stack_pointer = _PyFrame_GetStackPointer(frame); - if (PyStackRef_IsNull(iter)) { + if (iter_o == NULL) { goto error; } + iter = PyStackRef_FromPyObjectSteal(iter_o); PyStackRef_CLOSE(iterable); } } diff --git a/Tools/jit/ignore-tests-emulated-linux.txt b/Tools/jit/ignore-tests-emulated-linux.txt index e379e39def0eaf..080a569574470c 100644 --- a/Tools/jit/ignore-tests-emulated-linux.txt +++ b/Tools/jit/ignore-tests-emulated-linux.txt @@ -71,6 +71,7 @@ test.test_socket.RecvmsgSCMRightsStreamTest.testCmsgTruncLen1 test.test_socket.RecvmsgSCMRightsStreamTest.testCmsgTruncLen2Minus1 test.test_subprocess.POSIXProcessTestCase.test_exception_bad_args_0 test.test_subprocess.POSIXProcessTestCase.test_exception_bad_executable +test.test_subprocess.POSIXProcessTestCase.test_vfork_used_when_expected test.test_subprocess.ProcessTestCase.test_cwd_with_relative_arg test.test_subprocess.ProcessTestCase.test_cwd_with_relative_executable test.test_subprocess.ProcessTestCase.test_empty_env From 78842e4a98994a218a93992a2a1e3ca3eaa28e79 Mon Sep 17 00:00:00 2001 From: Stephen Morton Date: Tue, 5 Nov 2024 02:05:45 -0800 Subject: [PATCH 057/219] gh-126417: Register multiprocessing proxy types to an appropriate collections.abc class (#126419) --- Lib/multiprocessing/managers.py | 6 +++++- Lib/test/_test_multiprocessing.py | 9 +++++++++ Misc/ACKS | 1 + .../2024-11-04-16-40-02.gh-issue-126417.OWPqn0.rst | 3 +++ 4 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2024-11-04-16-40-02.gh-issue-126417.OWPqn0.rst diff --git a/Lib/multiprocessing/managers.py b/Lib/multiprocessing/managers.py index 0f5f9f64c2de9e..a5d2f53613952e 100644 --- a/Lib/multiprocessing/managers.py +++ b/Lib/multiprocessing/managers.py @@ -18,6 +18,7 @@ import threading import signal import array +import collections.abc import queue import time import types @@ -1167,8 +1168,9 @@ def __imul__(self, value): __class_getitem__ = classmethod(types.GenericAlias) +collections.abc.MutableSequence.register(BaseListProxy) -_BaseDictProxy = MakeProxyType('DictProxy', ( +_BaseDictProxy = MakeProxyType('_BaseDictProxy', ( '__contains__', '__delitem__', '__getitem__', '__ior__', '__iter__', '__len__', '__or__', '__reversed__', '__ror__', '__setitem__', 'clear', 'copy', 'fromkeys', 'get', 'items', @@ -1184,6 +1186,8 @@ def __ior__(self, value): __class_getitem__ = classmethod(types.GenericAlias) +collections.abc.MutableMapping.register(_BaseDictProxy) + ArrayProxy = MakeProxyType('ArrayProxy', ( '__len__', '__getitem__', '__setitem__' )) diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index 065fc27b770438..77b618c684475a 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -16,6 +16,7 @@ import functools import signal import array +import collections.abc import socket import random import logging @@ -2331,6 +2332,10 @@ def test_list(self): a.append('hello') self.assertEqual(f[0][:], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'hello']) + def test_list_isinstance(self): + a = self.list() + self.assertIsInstance(a, collections.abc.MutableSequence) + def test_list_iter(self): a = self.list(list(range(10))) it = iter(a) @@ -2371,6 +2376,10 @@ def test_dict(self): self.assertEqual(sorted(d.values()), [chr(i) for i in indices]) self.assertEqual(sorted(d.items()), [(i, chr(i)) for i in indices]) + def test_dict_isinstance(self): + a = self.dict() + self.assertIsInstance(a, collections.abc.MutableMapping) + def test_dict_iter(self): d = self.dict() indices = list(range(65, 70)) diff --git a/Misc/ACKS b/Misc/ACKS index 5e36eda554af0f..d03c70f6db87bf 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1273,6 +1273,7 @@ Emily Morehouse Derek Morr James A Morrison Martin Morrison +Stephen Morton Derek McTavish Mounce Alessandro Moura Pablo Mouzo diff --git a/Misc/NEWS.d/next/Library/2024-11-04-16-40-02.gh-issue-126417.OWPqn0.rst b/Misc/NEWS.d/next/Library/2024-11-04-16-40-02.gh-issue-126417.OWPqn0.rst new file mode 100644 index 00000000000000..c4a366343382f3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-11-04-16-40-02.gh-issue-126417.OWPqn0.rst @@ -0,0 +1,3 @@ +Register the :class:`!multiprocessing.managers.DictProxy` and :class:`!multiprocessing.managers.ListProxy` types in +:mod:`multiprocessing.managers` to :class:`collections.abc.MutableMapping` and +:class:`collections.abc.MutableSequence`, respectively. From 75872605aa78dbdfc5c4f025b0f90a7f37ba10c3 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Tue, 5 Nov 2024 15:23:24 +0300 Subject: [PATCH 058/219] gh-126425: Refactor `_lsprof_Profiler_enable` (#126426) - Explicit memory management for `None` objects (since we still try to treat immortal objects as regular objects) - Respect possible errors of `sys.monitoring.register_callback` call --- Modules/_lsprof.c | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/Modules/_lsprof.c b/Modules/_lsprof.c index 4f996c7230e16d..51ad9fc7da8492 100644 --- a/Modules/_lsprof.c +++ b/Modules/_lsprof.c @@ -780,34 +780,47 @@ _lsprof_Profiler_enable_impl(ProfilerObject *self, int subcalls, return NULL; } - if (PyObject_CallMethod(monitoring, "use_tool_id", "is", self->tool_id, "cProfile") == NULL) { + PyObject *check = PyObject_CallMethod(monitoring, + "use_tool_id", "is", + self->tool_id, "cProfile"); + if (check == NULL) { PyErr_Format(PyExc_ValueError, "Another profiling tool is already active"); - Py_DECREF(monitoring); - return NULL; + goto error; } + Py_DECREF(check); for (int i = 0; callback_table[i].callback_method; i++) { + int event = (1 << callback_table[i].event); PyObject* callback = PyObject_GetAttrString((PyObject*)self, callback_table[i].callback_method); if (!callback) { - Py_DECREF(monitoring); - return NULL; + goto error; } - Py_XDECREF(PyObject_CallMethod(monitoring, "register_callback", "iiO", self->tool_id, - (1 << callback_table[i].event), - callback)); + PyObject *register_result = PyObject_CallMethod(monitoring, "register_callback", + "iiO", self->tool_id, + event, callback); Py_DECREF(callback); - all_events |= (1 << callback_table[i].event); + if (register_result == NULL) { + goto error; + } + Py_DECREF(register_result); + all_events |= event; } - if (!PyObject_CallMethod(monitoring, "set_events", "ii", self->tool_id, all_events)) { - Py_DECREF(monitoring); - return NULL; + PyObject *event_result = PyObject_CallMethod(monitoring, "set_events", "ii", + self->tool_id, all_events); + if (event_result == NULL) { + goto error; } + Py_DECREF(event_result); Py_DECREF(monitoring); self->flags |= POF_ENABLED; Py_RETURN_NONE; + +error: + Py_DECREF(monitoring); + return NULL; } static void From bbfd9c92fa3e3d77a86c7858617eb3d09de44fd1 Mon Sep 17 00:00:00 2001 From: "T. Wouters" Date: Tue, 5 Nov 2024 15:49:27 +0100 Subject: [PATCH 059/219] gh-115999: Fix gdb support for libpython.so after thread-local bytecode change (#126440) Fix the gdb pretty printer in the face of --enable-shared by delaying the attempt to load the _PyInterpreterFrame definition until after .so files are loaded. --- Tools/gdb/libpython.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py index ed254152d7da41..698ecbd3b549aa 100755 --- a/Tools/gdb/libpython.py +++ b/Tools/gdb/libpython.py @@ -77,10 +77,14 @@ def _managed_dict_offset(): else: return -3 * _sizeof_void_p() -def _interp_frame_has_tlbc_index(): - interp_frame = gdb.lookup_type("_PyInterpreterFrame") - return any(field.name == "tlbc_index" for field in interp_frame.fields()) - +_INTERP_FRAME_HAS_TLBC_INDEX = None +def interp_frame_has_tlbc_index(): + global _INTERP_FRAME_HAS_TLBC_INDEX + if _INTERP_FRAME_HAS_TLBC_INDEX is None: + interp_frame = gdb.lookup_type("_PyInterpreterFrame") + _INTERP_FRAME_HAS_TLBC_INDEX = any(field.name == "tlbc_index" + for field in interp_frame.fields()) + return _INTERP_FRAME_HAS_TLBC_INDEX Py_TPFLAGS_INLINE_VALUES = (1 << 2) Py_TPFLAGS_MANAGED_DICT = (1 << 4) @@ -109,7 +113,6 @@ def _interp_frame_has_tlbc_index(): UNABLE_READ_INFO_PYTHON_FRAME = 'Unable to read information on python frame' EVALFRAME = '_PyEval_EvalFrameDefault' -INTERP_FRAME_HAS_TLBC_INDEX = _interp_frame_has_tlbc_index() class NullPyObjectPtr(RuntimeError): pass @@ -1101,7 +1104,7 @@ def _f_nlocalsplus(self): def _f_lasti(self): codeunit_p = gdb.lookup_type("_Py_CODEUNIT").pointer() instr_ptr = self._gdbval["instr_ptr"] - if INTERP_FRAME_HAS_TLBC_INDEX: + if interp_frame_has_tlbc_index(): tlbc_index = self._gdbval["tlbc_index"] code_arr = PyCodeArrayPtr(self._f_code().field("co_tlbc")) first_instr = code_arr.get_entry(tlbc_index).cast(codeunit_p) From 0b67ce930a56c4ffd597b1a658ddcbacfb40e798 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 5 Nov 2024 16:05:13 +0100 Subject: [PATCH 060/219] gh-126433: Fix compiler warnings on 32-bit Windows (#126444) --- Modules/_interpchannelsmodule.c | 2 +- Modules/_ssl.c | 6 ++++-- Modules/_winapi.c | 6 +++--- Modules/blake2module.c | 6 +++--- PC/venvlauncher.c | 2 +- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Modules/_interpchannelsmodule.c b/Modules/_interpchannelsmodule.c index 8e6b21db76e01c..68ee429a9e1dfe 100644 --- a/Modules/_interpchannelsmodule.c +++ b/Modules/_interpchannelsmodule.c @@ -2061,7 +2061,7 @@ _channel_get_info(_channels *channels, int64_t cid, struct channel_info *info) if (interp == NULL) { return -1; } - Py_ssize_t interpid = PyInterpreterState_GetID(interp); + int64_t interpid = PyInterpreterState_GetID(interp); // Hold the global lock until we're done. PyThread_acquire_lock(channels->mutex, WAIT_LOCK); diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 54bac28e5beccf..5223e21b5cdb11 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -4923,7 +4923,9 @@ static unsigned int psk_client_callback(SSL *s, goto error; } - if (identity_len_ + 1 > max_identity_len || psk_len_ > max_psk_len) { + if ((size_t)identity_len_ + 1 > max_identity_len + || (size_t)psk_len_ > max_psk_len) + { Py_DECREF(result); goto error; } @@ -5036,7 +5038,7 @@ static unsigned int psk_server_callback(SSL *s, goto error; } - if (psk_len_ > max_psk_len) { + if ((size_t)psk_len_ > max_psk_len) { Py_DECREF(result); goto error; } diff --git a/Modules/_winapi.c b/Modules/_winapi.c index a330b3ff68db62..4ce689fe30e6df 100644 --- a/Modules/_winapi.c +++ b/Modules/_winapi.c @@ -2317,7 +2317,7 @@ _winapi_BatchedWaitForMultipleObjects_impl(PyObject *module, BOOL wait_all, DWORD milliseconds) /*[clinic end generated code: output=d21c1a4ad0a252fd input=7e196f29005dc77b]*/ { - Py_ssize_t thread_count = 0, handle_count = 0, i, j; + Py_ssize_t thread_count = 0, handle_count = 0, i; Py_ssize_t nhandles; BatchedWaitData *thread_data[MAXIMUM_WAIT_OBJECTS]; HANDLE handles[MAXIMUM_WAIT_OBJECTS]; @@ -2378,7 +2378,7 @@ _winapi_BatchedWaitForMultipleObjects_impl(PyObject *module, if (data->handle_count > MAXIMUM_WAIT_OBJECTS - 1) { data->handle_count = MAXIMUM_WAIT_OBJECTS - 1; } - for (j = 0; j < data->handle_count; ++i, ++j) { + for (DWORD j = 0; j < data->handle_count; ++i, ++j) { PyObject *v = PySequence_GetItem(handle_seq, i); if (!v || !PyArg_Parse(v, F_HANDLE, &data->handles[j])) { Py_XDECREF(v); @@ -2526,7 +2526,7 @@ _winapi_BatchedWaitForMultipleObjects_impl(PyObject *module, if (triggered_indices) { for (i = 0; i < thread_count; ++i) { Py_ssize_t triggered = (Py_ssize_t)thread_data[i]->result - WAIT_OBJECT_0; - if (triggered >= 0 && triggered < thread_data[i]->handle_count - 1) { + if (triggered >= 0 && (size_t)triggered < thread_data[i]->handle_count - 1) { PyObject *v = PyLong_FromSsize_t(thread_data[i]->handle_base + triggered); if (!v || PyList_Append(triggered_indices, v) < 0) { Py_XDECREF(v); diff --git a/Modules/blake2module.c b/Modules/blake2module.c index 1ec676c34c6128..94cdfe7fd2e962 100644 --- a/Modules/blake2module.c +++ b/Modules/blake2module.c @@ -474,7 +474,7 @@ py_blake2b_or_s_new(PyTypeObject *type, PyObject *data, int digest_size, /* Validate salt parameter. */ if ((salt->obj != NULL) && salt->len) { - if (salt->len > (is_blake2b(self->impl) ? HACL_HASH_BLAKE2B_SALT_BYTES : HACL_HASH_BLAKE2S_SALT_BYTES)) { + if ((size_t)salt->len > (is_blake2b(self->impl) ? HACL_HASH_BLAKE2B_SALT_BYTES : HACL_HASH_BLAKE2S_SALT_BYTES)) { PyErr_Format(PyExc_ValueError, "maximum salt length is %d bytes", (is_blake2b(self->impl) ? HACL_HASH_BLAKE2B_SALT_BYTES : HACL_HASH_BLAKE2S_SALT_BYTES)); @@ -485,7 +485,7 @@ py_blake2b_or_s_new(PyTypeObject *type, PyObject *data, int digest_size, /* Validate personalization parameter. */ if ((person->obj != NULL) && person->len) { - if (person->len > (is_blake2b(self->impl) ? HACL_HASH_BLAKE2B_PERSONAL_BYTES : HACL_HASH_BLAKE2S_PERSONAL_BYTES)) { + if ((size_t)person->len > (is_blake2b(self->impl) ? HACL_HASH_BLAKE2B_PERSONAL_BYTES : HACL_HASH_BLAKE2S_PERSONAL_BYTES)) { PyErr_Format(PyExc_ValueError, "maximum person length is %d bytes", (is_blake2b(self->impl) ? HACL_HASH_BLAKE2B_PERSONAL_BYTES : HACL_HASH_BLAKE2S_PERSONAL_BYTES)); @@ -534,7 +534,7 @@ py_blake2b_or_s_new(PyTypeObject *type, PyObject *data, int digest_size, /* Set key length. */ if ((key->obj != NULL) && key->len) { - if (key->len > (is_blake2b(self->impl) ? HACL_HASH_BLAKE2B_KEY_BYTES : HACL_HASH_BLAKE2S_KEY_BYTES)) { + if ((size_t)key->len > (is_blake2b(self->impl) ? HACL_HASH_BLAKE2B_KEY_BYTES : HACL_HASH_BLAKE2S_KEY_BYTES)) { PyErr_Format(PyExc_ValueError, "maximum key length is %d bytes", (is_blake2b(self->impl) ? HACL_HASH_BLAKE2B_KEY_BYTES : HACL_HASH_BLAKE2S_KEY_BYTES)); diff --git a/PC/venvlauncher.c b/PC/venvlauncher.c index b1c8d0763d8c76..b6bb0218236ae9 100644 --- a/PC/venvlauncher.c +++ b/PC/venvlauncher.c @@ -223,7 +223,7 @@ find_home_value(const char *buffer, DWORD maxlen, const char **start, DWORD *len return 0; } for (const char *s = strstr(buffer, "home"); - s && ((ptrdiff_t)s - (ptrdiff_t)buffer) < maxlen; + s && (size_t)((ptrdiff_t)s - (ptrdiff_t)buffer) < maxlen; s = strstr(s + 1, "\nhome") ) { if (*s == '\n') { From 8525c9375f25e6ec0c0b5dfcab464703f6e78082 Mon Sep 17 00:00:00 2001 From: Valery Fedorenko Date: Tue, 5 Nov 2024 20:34:33 +0300 Subject: [PATCH 061/219] gh-126238: Fix possible null pointer dereference of freevars in _PyCompile_LookupArg (#126239) * Replace Py_DECREF by Py_XDECREF Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> Co-authored-by: Peter Bierma --- Python/compile.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/compile.c b/Python/compile.c index 4dcb9a1b5acdb3..ecca9b0b06ecf7 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -901,7 +901,7 @@ _PyCompile_LookupArg(compiler *c, PyCodeObject *co, PyObject *name) c->u->u_metadata.u_name, co->co_name, freevars); - Py_DECREF(freevars); + Py_XDECREF(freevars); return ERROR; } return arg; From ff8349979c2ca4e442afc583e1217519611c6c48 Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Tue, 5 Nov 2024 18:43:43 +0000 Subject: [PATCH 062/219] GH-124985: Document that `pathlib.Path.copy()` uses copy-on-write. (#125861) --- Doc/library/pathlib.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index b6fb36554f7cec..a42ac1f8bcdf71 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -1592,6 +1592,11 @@ Copying, moving and deleting This argument has no effect when copying files on Windows (where metadata is always preserved). + .. note:: + Where supported by the operating system and file system, this method + performs a lightweight copy, where data blocks are only copied when + modified. This is known as copy-on-write. + .. versionadded:: 3.14 From f51fd84034e2cbf458321c25ba6fd085a39d6f6f Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Tue, 5 Nov 2024 20:43:52 +0000 Subject: [PATCH 063/219] gh-126074: Removes unnecessary DLLs from embeddable package (GH-126143) --- .../2024-10-29-20-09-52.gh-issue-126074.83ZzZs.rst | 1 + PC/layout/main.py | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Windows/2024-10-29-20-09-52.gh-issue-126074.83ZzZs.rst diff --git a/Misc/NEWS.d/next/Windows/2024-10-29-20-09-52.gh-issue-126074.83ZzZs.rst b/Misc/NEWS.d/next/Windows/2024-10-29-20-09-52.gh-issue-126074.83ZzZs.rst new file mode 100644 index 00000000000000..d4d06b090b5922 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2024-10-29-20-09-52.gh-issue-126074.83ZzZs.rst @@ -0,0 +1 @@ +Removed unnecessary DLLs from Windows embeddable package diff --git a/PC/layout/main.py b/PC/layout/main.py index 0350ed7af3f9b5..8bd435456c635a 100644 --- a/PC/layout/main.py +++ b/PC/layout/main.py @@ -31,11 +31,13 @@ from .support.nuspec import * TEST_PYDS_ONLY = FileStemSet("xxlimited", "xxlimited_35", "_ctypes_test", "_test*") +TEST_DLLS_ONLY = set() TEST_DIRS_ONLY = FileNameSet("test", "tests") IDLE_DIRS_ONLY = FileNameSet("idlelib") -TCLTK_PYDS_ONLY = FileStemSet("tcl*", "tk*", "_tkinter", "zlib1") +TCLTK_PYDS_ONLY = FileStemSet("_tkinter") +TCLTK_DLLS_ONLY = FileStemSet("tcl*", "tk*", "zlib1") TCLTK_DIRS_ONLY = FileNameSet("tkinter", "turtledemo") TCLTK_FILES_ONLY = FileNameSet("turtle.py") @@ -226,6 +228,10 @@ def in_build(f, dest="", new_name=None, no_lib=False): continue if src in EXCLUDE_FROM_DLLS: continue + if src in TEST_DLLS_ONLY and not ns.include_tests: + continue + if src in TCLTK_DLLS_ONLY and not ns.include_tcltk: + continue yield from in_build(src.name, dest=dest, no_lib=True) if ns.zip_lib: From 5e9168492f12c579b2481f3f3e0ae11f9d986857 Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Tue, 5 Nov 2024 21:19:36 +0000 Subject: [PATCH 064/219] pathlib ABCs: defer path joining (#126409) Defer joining of path segments in the private `PurePathBase` ABC. The new behaviour matches how the public `PurePath` class handles path segments. This removes a hard-to-grok difference between the ABCs and the main classes. It also slightly reduces the size of `PurePath` objects by eliminating a `_raw_path` slot. --- Lib/pathlib/_abc.py | 59 ++++++++++++++--------- Lib/pathlib/_local.py | 25 +++------- Lib/test/test_pathlib/test_pathlib_abc.py | 5 -- 3 files changed, 43 insertions(+), 46 deletions(-) diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py index f5eed6f025c250..43e6624934b045 100644 --- a/Lib/pathlib/_abc.py +++ b/Lib/pathlib/_abc.py @@ -99,7 +99,7 @@ class PathGlobber(_GlobberBase): @staticmethod def concat_path(path, text): """Appends text to the given path.""" - return path.with_segments(path._raw_path + text) + return path.with_segments(str(path) + text) class PurePathBase: @@ -112,9 +112,9 @@ class PurePathBase: """ __slots__ = ( - # The `_raw_path` slot store a joined string path. This is set in the - # `__init__()` method. - '_raw_path', + # The `_raw_paths` slot stores unjoined string paths. This is set in + # the `__init__()` method. + '_raw_paths', # The '_resolving' slot stores a boolean indicating whether the path # is being processed by `PathBase.resolve()`. This prevents duplicate @@ -124,11 +124,14 @@ class PurePathBase: parser = ParserBase() _globber = PathGlobber - def __init__(self, path, *paths): - self._raw_path = self.parser.join(path, *paths) if paths else path - if not isinstance(self._raw_path, str): - raise TypeError( - f"path should be a str, not {type(self._raw_path).__name__!r}") + def __init__(self, arg, *args): + paths = [arg] + paths.extend(args) + for path in paths: + if not isinstance(path, str): + raise TypeError( + f"path should be a str, not {type(path).__name__!r}") + self._raw_paths = paths self._resolving = False def with_segments(self, *pathsegments): @@ -141,7 +144,19 @@ def with_segments(self, *pathsegments): def __str__(self): """Return the string representation of the path, suitable for passing to system calls.""" - return self._raw_path + paths = self._raw_paths + if len(paths) == 1: + return paths[0] + elif paths: + # Join path segments from the initializer. + path = self.parser.join(*paths) + # Cache the joined path. + paths.clear() + paths.append(path) + return path + else: + paths.append('') + return '' def as_posix(self): """Return the string representation of the path with forward (/) @@ -166,7 +181,7 @@ def anchor(self): @property def name(self): """The final path component, if any.""" - return self.parser.split(self._raw_path)[1] + return self.parser.split(str(self))[1] @property def suffix(self): @@ -202,7 +217,7 @@ def with_name(self, name): split = self.parser.split if split(name)[0]: raise ValueError(f"Invalid name {name!r}") - return self.with_segments(split(self._raw_path)[0], name) + return self.with_segments(split(str(self))[0], name) def with_stem(self, stem): """Return a new path with the stem changed.""" @@ -242,7 +257,7 @@ def relative_to(self, other, *, walk_up=False): anchor0, parts0 = self._stack anchor1, parts1 = other._stack if anchor0 != anchor1: - raise ValueError(f"{self._raw_path!r} and {other._raw_path!r} have different anchors") + raise ValueError(f"{str(self)!r} and {str(other)!r} have different anchors") while parts0 and parts1 and parts0[-1] == parts1[-1]: parts0.pop() parts1.pop() @@ -250,9 +265,9 @@ def relative_to(self, other, *, walk_up=False): if not part or part == '.': pass elif not walk_up: - raise ValueError(f"{self._raw_path!r} is not in the subpath of {other._raw_path!r}") + raise ValueError(f"{str(self)!r} is not in the subpath of {str(other)!r}") elif part == '..': - raise ValueError(f"'..' segment in {other._raw_path!r} cannot be walked") + raise ValueError(f"'..' segment in {str(other)!r} cannot be walked") else: parts0.append('..') return self.with_segments('', *reversed(parts0)) @@ -289,17 +304,17 @@ def joinpath(self, *pathsegments): paths) or a totally different path (if one of the arguments is anchored). """ - return self.with_segments(self._raw_path, *pathsegments) + return self.with_segments(*self._raw_paths, *pathsegments) def __truediv__(self, key): try: - return self.with_segments(self._raw_path, key) + return self.with_segments(*self._raw_paths, key) except TypeError: return NotImplemented def __rtruediv__(self, key): try: - return self.with_segments(key, self._raw_path) + return self.with_segments(key, *self._raw_paths) except TypeError: return NotImplemented @@ -311,7 +326,7 @@ def _stack(self): *parts* is a reversed list of parts following the anchor. """ split = self.parser.split - path = self._raw_path + path = str(self) parent, name = split(path) names = [] while path != parent: @@ -323,7 +338,7 @@ def _stack(self): @property def parent(self): """The logical parent of the path.""" - path = self._raw_path + path = str(self) parent = self.parser.split(path)[0] if path != parent: parent = self.with_segments(parent) @@ -335,7 +350,7 @@ def parent(self): def parents(self): """A sequence of this path's logical parents.""" split = self.parser.split - path = self._raw_path + path = str(self) parent = split(path)[0] parents = [] while path != parent: @@ -347,7 +362,7 @@ def parents(self): def is_absolute(self): """True if the path is absolute (has both a root and, if applicable, a drive).""" - return self.parser.isabs(self._raw_path) + return self.parser.isabs(str(self)) @property def _pattern_str(self): diff --git a/Lib/pathlib/_local.py b/Lib/pathlib/_local.py index 99474e1f71a307..b27f456d375225 100644 --- a/Lib/pathlib/_local.py +++ b/Lib/pathlib/_local.py @@ -68,10 +68,6 @@ class PurePath(PurePathBase): """ __slots__ = ( - # The `_raw_paths` slot stores unnormalized string paths. This is set - # in the `__init__()` method. - '_raw_paths', - # The `_drv`, `_root` and `_tail_cached` slots store parsed and # normalized parts of the path. They are set when any of the `drive`, # `root` or `_tail` properties are accessed for the first time. The @@ -299,25 +295,14 @@ def _parse_pattern(cls, pattern): parts.append('') return parts - @property - def _raw_path(self): - """The joined but unnormalized path.""" - paths = self._raw_paths - if len(paths) == 0: - path = '' - elif len(paths) == 1: - path = paths[0] - else: - path = self.parser.join(*paths) - return path - @property def drive(self): """The drive prefix (letter or UNC path), if any.""" try: return self._drv except AttributeError: - self._drv, self._root, self._tail_cached = self._parse_path(self._raw_path) + raw_path = PurePathBase.__str__(self) + self._drv, self._root, self._tail_cached = self._parse_path(raw_path) return self._drv @property @@ -326,7 +311,8 @@ def root(self): try: return self._root except AttributeError: - self._drv, self._root, self._tail_cached = self._parse_path(self._raw_path) + raw_path = PurePathBase.__str__(self) + self._drv, self._root, self._tail_cached = self._parse_path(raw_path) return self._root @property @@ -334,7 +320,8 @@ def _tail(self): try: return self._tail_cached except AttributeError: - self._drv, self._root, self._tail_cached = self._parse_path(self._raw_path) + raw_path = PurePathBase.__str__(self) + self._drv, self._root, self._tail_cached = self._parse_path(raw_path) return self._tail_cached @property diff --git a/Lib/test/test_pathlib/test_pathlib_abc.py b/Lib/test/test_pathlib/test_pathlib_abc.py index 4ab804850e9c3e..d155e7c5bb9935 100644 --- a/Lib/test/test_pathlib/test_pathlib_abc.py +++ b/Lib/test/test_pathlib/test_pathlib_abc.py @@ -86,11 +86,6 @@ def test_unsupported_operation_pure(self): p.suffix with self.assertRaises(e): p.suffixes - with self.assertRaises(e): - p / 'bar' - with self.assertRaises(e): - 'bar' / p - self.assertRaises(e, p.joinpath, 'bar') self.assertRaises(e, p.with_name, 'bar') self.assertRaises(e, p.with_stem, 'bar') self.assertRaises(e, p.with_suffix, '.txt') From 478a1c09c4cdb082c84f9102e3e452e6911b84f1 Mon Sep 17 00:00:00 2001 From: Damien <81557462+Damien-Chen@users.noreply.github.com> Date: Wed, 6 Nov 2024 07:10:12 +0800 Subject: [PATCH 065/219] gh-122544: Change OS image in Azure pipeline to Ubuntu 24.04 (#125344) --- .azure-pipelines/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.azure-pipelines/ci.yml b/.azure-pipelines/ci.yml index d3e842d9f31d01..7490dd947e1504 100644 --- a/.azure-pipelines/ci.yml +++ b/.azure-pipelines/ci.yml @@ -5,7 +5,7 @@ jobs: displayName: Pre-build checks pool: - vmImage: ubuntu-22.04 + vmImage: ubuntu-24.04 steps: - template: ./prebuild-checks.yml From c3a12ae13ee0212a096f570064407f8ba954e6aa Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Tue, 5 Nov 2024 15:26:46 -0800 Subject: [PATCH 066/219] GH-125911: Rename big trampoline to "shim" (GH-126339) --- Python/jit.c | 12 +++++------- Tools/jit/_targets.py | 4 ++-- Tools/jit/_writer.py | 4 ++-- Tools/jit/{trampoline.c => shim.c} | 0 4 files changed, 9 insertions(+), 11 deletions(-) rename Tools/jit/{trampoline.c => shim.c} (100%) diff --git a/Python/jit.c b/Python/jit.c index 135daeb1b1da80..90f693dfb7c41b 100644 --- a/Python/jit.c +++ b/Python/jit.c @@ -470,7 +470,7 @@ _PyJIT_Compile(_PyExecutorObject *executor, const _PyUOpInstruction trace[], siz size_t code_size = 0; size_t data_size = 0; jit_state state = {0}; - group = &trampoline; + group = &shim; code_size += group->code_size; data_size += group->data_size; combine_symbol_mask(group->trampoline_mask, state.trampolines.mask); @@ -507,12 +507,10 @@ _PyJIT_Compile(_PyExecutorObject *executor, const _PyUOpInstruction trace[], siz unsigned char *code = memory; unsigned char *data = memory + code_size; state.trampolines.mem = memory + code_size + data_size; - // Compile the trampoline, which handles converting between the native + // Compile the shim, which handles converting between the native // calling convention and the calling convention used by jitted code - // (which may be different for efficiency reasons). On platforms where - // we don't change calling conventions, the trampoline is empty and - // nothing is emitted here: - group = &trampoline; + // (which may be different for efficiency reasons). + group = &shim; group->emit(code, data, executor, NULL, &state); code += group->code_size; data += group->data_size; @@ -536,7 +534,7 @@ _PyJIT_Compile(_PyExecutorObject *executor, const _PyUOpInstruction trace[], siz return -1; } executor->jit_code = memory; - executor->jit_side_entry = memory + trampoline.code_size; + executor->jit_side_entry = memory + shim.code_size; executor->jit_size = total_size; return 0; } diff --git a/Tools/jit/_targets.py b/Tools/jit/_targets.py index 634208da3c8157..d8dce0a905c0f8 100644 --- a/Tools/jit/_targets.py +++ b/Tools/jit/_targets.py @@ -154,8 +154,8 @@ async def _build_stencils(self) -> dict[str, _stencils.StencilGroup]: with tempfile.TemporaryDirectory() as tempdir: work = pathlib.Path(tempdir).resolve() async with asyncio.TaskGroup() as group: - coro = self._compile("trampoline", TOOLS_JIT / "trampoline.c", work) - tasks.append(group.create_task(coro, name="trampoline")) + coro = self._compile("shim", TOOLS_JIT / "shim.c", work) + tasks.append(group.create_task(coro, name="shim")) template = TOOLS_JIT_TEMPLATE_C.read_text() for case, opname in cases_and_opnames: # Write out a copy of the template with *only* this case diff --git a/Tools/jit/_writer.py b/Tools/jit/_writer.py index f33d8ef322f073..81a9f08db31703 100644 --- a/Tools/jit/_writer.py +++ b/Tools/jit/_writer.py @@ -22,11 +22,11 @@ def _dump_footer( yield " symbol_mask trampoline_mask;" yield "} StencilGroup;" yield "" - yield f"static const StencilGroup trampoline = {groups['trampoline'].as_c('trampoline')};" + yield f"static const StencilGroup shim = {groups['shim'].as_c('shim')};" yield "" yield "static const StencilGroup stencil_groups[MAX_UOP_ID + 1] = {" for opname, group in sorted(groups.items()): - if opname == "trampoline": + if opname == "shim": continue yield f" [{opname}] = {group.as_c(opname)}," yield "};" diff --git a/Tools/jit/trampoline.c b/Tools/jit/shim.c similarity index 100% rename from Tools/jit/trampoline.c rename to Tools/jit/shim.c From fc233f46d3761b4e808be2c44fda0b843179004e Mon Sep 17 00:00:00 2001 From: Damien <81557462+Damien-Chen@users.noreply.github.com> Date: Wed, 6 Nov 2024 07:29:21 +0800 Subject: [PATCH 067/219] gh-122544: Change OS image in GitHub Actions to Ubuntu 24.04 (#122566) --- .github/workflows/build.yml | 8 ++++---- .github/workflows/posix-deps-apt.sh | 1 - .github/workflows/reusable-tsan.yml | 2 +- .github/workflows/reusable-ubuntu.yml | 2 +- .github/workflows/reusable-wasi.yml | 2 +- 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 083b07156674df..f63c4606220494 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -88,7 +88,7 @@ jobs: name: 'Check if generated files are up to date' # Don't use ubuntu-latest but a specific version to make the job # reproducible: to get the same tools versions (autoconf, aclocal, ...) - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 timeout-minutes: 60 needs: check_source if: needs.check_source.outputs.run_tests == 'true' @@ -237,7 +237,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-22.04] + os: [ubuntu-24.04] openssl_ver: [3.0.15, 3.1.7, 3.2.3, 3.3.2] env: OPENSSL_VER: ${{ matrix.openssl_ver }} @@ -297,7 +297,7 @@ jobs: test_hypothesis: name: "Hypothesis tests on Ubuntu" - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 timeout-minutes: 60 needs: check_source if: needs.check_source.outputs.run_tests == 'true' && needs.check_source.outputs.run_hypothesis == 'true' @@ -417,7 +417,7 @@ jobs: if: needs.check_source.outputs.run_tests == 'true' strategy: matrix: - os: [ubuntu-22.04] + os: [ubuntu-24.04] env: OPENSSL_VER: 3.0.15 PYTHONSTRICTEXTENSIONBUILD: 1 diff --git a/.github/workflows/posix-deps-apt.sh b/.github/workflows/posix-deps-apt.sh index bfc5a0874281bd..d5538cd9367ec6 100755 --- a/.github/workflows/posix-deps-apt.sh +++ b/.github/workflows/posix-deps-apt.sh @@ -13,7 +13,6 @@ apt-get -yq install \ libgdbm-dev \ libgdbm-compat-dev \ liblzma-dev \ - libmpdec-dev \ libncurses5-dev \ libreadline6-dev \ libsqlite3-dev \ diff --git a/.github/workflows/reusable-tsan.yml b/.github/workflows/reusable-tsan.yml index 65072efa8e9023..7a4d81f0bdcad1 100644 --- a/.github/workflows/reusable-tsan.yml +++ b/.github/workflows/reusable-tsan.yml @@ -21,7 +21,7 @@ on: jobs: build_tsan_reusable: name: 'Thread sanitizer' - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 timeout-minutes: 60 steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/reusable-ubuntu.yml b/.github/workflows/reusable-ubuntu.yml index f0ca6a9e7ed793..ec39025504efd1 100644 --- a/.github/workflows/reusable-ubuntu.yml +++ b/.github/workflows/reusable-ubuntu.yml @@ -20,7 +20,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-22.04] + os: [ubuntu-24.04] env: FORCE_COLOR: 1 OPENSSL_VER: 3.0.15 diff --git a/.github/workflows/reusable-wasi.yml b/.github/workflows/reusable-wasi.yml index abc617a317cc0f..85af793c342c51 100644 --- a/.github/workflows/reusable-wasi.yml +++ b/.github/workflows/reusable-wasi.yml @@ -11,7 +11,7 @@ jobs: build_wasi_reusable: name: 'build and test' timeout-minutes: 60 - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 env: WASMTIME_VERSION: 22.0.0 WASI_SDK_VERSION: 24 From 83ba8c2bba834c0b92de669cac16fcda17485e0e Mon Sep 17 00:00:00 2001 From: blhsing Date: Wed, 6 Nov 2024 07:53:54 +0800 Subject: [PATCH 068/219] gh-70764: inspect.getclosurevars now identifies global variables with LOAD_GLOBAL (#120143) --- Lib/inspect.py | 14 +++++++++----- Lib/test/test_inspect/test_inspect.py | 13 +++++++++++++ .../2024-06-06-04-06-05.gh-issue-70764.6511hw.rst | 1 + 3 files changed, 23 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-06-06-04-06-05.gh-issue-70764.6511hw.rst diff --git a/Lib/inspect.py b/Lib/inspect.py index 08718d82e91582..e3f74e9f047eaf 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -1507,11 +1507,15 @@ def getclosurevars(func): global_vars = {} builtin_vars = {} unbound_names = set() - for name in code.co_names: - if name in ("None", "True", "False"): - # Because these used to be builtins instead of keywords, they - # may still show up as name references. We ignore them. - continue + global_names = set() + for instruction in dis.get_instructions(code): + opname = instruction.opname + name = instruction.argval + if opname == "LOAD_ATTR": + unbound_names.add(name) + elif opname == "LOAD_GLOBAL": + global_names.add(name) + for name in global_names: try: global_vars[name] = global_ns[name] except KeyError: diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index a4430a868676e2..a92627a4d60f68 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -1960,6 +1960,19 @@ def g(local_ref): builtin_vars, unbound_names) self.assertEqual(inspect.getclosurevars(C().f(_arg)), expected) + def test_attribute_same_name_as_global_var(self): + class C: + _global_ref = object() + def f(): + print(C._global_ref, _global_ref) + nonlocal_vars = {"C": C} + global_vars = {"_global_ref": _global_ref} + builtin_vars = {"print": print} + unbound_names = {"_global_ref"} + expected = inspect.ClosureVars(nonlocal_vars, global_vars, + builtin_vars, unbound_names) + self.assertEqual(inspect.getclosurevars(f), expected) + def test_nonlocal_vars(self): # More complex tests of nonlocal resolution def _nonlocal_vars(f): diff --git a/Misc/NEWS.d/next/Library/2024-06-06-04-06-05.gh-issue-70764.6511hw.rst b/Misc/NEWS.d/next/Library/2024-06-06-04-06-05.gh-issue-70764.6511hw.rst new file mode 100644 index 00000000000000..4cfb66a6ccc6ee --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-06-06-04-06-05.gh-issue-70764.6511hw.rst @@ -0,0 +1 @@ +Fixed an issue where :func:`inspect.getclosurevars` would incorrectly classify an attribute name as a global variable when the name exists both as an attribute name and a global variable. From a204c63919ca7ce528d8e3ab4196a4aa1a2b6ac4 Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Tue, 5 Nov 2024 19:09:04 -0800 Subject: [PATCH 069/219] GH-126464: Temporarily disable `aarch64-apple-darwin` JIT CI jobs (gh-126465) * Temporarily disable aarch64-apple-darwin JIT CI jobs * Also up here --- .github/workflows/jit.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/jit.yml b/.github/workflows/jit.yml index 48f05818a38f96..897c692118e9a4 100644 --- a/.github/workflows/jit.yml +++ b/.github/workflows/jit.yml @@ -52,7 +52,7 @@ jobs: - x86_64-pc-windows-msvc/msvc - aarch64-pc-windows-msvc/msvc - x86_64-apple-darwin/clang - - aarch64-apple-darwin/clang + # - aarch64-apple-darwin/clang - x86_64-unknown-linux-gnu/gcc - x86_64-unknown-linux-gnu/clang - aarch64-unknown-linux-gnu/gcc @@ -79,10 +79,11 @@ jobs: architecture: x86_64 runner: macos-13 compiler: clang - - target: aarch64-apple-darwin/clang - architecture: aarch64 - runner: macos-14 - compiler: clang + # GH-126464: A recent change to either GHA or LLVM broke this job: + # - target: aarch64-apple-darwin/clang + # architecture: aarch64 + # runner: macos-14 + # compiler: clang - target: x86_64-unknown-linux-gnu/gcc architecture: x86_64 runner: ubuntu-22.04 From 4ea214ea982944b59ff543a5c6f4ec782a47588c Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Wed, 6 Nov 2024 12:35:10 +0900 Subject: [PATCH 070/219] gh-115999: Add free-threaded specialization for CONTAINS_OP (gh-126450) - The specialization logic determines the appropriate specialization using only the operand's type, which is safe to read non-atomically (changing it requires stopping the world). We are guaranteed that the type will not change in between when it is checked and when we specialize the bytecode because the types involved are immutable (you cannot assign to `__class__` for exact instances of `dict`, `set`, or `frozenset`). The bytecode is mutated atomically using helpers. - The specialized instructions rely on the operand type not changing in between the `DEOPT_IF` checks and the calls to the appropriate type-specific helpers (e.g. `_PySet_Contains`). This is a correctness requirement in the default builds and there are no changes to the opcodes in the free-threaded builds that would invalidate this. --- Lib/test/test_dis.py | 21 +++++++++++++++++++++ Python/bytecodes.c | 2 +- Python/generated_cases.c.h | 2 +- Python/specialize.c | 10 ++++++---- 4 files changed, 29 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index a991c67fca46be..337ee3bbb05136 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -1335,6 +1335,27 @@ def test_call_specialize(self): got = self.get_disassembly(co, adaptive=True) self.do_disassembly_compare(got, call_quicken) + @cpython_only + @requires_specialization_ft + def test_contains_specialize(self): + contains_op_quicken = """\ + 0 RESUME_CHECK 0 + + 1 LOAD_NAME 0 (a) + LOAD_NAME 1 (b) + %s + RETURN_VALUE +""" + co_dict = compile('a in b', "", "eval") + self.code_quicken(lambda: exec(co_dict, {}, {'a': 1, 'b': {1: 5}})) + got = self.get_disassembly(co_dict, adaptive=True) + self.do_disassembly_compare(got, contains_op_quicken % "CONTAINS_OP_DICT 0 (in)") + + co_set = compile('a in b', "", "eval") + self.code_quicken(lambda: exec(co_set, {}, {'a': 1.0, 'b': {1, 2, 3}})) + got = self.get_disassembly(co_set, adaptive=True) + self.do_disassembly_compare(got, contains_op_quicken % "CONTAINS_OP_SET 0 (in)") + @cpython_only @requires_specialization def test_loop_quicken(self): diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 8c52db6ab68436..7ae0f20369641a 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2508,7 +2508,7 @@ dummy_func( } specializing op(_SPECIALIZE_CONTAINS_OP, (counter/1, left, right -- left, right)) { - #if ENABLE_SPECIALIZATION + #if ENABLE_SPECIALIZATION_FT if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { next_instr = this_instr; _Py_Specialize_ContainsOp(right, next_instr); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index d346875ea4455f..03b4d2224922f0 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -3395,7 +3395,7 @@ right = stack_pointer[-1]; uint16_t counter = read_u16(&this_instr[1].cache); (void)counter; - #if ENABLE_SPECIALIZATION + #if ENABLE_SPECIALIZATION_FT if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { next_instr = this_instr; _PyFrame_SetStackPointer(frame, stack_pointer); diff --git a/Python/specialize.c b/Python/specialize.c index 86cb997ca2ced3..17e661b2bd3c76 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -2747,25 +2747,27 @@ _Py_Specialize_ContainsOp(_PyStackRef value_st, _Py_CODEUNIT *instr) { PyObject *value = PyStackRef_AsPyObjectBorrow(value_st); - assert(ENABLE_SPECIALIZATION); + assert(ENABLE_SPECIALIZATION_FT); assert(_PyOpcode_Caches[CONTAINS_OP] == INLINE_CACHE_ENTRIES_COMPARE_OP); + uint8_t specialized_op; _PyContainsOpCache *cache = (_PyContainsOpCache *)(instr + 1); if (PyDict_CheckExact(value)) { - instr->op.code = CONTAINS_OP_DICT; + specialized_op = CONTAINS_OP_DICT; goto success; } if (PySet_CheckExact(value) || PyFrozenSet_CheckExact(value)) { - instr->op.code = CONTAINS_OP_SET; + specialized_op = CONTAINS_OP_SET; goto success; } SPECIALIZATION_FAIL(CONTAINS_OP, containsop_fail_kind(value)); STAT_INC(CONTAINS_OP, failure); - instr->op.code = CONTAINS_OP; + SET_OPCODE_OR_RETURN(instr, CONTAINS_OP); cache->counter = adaptive_counter_backoff(cache->counter); return; success: STAT_INC(CONTAINS_OP, success); + SET_OPCODE_OR_RETURN(instr, specialized_op); cache->counter = adaptive_counter_cooldown(); } From 6431f379b86c2f41a9a9f5a54e77d4e8d556489d Mon Sep 17 00:00:00 2001 From: Cody Maloney Date: Tue, 5 Nov 2024 22:54:40 -0800 Subject: [PATCH 071/219] gh-120754: Add to `io` optimization to what's new (#126466) --- Doc/whatsnew/3.14.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 9300de440cdc30..b9d2c27eb9a321 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -471,6 +471,15 @@ asyncio reduces memory usage. (Contributed by Kumar Aditya in :gh:`107803`.) +io +--- +* :mod:`io` which provides the built-in :func:`open` makes less system calls + when opening regular files as well as reading whole files. Reading a small + operating system cached file in full is up to 15% faster. + :func:`pathlib.Path.read_bytes` has the most optimizations for reading a + file's bytes in full. (Contributed by Cody Maloney and Victor Stinner in + :gh:`120754` and :gh:`90102`.) + Deprecated ========== From 09d7083962062acfef7e7a9a309a01fb70ad8276 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 6 Nov 2024 11:20:17 +0100 Subject: [PATCH 072/219] gh-126433: Change channel_info.count to int64_t (#126447) Fix compiler warnings on 32-bit Windows: change channel_info.count type from Py_ssize_t to int64_t in _interpchannelsmodule.c. --- Modules/_interpchannelsmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_interpchannelsmodule.c b/Modules/_interpchannelsmodule.c index 68ee429a9e1dfe..5dc032b46cac9a 100644 --- a/Modules/_interpchannelsmodule.c +++ b/Modules/_interpchannelsmodule.c @@ -2047,7 +2047,7 @@ struct channel_info { int recv; } cur; } status; - Py_ssize_t count; + int64_t count; }; static int From b1c4ffc20573befb4db66bbbdd569b9bd13bb127 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 6 Nov 2024 11:59:39 +0100 Subject: [PATCH 073/219] gh-126455: Disallow _ssl.SSLSocket instantiation (#126481) Prevent creation of incomplete/invalid _ssl.SSLSocket objects when created directly. --- Modules/_ssl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 5223e21b5cdb11..b6b5ebf094c938 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -2979,7 +2979,7 @@ static PyType_Spec PySSLSocket_spec = { .name = "_ssl._SSLSocket", .basicsize = sizeof(PySSLSocket), .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE | - Py_TPFLAGS_HAVE_GC), + Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_DISALLOW_INSTANTIATION), .slots = PySSLSocket_slots, }; From 9cba47d9f151734815a61e32391ea7fca877ea55 Mon Sep 17 00:00:00 2001 From: Kamil Turek Date: Wed, 6 Nov 2024 13:16:13 +0100 Subject: [PATCH 074/219] gh-122838: Document missing opcodes (#123073) --- Doc/library/dis.rst | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst index ecbe0fae8cd74c..e2926f2440af6d 100644 --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -1395,6 +1395,13 @@ iterations of the loop. This opcode is now only used in situations where the local variable is guaranteed to be initialized. It cannot raise :exc:`UnboundLocalError`. +.. opcode:: LOAD_FAST_LOAD_FAST (var_nums) + + Pushes references to ``co_varnames[var_nums >> 4]`` and + ``co_varnames[var_nums & 15]`` onto the stack. + + .. versionadded:: 3.13 + .. opcode:: LOAD_FAST_CHECK (var_num) Pushes a reference to the local ``co_varnames[var_num]`` onto the stack, @@ -1415,6 +1422,20 @@ iterations of the loop. Stores ``STACK.pop()`` into the local ``co_varnames[var_num]``. +.. opcode:: STORE_FAST_STORE_FAST (var_nums) + + Stores ``STACK[-1]`` into ``co_varnames[var_nums >> 4]`` + and ``STACK[-2]`` into ``co_varnames[var_nums & 15]``. + + .. versionadded:: 3.13 + +.. opcode:: STORE_FAST_LOAD_FAST (var_nums) + + Stores ``STACK.pop()`` into the local ``co_varnames[var_nums >> 4]`` + and pushes a reference to the local ``co_varnames[var_nums & 15]`` + onto the stack. + + .. versionadded:: 3.13 .. opcode:: DELETE_FAST (var_num) From a1c57bcfd2bcbc55ff858407e09c1d8d8cee44e6 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 6 Nov 2024 14:24:46 +0100 Subject: [PATCH 075/219] gh-126461: Fix _Unpickler_ReadFromFile() error handling (#126485) Handle _Unpickler_SetStringInput() failure. --- Modules/_pickle.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Modules/_pickle.c b/Modules/_pickle.c index b2bd9545c1b130..863da6878409f3 100644 --- a/Modules/_pickle.c +++ b/Modules/_pickle.c @@ -1288,6 +1288,10 @@ _Unpickler_ReadFromFile(UnpicklerObject *self, Py_ssize_t n) else { read_size = _Unpickler_SetStringInput(self, data); Py_DECREF(data); + if (read_size < 0) { + return -1; + } + self->prefetched_idx = 0; if (n <= read_size) return n; From 9ce4fa0719d291070b6a66fe25716ef1e81448fc Mon Sep 17 00:00:00 2001 From: mpage Date: Wed, 6 Nov 2024 12:04:04 -0800 Subject: [PATCH 076/219] gh-115999: Introduce helpers for (un)specializing instructions (#126414) Introduce helpers for (un)specializing instructions Consolidate the code to specialize/unspecialize instructions into two helper functions and use them in `_Py_Specialize_BinaryOp`. The resulting code is more concise and keeps all of the logic at the point where we decide to specialize/unspecialize an instruction. --- Python/specialize.c | 132 ++++++++++++++++++++++++++++---------------- 1 file changed, 84 insertions(+), 48 deletions(-) diff --git a/Python/specialize.c b/Python/specialize.c index 17e661b2bd3c76..2673e16e596a1a 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -24,25 +24,6 @@ extern const char *_PyUOpName(int index); * ./adaptive.md */ -#ifdef Py_GIL_DISABLED -#define SET_OPCODE_OR_RETURN(instr, opcode) \ - do { \ - uint8_t old_op = _Py_atomic_load_uint8_relaxed(&(instr)->op.code); \ - if (old_op >= MIN_INSTRUMENTED_OPCODE) { \ - /* Lost race with instrumentation */ \ - return; \ - } \ - if (!_Py_atomic_compare_exchange_uint8(&(instr)->op.code, &old_op, \ - (opcode))) { \ - /* Lost race with instrumentation */ \ - assert(old_op >= MIN_INSTRUMENTED_OPCODE); \ - return; \ - } \ - } while (0) -#else -#define SET_OPCODE_OR_RETURN(instr, opcode) (instr)->op.code = (opcode) -#endif - #ifdef Py_STATS GCStats _py_gc_stats[NUM_GENERATIONS] = { 0 }; static PyStats _Py_stats_struct = { .gc_stats = _py_gc_stats }; @@ -687,6 +668,73 @@ _PyCode_Quicken(_Py_CODEUNIT *instructions, Py_ssize_t size, PyObject *consts, #define SPEC_FAIL_CONTAINS_OP_LIST 11 #define SPEC_FAIL_CONTAINS_OP_USER_CLASS 12 +static inline int +set_opcode(_Py_CODEUNIT *instr, uint8_t opcode) +{ +#ifdef Py_GIL_DISABLED + uint8_t old_op = _Py_atomic_load_uint8_relaxed(&instr->op.code); + if (old_op >= MIN_INSTRUMENTED_OPCODE) { + /* Lost race with instrumentation */ + return 0; + } + if (!_Py_atomic_compare_exchange_uint8(&instr->op.code, &old_op, opcode)) { + /* Lost race with instrumentation */ + assert(old_op >= MIN_INSTRUMENTED_OPCODE); + return 0; + } + return 1; +#else + instr->op.code = opcode; + return 1; +#endif +} + +static inline void +set_counter(_Py_BackoffCounter *counter, _Py_BackoffCounter value) +{ + FT_ATOMIC_STORE_UINT16_RELAXED(counter->value_and_backoff, + value.value_and_backoff); +} + +static inline _Py_BackoffCounter +load_counter(_Py_BackoffCounter *counter) +{ + _Py_BackoffCounter result = { + .value_and_backoff = + FT_ATOMIC_LOAD_UINT16_RELAXED(counter->value_and_backoff)}; + return result; +} + +static inline void +specialize(_Py_CODEUNIT *instr, uint8_t specialized_opcode) +{ + assert(!PyErr_Occurred()); + if (!set_opcode(instr, specialized_opcode)) { + STAT_INC(_PyOpcode_Deopt[specialized_opcode], failure); + SPECIALIZATION_FAIL(_PyOpcode_Deopt[specialized_opcode], + SPEC_FAIL_OTHER); + return; + } + set_counter((_Py_BackoffCounter *)instr + 1, adaptive_counter_cooldown()); +} + +static inline void +unspecialize(_Py_CODEUNIT *instr, int reason) +{ + assert(!PyErr_Occurred()); + uint8_t opcode = FT_ATOMIC_LOAD_UINT8_RELAXED(instr->op.code); + uint8_t generic_opcode = _PyOpcode_Deopt[opcode]; + STAT_INC(generic_opcode, failure); + if (!set_opcode(instr, generic_opcode)) { + SPECIALIZATION_FAIL(generic_opcode, SPEC_FAIL_OTHER); + return; + } + SPECIALIZATION_FAIL(generic_opcode, reason); + _Py_BackoffCounter *counter = (_Py_BackoffCounter *)instr + 1; + _Py_BackoffCounter cur = load_counter(counter); + set_counter(counter, adaptive_counter_backoff(cur)); +} + static int function_kind(PyCodeObject *code); static bool function_check_args(PyObject *o, int expected_argcount, int opcode); static uint32_t function_get_version(PyObject *o, int opcode); @@ -2195,7 +2243,6 @@ _Py_Specialize_CallKw(_PyStackRef callable_st, _Py_CODEUNIT *instr, int nargs) } } -#ifdef Py_STATS static int binary_op_fail_kind(int oparg, PyObject *lhs, PyObject *rhs) { @@ -2263,7 +2310,6 @@ binary_op_fail_kind(int oparg, PyObject *lhs, PyObject *rhs) } Py_UNREACHABLE(); } -#endif // Py_STATS void _Py_Specialize_BinaryOp(_PyStackRef lhs_st, _PyStackRef rhs_st, _Py_CODEUNIT *instr, @@ -2273,8 +2319,6 @@ _Py_Specialize_BinaryOp(_PyStackRef lhs_st, _PyStackRef rhs_st, _Py_CODEUNIT *in PyObject *rhs = PyStackRef_AsPyObjectBorrow(rhs_st); assert(ENABLE_SPECIALIZATION_FT); assert(_PyOpcode_Caches[BINARY_OP] == INLINE_CACHE_ENTRIES_BINARY_OP); - _PyBinaryOpCache *cache = (_PyBinaryOpCache *)(instr + 1); - uint8_t specialized_op; switch (oparg) { case NB_ADD: case NB_INPLACE_ADD: @@ -2285,19 +2329,19 @@ _Py_Specialize_BinaryOp(_PyStackRef lhs_st, _PyStackRef rhs_st, _Py_CODEUNIT *in _Py_CODEUNIT next = instr[INLINE_CACHE_ENTRIES_BINARY_OP + 1]; bool to_store = (next.op.code == STORE_FAST); if (to_store && PyStackRef_AsPyObjectBorrow(locals[next.op.arg]) == lhs) { - specialized_op = BINARY_OP_INPLACE_ADD_UNICODE; - goto success; + specialize(instr, BINARY_OP_INPLACE_ADD_UNICODE); + return; } - specialized_op = BINARY_OP_ADD_UNICODE; - goto success; + specialize(instr, BINARY_OP_ADD_UNICODE); + return; } if (PyLong_CheckExact(lhs)) { - specialized_op = BINARY_OP_ADD_INT; - goto success; + specialize(instr, BINARY_OP_ADD_INT); + return; } if (PyFloat_CheckExact(lhs)) { - specialized_op = BINARY_OP_ADD_FLOAT; - goto success; + specialize(instr, BINARY_OP_ADD_FLOAT); + return; } break; case NB_MULTIPLY: @@ -2306,12 +2350,12 @@ _Py_Specialize_BinaryOp(_PyStackRef lhs_st, _PyStackRef rhs_st, _Py_CODEUNIT *in break; } if (PyLong_CheckExact(lhs)) { - specialized_op = BINARY_OP_MULTIPLY_INT; - goto success; + specialize(instr, BINARY_OP_MULTIPLY_INT); + return; } if (PyFloat_CheckExact(lhs)) { - specialized_op = BINARY_OP_MULTIPLY_FLOAT; - goto success; + specialize(instr, BINARY_OP_MULTIPLY_FLOAT); + return; } break; case NB_SUBTRACT: @@ -2320,24 +2364,16 @@ _Py_Specialize_BinaryOp(_PyStackRef lhs_st, _PyStackRef rhs_st, _Py_CODEUNIT *in break; } if (PyLong_CheckExact(lhs)) { - specialized_op = BINARY_OP_SUBTRACT_INT; - goto success; + specialize(instr, BINARY_OP_SUBTRACT_INT); + return; } if (PyFloat_CheckExact(lhs)) { - specialized_op = BINARY_OP_SUBTRACT_FLOAT; - goto success; + specialize(instr, BINARY_OP_SUBTRACT_FLOAT); + return; } break; } - SPECIALIZATION_FAIL(BINARY_OP, binary_op_fail_kind(oparg, lhs, rhs)); - STAT_INC(BINARY_OP, failure); - SET_OPCODE_OR_RETURN(instr, BINARY_OP); - cache->counter = adaptive_counter_backoff(cache->counter); - return; -success: - STAT_INC(BINARY_OP, success); - SET_OPCODE_OR_RETURN(instr, specialized_op); - cache->counter = adaptive_counter_cooldown(); + unspecialize(instr, binary_op_fail_kind(oparg, lhs, rhs)); } From e56fd449fbb9ac099f389806d4c494fa66fca248 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Thu, 7 Nov 2024 05:23:47 +0900 Subject: [PATCH 077/219] gh-115999: Move specializer test from test_dis to test_opcache (gh-126498) --- Lib/test/test_dis.py | 45 +----------------------------- Lib/test/test_opcache.py | 59 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 59 insertions(+), 45 deletions(-) diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 337ee3bbb05136..f26411ace8fa73 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -10,8 +10,7 @@ import types import unittest from test.support import (captured_stdout, requires_debug_ranges, - requires_specialization, requires_specialization_ft, - cpython_only) + requires_specialization, cpython_only) from test.support.bytecode_helper import BytecodeTestCase import opcode @@ -1261,27 +1260,6 @@ def test_super_instructions(self): got = self.get_disassembly(load_test, adaptive=True) self.do_disassembly_compare(got, dis_load_test_quickened_code) - @cpython_only - @requires_specialization_ft - def test_binary_specialize(self): - binary_op_quicken = """\ - 0 RESUME_CHECK 0 - - 1 LOAD_NAME 0 (a) - LOAD_NAME 1 (b) - %s - RETURN_VALUE -""" - co_int = compile('a + b', "", "eval") - self.code_quicken(lambda: exec(co_int, {}, {'a': 1, 'b': 2})) - got = self.get_disassembly(co_int, adaptive=True) - self.do_disassembly_compare(got, binary_op_quicken % "BINARY_OP_ADD_INT 0 (+)") - - co_unicode = compile('a + b', "", "eval") - self.code_quicken(lambda: exec(co_unicode, {}, {'a': 'a', 'b': 'b'})) - got = self.get_disassembly(co_unicode, adaptive=True) - self.do_disassembly_compare(got, binary_op_quicken % "BINARY_OP_ADD_UNICODE 0 (+)") - @cpython_only @requires_specialization def test_binary_subscr_specialize(self): @@ -1335,27 +1313,6 @@ def test_call_specialize(self): got = self.get_disassembly(co, adaptive=True) self.do_disassembly_compare(got, call_quicken) - @cpython_only - @requires_specialization_ft - def test_contains_specialize(self): - contains_op_quicken = """\ - 0 RESUME_CHECK 0 - - 1 LOAD_NAME 0 (a) - LOAD_NAME 1 (b) - %s - RETURN_VALUE -""" - co_dict = compile('a in b', "", "eval") - self.code_quicken(lambda: exec(co_dict, {}, {'a': 1, 'b': {1: 5}})) - got = self.get_disassembly(co_dict, adaptive=True) - self.do_disassembly_compare(got, contains_op_quicken % "CONTAINS_OP_DICT 0 (in)") - - co_set = compile('a in b', "", "eval") - self.code_quicken(lambda: exec(co_set, {}, {'a': 1.0, 'b': {1, 2, 3}})) - got = self.get_disassembly(co_set, adaptive=True) - self.do_disassembly_compare(got, contains_op_quicken % "CONTAINS_OP_SET 0 (in)") - @cpython_only @requires_specialization def test_loop_quicken(self): diff --git a/Lib/test/test_opcache.py b/Lib/test/test_opcache.py index cdcddb0d717f23..78e4bf44f7ea0c 100644 --- a/Lib/test/test_opcache.py +++ b/Lib/test/test_opcache.py @@ -4,7 +4,9 @@ import threading import types import unittest -from test.support import threading_helper, check_impl_detail, requires_specialization +from test.support import (threading_helper, check_impl_detail, + requires_specialization, requires_specialization_ft, + cpython_only) from test.support.import_helper import import_module # Skip this module on other interpreters, it is cpython specific: @@ -34,6 +36,11 @@ def assert_specialized(self, f, opname): opnames = {instruction.opname for instruction in instructions} self.assertIn(opname, opnames) + def assert_no_opcode(self, f, opname): + instructions = dis.get_instructions(f, adaptive=True) + opnames = {instruction.opname for instruction in instructions} + self.assertNotIn(opname, opnames) + class TestLoadSuperAttrCache(unittest.TestCase): def test_descriptor_not_double_executed_on_spec_fail(self): @@ -1200,5 +1207,55 @@ def f(o, n): self.assertEqual(test_obj.b, 0) +class TestSpecializer(TestBase): + + @cpython_only + @requires_specialization_ft + def test_binary_op(self): + def f(): + for _ in range(100): + a, b = 1, 2 + c = a + b + self.assertEqual(c, 3) + + f() + self.assert_specialized(f, "BINARY_OP_ADD_INT") + self.assert_no_opcode(f, "BINARY_OP") + + def g(): + for _ in range(100): + a, b = "foo", "bar" + c = a + b + self.assertEqual(c, "foobar") + + g() + self.assert_specialized(g, "BINARY_OP_ADD_UNICODE") + self.assert_no_opcode(g, "BINARY_OP") + + @cpython_only + @requires_specialization_ft + def test_contain_op(self): + def f(): + for _ in range(100): + a, b = 1, {1: 2, 2: 5} + self.assertTrue(a in b) + self.assertFalse(3 in b) + + f() + self.assert_specialized(f, "CONTAINS_OP_DICT") + self.assert_no_opcode(f, "CONTAINS_OP") + + def g(): + for _ in range(100): + a, b = 1, {1, 2} + self.assertTrue(a in b) + self.assertFalse(3 in b) + + g() + self.assert_specialized(g, "CONTAINS_OP_SET") + self.assert_no_opcode(g, "CONTAINS_OP") + + + if __name__ == "__main__": unittest.main() From 8fa4dc4ba8646c59f945f2451c53e2919f066065 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 6 Nov 2024 22:25:14 +0200 Subject: [PATCH 078/219] gh-126489: Do not call persistent_id() for a persistent id in Python pickle (GH-126490) --- Lib/pickle.py | 9 +++++---- Lib/test/test_pickle.py | 6 ++++++ .../2024-11-06-13-41-38.gh-issue-126489.toaf-0.rst | 3 +++ 3 files changed, 14 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-11-06-13-41-38.gh-issue-126489.toaf-0.rst diff --git a/Lib/pickle.py b/Lib/pickle.py index 965e1952fb8c5e..25dadb3f75a573 100644 --- a/Lib/pickle.py +++ b/Lib/pickle.py @@ -548,10 +548,11 @@ def save(self, obj, save_persistent_id=True): self.framer.commit_frame() # Check for persistent id (defined by a subclass) - pid = self.persistent_id(obj) - if pid is not None and save_persistent_id: - self.save_pers(pid) - return + if save_persistent_id: + pid = self.persistent_id(obj) + if pid is not None: + self.save_pers(pid) + return # Check the memo x = self.memo.get(id(obj)) diff --git a/Lib/test/test_pickle.py b/Lib/test/test_pickle.py index c84e507cdf645f..9ec2eb97147fae 100644 --- a/Lib/test/test_pickle.py +++ b/Lib/test/test_pickle.py @@ -224,25 +224,31 @@ def persistent_load(pid): def test_pickler_super(self): class PersPickler(self.pickler): def persistent_id(subself, obj): + called.append(obj) self.assertIsNone(super().persistent_id(obj)) return obj for proto in range(pickle.HIGHEST_PROTOCOL + 1): f = io.BytesIO() pickler = PersPickler(f, proto) + called = [] pickler.dump('abc') + self.assertEqual(called, ['abc']) self.assertEqual(self.loads(f.getvalue()), 'abc') def test_unpickler_super(self): class PersUnpickler(self.unpickler): def persistent_load(subself, pid): + called.append(pid) with self.assertRaises(self.persistent_load_error): super().persistent_load(pid) return pid for proto in range(pickle.HIGHEST_PROTOCOL + 1): unpickler = PersUnpickler(io.BytesIO(self.dumps('abc', proto))) + called = [] self.assertEqual(unpickler.load(), 'abc') + self.assertEqual(called, ['abc']) class PyPicklerUnpicklerObjectTests(AbstractPicklerUnpicklerObjectTests, unittest.TestCase): diff --git a/Misc/NEWS.d/next/Library/2024-11-06-13-41-38.gh-issue-126489.toaf-0.rst b/Misc/NEWS.d/next/Library/2024-11-06-13-41-38.gh-issue-126489.toaf-0.rst new file mode 100644 index 00000000000000..8a6573cdea7b42 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-11-06-13-41-38.gh-issue-126489.toaf-0.rst @@ -0,0 +1,3 @@ +The Python implementation of :mod:`pickle` no longer calls +:meth:`pickle.Pickler.persistent_id` for the result of +:meth:`!persistent_id`. From 6e03ff2419a7faf514ad833dc513175c4f7e9bc7 Mon Sep 17 00:00:00 2001 From: Kirill Podoprigora Date: Wed, 6 Nov 2024 23:52:15 +0200 Subject: [PATCH 079/219] gh-126513: Use helpers for `_Py_Specialize_ConstainsOp` (#126517) * Use helpers for _Py_Specialize_ConstainsOp * Remove unnecessary variable --- Python/specialize.c | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/Python/specialize.c b/Python/specialize.c index 2673e16e596a1a..0699e7be5e6b9c 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -2760,8 +2760,8 @@ _Py_Specialize_ToBool(_PyStackRef value_o, _Py_CODEUNIT *instr) cache->counter = adaptive_counter_cooldown(); } -#ifdef Py_STATS -static int containsop_fail_kind(PyObject *value) { +static int +containsop_fail_kind(PyObject *value) { if (PyUnicode_CheckExact(value)) { return SPEC_FAIL_CONTAINS_OP_STR; } @@ -2776,7 +2776,6 @@ static int containsop_fail_kind(PyObject *value) { } return SPEC_FAIL_OTHER; } -#endif // Py_STATS void _Py_Specialize_ContainsOp(_PyStackRef value_st, _Py_CODEUNIT *instr) @@ -2785,26 +2784,17 @@ _Py_Specialize_ContainsOp(_PyStackRef value_st, _Py_CODEUNIT *instr) assert(ENABLE_SPECIALIZATION_FT); assert(_PyOpcode_Caches[CONTAINS_OP] == INLINE_CACHE_ENTRIES_COMPARE_OP); - uint8_t specialized_op; - _PyContainsOpCache *cache = (_PyContainsOpCache *)(instr + 1); if (PyDict_CheckExact(value)) { - specialized_op = CONTAINS_OP_DICT; - goto success; + specialize(instr, CONTAINS_OP_DICT); + return; } if (PySet_CheckExact(value) || PyFrozenSet_CheckExact(value)) { - specialized_op = CONTAINS_OP_SET; - goto success; + specialize(instr, CONTAINS_OP_SET); + return; } - SPECIALIZATION_FAIL(CONTAINS_OP, containsop_fail_kind(value)); - STAT_INC(CONTAINS_OP, failure); - SET_OPCODE_OR_RETURN(instr, CONTAINS_OP); - cache->counter = adaptive_counter_backoff(cache->counter); + unspecialize(instr, containsop_fail_kind(value)); return; -success: - STAT_INC(CONTAINS_OP, success); - SET_OPCODE_OR_RETURN(instr, specialized_op); - cache->counter = adaptive_counter_cooldown(); } /* Code init cleanup. From b9082958ef7dfb57d0fef745a5bf2521546a0dd6 Mon Sep 17 00:00:00 2001 From: Valerii <81074936+valerii-chirkov@users.noreply.github.com> Date: Thu, 7 Nov 2024 03:11:48 +0500 Subject: [PATCH 080/219] gh-126509: Update link to CPython's grammar docs in InternalDocs/parser.md (#126510) --- InternalDocs/parser.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InternalDocs/parser.md b/InternalDocs/parser.md index 6398ba6cd2838f..a0c70b46087d1a 100644 --- a/InternalDocs/parser.md +++ b/InternalDocs/parser.md @@ -17,7 +17,7 @@ Therefore, changes to the Python language are made by modifying the [grammar file](../Grammar/python.gram). Developers rarely need to modify the generator itself. -See the devguide's [Changing CPython's grammar](https://devguide.python.org/developer-workflow/grammar/#grammar) +See [Changing CPython's grammar](./changing_grammar.md) for a detailed description of the grammar and the process for changing it. How PEG parsers work From 5dc36dc5658f6ba9cfd9d7a2771baaf17d2ee23a Mon Sep 17 00:00:00 2001 From: Stephen Morton Date: Wed, 6 Nov 2024 14:12:45 -0800 Subject: [PATCH 081/219] gh-126451: Register contextvars.Context to collections.abc.Mapping (#126452) Co-authored-by: sobolevn Co-authored-by: Alex Waygood Co-authored-by: Peter Bierma --- Lib/contextvars.py | 4 ++++ Lib/test/test_context.py | 14 ++++++++++++++ .../2024-11-05-11-28-45.gh-issue-126451.XJMtqz.rst | 2 ++ 3 files changed, 20 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2024-11-05-11-28-45.gh-issue-126451.XJMtqz.rst diff --git a/Lib/contextvars.py b/Lib/contextvars.py index d78c80dfe6f99c..14514f185e069d 100644 --- a/Lib/contextvars.py +++ b/Lib/contextvars.py @@ -1,4 +1,8 @@ +import _collections_abc from _contextvars import Context, ContextVar, Token, copy_context __all__ = ('Context', 'ContextVar', 'Token', 'copy_context') + + +_collections_abc.Mapping.register(Context) diff --git a/Lib/test/test_context.py b/Lib/test/test_context.py index b06b9df9f5b0b8..82d1797ab3b79e 100644 --- a/Lib/test/test_context.py +++ b/Lib/test/test_context.py @@ -1,3 +1,4 @@ +import collections.abc import concurrent.futures import contextvars import functools @@ -350,6 +351,19 @@ def ctx2_fun(): ctx1.run(ctx1_fun) + def test_context_isinstance(self): + ctx = contextvars.Context() + self.assertIsInstance(ctx, collections.abc.Mapping) + self.assertTrue(issubclass(contextvars.Context, collections.abc.Mapping)) + + mapping_methods = ( + '__contains__', '__eq__', '__getitem__', '__iter__', '__len__', + '__ne__', 'get', 'items', 'keys', 'values', + ) + for name in mapping_methods: + with self.subTest(name=name): + self.assertTrue(callable(getattr(ctx, name))) + @isolated_context @threading_helper.requires_working_threading() def test_context_threads_1(self): diff --git a/Misc/NEWS.d/next/Library/2024-11-05-11-28-45.gh-issue-126451.XJMtqz.rst b/Misc/NEWS.d/next/Library/2024-11-05-11-28-45.gh-issue-126451.XJMtqz.rst new file mode 100644 index 00000000000000..563cb2515eca60 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-11-05-11-28-45.gh-issue-126451.XJMtqz.rst @@ -0,0 +1,2 @@ +Register the :class:`contextvars.Context` type to +:class:`collections.abc.Mapping`. From 2a6b6b33dfe0f3c435abf2829b62ef3f1ef12cd3 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Wed, 6 Nov 2024 14:33:46 -0800 Subject: [PATCH 082/219] GH-126458: disable SIMD for HACL under WASI (#126512) Requires an extra `-msimd128` flag and the `*mmintrin.h` header files are exclusive to x86-family CPUs. --- .../Build/2024-11-06-11-12-04.gh-issue-126458.7vzHtx.rst | 1 + configure | 6 ++++-- configure.ac | 6 ++++-- 3 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Build/2024-11-06-11-12-04.gh-issue-126458.7vzHtx.rst diff --git a/Misc/NEWS.d/next/Build/2024-11-06-11-12-04.gh-issue-126458.7vzHtx.rst b/Misc/NEWS.d/next/Build/2024-11-06-11-12-04.gh-issue-126458.7vzHtx.rst new file mode 100644 index 00000000000000..cc06dd8a30e30a --- /dev/null +++ b/Misc/NEWS.d/next/Build/2024-11-06-11-12-04.gh-issue-126458.7vzHtx.rst @@ -0,0 +1 @@ +Disable SIMD support for HACL under WASI. diff --git a/configure b/configure index e529527214da29..e0ab304570dfd4 100755 --- a/configure +++ b/configure @@ -30770,7 +30770,8 @@ esac # The SIMD files use aligned_alloc, which is not available on older versions of # Android. -if test "$ac_sys_system" != "Linux-android" || test "$ANDROID_API_LEVEL" -ge 28; then +# The *mmintrin.h headers are x86-family-specific, so can't be used on WASI. +if test "$ac_sys_system" != "Linux-android" -a "$ac_sys_system" != "WASI" || test "$ANDROID_API_LEVEL" -ge 28; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -msse -msse2 -msse3 -msse4.1 -msse4.2" >&5 printf %s "checking whether C compiler accepts -msse -msse2 -msse3 -msse4.1 -msse4.2... " >&6; } if test ${ax_cv_check_cflags__Werror__msse__msse2__msse3__msse4_1__msse4_2+y} @@ -30837,11 +30838,12 @@ fi # The SIMD files use aligned_alloc, which is not available on older versions of # Android. +# The *mmintrin.h headers are x86-family-specific, so can't be used on WASI. # # Although AVX support is not guaranteed on Android # (https://developer.android.com/ndk/guides/abis#86-64), this is safe because we do a # runtime CPUID check. -if test "$ac_sys_system" != "Linux-android" || test "$ANDROID_API_LEVEL" -ge 28; then +if test "$ac_sys_system" != "Linux-android" -a "$ac_sys_system" != "WASI" || test "$ANDROID_API_LEVEL" -ge 28; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -mavx2" >&5 printf %s "checking whether C compiler accepts -mavx2... " >&6; } if test ${ax_cv_check_cflags__Werror__mavx2+y} diff --git a/configure.ac b/configure.ac index bc67a0596ac2b4..da7d1ef68eefa8 100644 --- a/configure.ac +++ b/configure.ac @@ -7853,7 +7853,8 @@ AC_SUBST([LIBHACL_CFLAGS]) # The SIMD files use aligned_alloc, which is not available on older versions of # Android. -if test "$ac_sys_system" != "Linux-android" || test "$ANDROID_API_LEVEL" -ge 28; then +# The *mmintrin.h headers are x86-family-specific, so can't be used on WASI. +if test "$ac_sys_system" != "Linux-android" -a "$ac_sys_system" != "WASI" || test "$ANDROID_API_LEVEL" -ge 28; then dnl This can be extended here to detect e.g. Power8, which HACL* should also support. AX_CHECK_COMPILE_FLAG([-msse -msse2 -msse3 -msse4.1 -msse4.2],[ [LIBHACL_SIMD128_FLAGS="-msse -msse2 -msse3 -msse4.1 -msse4.2"] @@ -7879,11 +7880,12 @@ AC_SUBST([LIBHACL_SIMD128_OBJS]) # The SIMD files use aligned_alloc, which is not available on older versions of # Android. +# The *mmintrin.h headers are x86-family-specific, so can't be used on WASI. # # Although AVX support is not guaranteed on Android # (https://developer.android.com/ndk/guides/abis#86-64), this is safe because we do a # runtime CPUID check. -if test "$ac_sys_system" != "Linux-android" || test "$ANDROID_API_LEVEL" -ge 28; then +if test "$ac_sys_system" != "Linux-android" -a "$ac_sys_system" != "WASI" || test "$ANDROID_API_LEVEL" -ge 28; then AX_CHECK_COMPILE_FLAG([-mavx2],[ [LIBHACL_SIMD256_FLAGS="-mavx2"] AC_DEFINE([HACL_CAN_COMPILE_SIMD256], [1], [HACL* library can compile SIMD256 implementations]) From 223d3dc554dde45f185f7f465753824c6f698b9b Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 7 Nov 2024 08:53:02 +0200 Subject: [PATCH 083/219] gh-125631: Enable setting persistent_id and persistent_load of pickler and unpickler (GH-125752) pickle.Pickler.persistent_id and pickle.Unpickler.persistent_load can again be overridden as instance attributes. --- Lib/test/test_pickle.py | 82 ++++++++++++++++++- ...-10-19-11-06-06.gh-issue-125631.BlhVvR.rst | 4 + Modules/_pickle.c | 62 ++++++++++++++ 3 files changed, 146 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-10-19-11-06-06.gh-issue-125631.BlhVvR.rst diff --git a/Lib/test/test_pickle.py b/Lib/test/test_pickle.py index 9ec2eb97147fae..4ec966d8351490 100644 --- a/Lib/test/test_pickle.py +++ b/Lib/test/test_pickle.py @@ -250,6 +250,84 @@ def persistent_load(subself, pid): self.assertEqual(unpickler.load(), 'abc') self.assertEqual(called, ['abc']) + def test_pickler_instance_attribute(self): + def persistent_id(obj): + called.append(obj) + return obj + + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + f = io.BytesIO() + pickler = self.pickler(f, proto) + called = [] + old_persistent_id = pickler.persistent_id + pickler.persistent_id = persistent_id + self.assertEqual(pickler.persistent_id, persistent_id) + pickler.dump('abc') + self.assertEqual(called, ['abc']) + self.assertEqual(self.loads(f.getvalue()), 'abc') + del pickler.persistent_id + self.assertEqual(pickler.persistent_id, old_persistent_id) + + def test_unpickler_instance_attribute(self): + def persistent_load(pid): + called.append(pid) + return pid + + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + unpickler = self.unpickler(io.BytesIO(self.dumps('abc', proto))) + called = [] + old_persistent_load = unpickler.persistent_load + unpickler.persistent_load = persistent_load + self.assertEqual(unpickler.persistent_load, persistent_load) + self.assertEqual(unpickler.load(), 'abc') + self.assertEqual(called, ['abc']) + del unpickler.persistent_load + self.assertEqual(unpickler.persistent_load, old_persistent_load) + + def test_pickler_super_instance_attribute(self): + class PersPickler(self.pickler): + def persistent_id(subself, obj): + raise AssertionError('should never be called') + def _persistent_id(subself, obj): + called.append(obj) + self.assertIsNone(super().persistent_id(obj)) + return obj + + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + f = io.BytesIO() + pickler = PersPickler(f, proto) + called = [] + old_persistent_id = pickler.persistent_id + pickler.persistent_id = pickler._persistent_id + self.assertEqual(pickler.persistent_id, pickler._persistent_id) + pickler.dump('abc') + self.assertEqual(called, ['abc']) + self.assertEqual(self.loads(f.getvalue()), 'abc') + del pickler.persistent_id + self.assertEqual(pickler.persistent_id, old_persistent_id) + + def test_unpickler_super_instance_attribute(self): + class PersUnpickler(self.unpickler): + def persistent_load(subself, pid): + raise AssertionError('should never be called') + def _persistent_load(subself, pid): + called.append(pid) + with self.assertRaises(self.persistent_load_error): + super().persistent_load(pid) + return pid + + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + unpickler = PersUnpickler(io.BytesIO(self.dumps('abc', proto))) + called = [] + old_persistent_load = unpickler.persistent_load + unpickler.persistent_load = unpickler._persistent_load + self.assertEqual(unpickler.persistent_load, unpickler._persistent_load) + self.assertEqual(unpickler.load(), 'abc') + self.assertEqual(called, ['abc']) + del unpickler.persistent_load + self.assertEqual(unpickler.persistent_load, old_persistent_load) + + class PyPicklerUnpicklerObjectTests(AbstractPicklerUnpicklerObjectTests, unittest.TestCase): pickler_class = pickle._Pickler @@ -373,7 +451,7 @@ class SizeofTests(unittest.TestCase): check_sizeof = support.check_sizeof def test_pickler(self): - basesize = support.calcobjsize('6P2n3i2n3i2P') + basesize = support.calcobjsize('7P2n3i2n3i2P') p = _pickle.Pickler(io.BytesIO()) self.assertEqual(object.__sizeof__(p), basesize) MT_size = struct.calcsize('3nP0n') @@ -390,7 +468,7 @@ def test_pickler(self): 0) # Write buffer is cleared after every dump(). def test_unpickler(self): - basesize = support.calcobjsize('2P2nP 2P2n2i5P 2P3n8P2n2i') + basesize = support.calcobjsize('2P2n2P 2P2n2i5P 2P3n8P2n2i') unpickler = _pickle.Unpickler P = struct.calcsize('P') # Size of memo table entry. n = struct.calcsize('n') # Size of mark table entry. diff --git a/Misc/NEWS.d/next/Library/2024-10-19-11-06-06.gh-issue-125631.BlhVvR.rst b/Misc/NEWS.d/next/Library/2024-10-19-11-06-06.gh-issue-125631.BlhVvR.rst new file mode 100644 index 00000000000000..e870abbf87803a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-10-19-11-06-06.gh-issue-125631.BlhVvR.rst @@ -0,0 +1,4 @@ +Restore ability to set :attr:`~pickle.Pickler.persistent_id` and +:attr:`~pickle.Unpickler.persistent_load` attributes of instances of the +:class:`!Pickler` and :class:`!Unpickler` classes in the :mod:`pickle` +module. diff --git a/Modules/_pickle.c b/Modules/_pickle.c index 863da6878409f3..5837cd41a40cd4 100644 --- a/Modules/_pickle.c +++ b/Modules/_pickle.c @@ -613,6 +613,7 @@ typedef struct PicklerObject { objects to support self-referential objects pickling. */ PyObject *persistent_id; /* persistent_id() method, can be NULL */ + PyObject *persistent_id_attr; /* instance attribute, can be NULL */ PyObject *dispatch_table; /* private dispatch_table, can be NULL */ PyObject *reducer_override; /* hook for invoking user-defined callbacks instead of save_global when pickling @@ -655,6 +656,7 @@ typedef struct UnpicklerObject { size_t memo_len; /* Number of objects in the memo */ PyObject *persistent_load; /* persistent_load() method, can be NULL. */ + PyObject *persistent_load_attr; /* instance attribute, can be NULL. */ Py_buffer buffer; char *input_buffer; @@ -1108,6 +1110,7 @@ _Pickler_New(PickleState *st) self->memo = memo; self->persistent_id = NULL; + self->persistent_id_attr = NULL; self->dispatch_table = NULL; self->reducer_override = NULL; self->write = NULL; @@ -1606,6 +1609,7 @@ _Unpickler_New(PyObject *module) self->memo_size = MEMO_SIZE; self->memo_len = 0; self->persistent_load = NULL; + self->persistent_load_attr = NULL; memset(&self->buffer, 0, sizeof(Py_buffer)); self->input_buffer = NULL; self->input_line = NULL; @@ -5092,6 +5096,33 @@ Pickler_set_memo(PicklerObject *self, PyObject *obj, void *Py_UNUSED(ignored)) return -1; } +static PyObject * +Pickler_getattr(PyObject *self, PyObject *name) +{ + if (PyUnicode_Check(name) + && PyUnicode_EqualToUTF8(name, "persistent_id") + && ((PicklerObject *)self)->persistent_id_attr) + { + return Py_NewRef(((PicklerObject *)self)->persistent_id_attr); + } + + return PyObject_GenericGetAttr(self, name); +} + +static int +Pickler_setattr(PyObject *self, PyObject *name, PyObject *value) +{ + if (PyUnicode_Check(name) + && PyUnicode_EqualToUTF8(name, "persistent_id")) + { + Py_XINCREF(value); + Py_XSETREF(((PicklerObject *)self)->persistent_id_attr, value); + return 0; + } + + return PyObject_GenericSetAttr(self, name, value); +} + static PyMemberDef Pickler_members[] = { {"bin", Py_T_INT, offsetof(PicklerObject, bin)}, {"fast", Py_T_INT, offsetof(PicklerObject, fast)}, @@ -5107,6 +5138,8 @@ static PyGetSetDef Pickler_getsets[] = { static PyType_Slot pickler_type_slots[] = { {Py_tp_dealloc, Pickler_dealloc}, + {Py_tp_getattro, Pickler_getattr}, + {Py_tp_setattro, Pickler_setattr}, {Py_tp_methods, Pickler_methods}, {Py_tp_members, Pickler_members}, {Py_tp_getset, Pickler_getsets}, @@ -7566,6 +7599,33 @@ Unpickler_set_memo(UnpicklerObject *self, PyObject *obj, void *Py_UNUSED(ignored return -1; } +static PyObject * +Unpickler_getattr(PyObject *self, PyObject *name) +{ + if (PyUnicode_Check(name) + && PyUnicode_EqualToUTF8(name, "persistent_load") + && ((UnpicklerObject *)self)->persistent_load_attr) + { + return Py_NewRef(((UnpicklerObject *)self)->persistent_load_attr); + } + + return PyObject_GenericGetAttr(self, name); +} + +static int +Unpickler_setattr(PyObject *self, PyObject *name, PyObject *value) +{ + if (PyUnicode_Check(name) + && PyUnicode_EqualToUTF8(name, "persistent_load")) + { + Py_XINCREF(value); + Py_XSETREF(((UnpicklerObject *)self)->persistent_load_attr, value); + return 0; + } + + return PyObject_GenericSetAttr(self, name, value); +} + static PyGetSetDef Unpickler_getsets[] = { {"memo", (getter)Unpickler_get_memo, (setter)Unpickler_set_memo}, {NULL} @@ -7574,6 +7634,8 @@ static PyGetSetDef Unpickler_getsets[] = { static PyType_Slot unpickler_type_slots[] = { {Py_tp_dealloc, Unpickler_dealloc}, {Py_tp_doc, (char *)_pickle_Unpickler___init____doc__}, + {Py_tp_getattro, Unpickler_getattr}, + {Py_tp_setattro, Unpickler_setattr}, {Py_tp_traverse, Unpickler_traverse}, {Py_tp_clear, Unpickler_clear}, {Py_tp_methods, Unpickler_methods}, From dbb6e22cb1f533bba00a61a5b63ec68af9d48836 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 7 Nov 2024 09:09:59 +0200 Subject: [PATCH 084/219] gh-125926: Fix urllib.parse.urljoin() for base URI with undefined authority (GH-125989) Although this goes beyond the application of RFC 3986, urljoin() should support relative base URIs for backward compatibility. --- Lib/test/test_urlparse.py | 72 +++++++++++++++++++ Lib/urllib/parse.py | 4 +- ...-10-25-20-52-15.gh-issue-125926.pp8rtZ.rst | 4 ++ 3 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-10-25-20-52-15.gh-issue-125926.pp8rtZ.rst diff --git a/Lib/test/test_urlparse.py b/Lib/test/test_urlparse.py index d49e4388696ab4..297fb4831c16bf 100644 --- a/Lib/test/test_urlparse.py +++ b/Lib/test/test_urlparse.py @@ -623,6 +623,78 @@ def test_urljoins(self): self.checkJoin(RFC1808_BASE, 'https:;', 'https:;') self.checkJoin(RFC1808_BASE, 'https:;x', 'https:;x') + def test_urljoins_relative_base(self): + # According to RFC 3986, Section 5.1, a base URI must conform to + # the absolute-URI syntax rule (Section 4.3). But urljoin() lacks + # a context to establish missed components of the relative base URI. + # It still has to return a sensible result for backwards compatibility. + # The following tests are figments of the imagination and artifacts + # of the current implementation that are not based on any standard. + self.checkJoin('', '', '') + self.checkJoin('', '//', '//', relroundtrip=False) + self.checkJoin('', '//v', '//v') + self.checkJoin('', '//v/w', '//v/w') + self.checkJoin('', '/w', '/w') + self.checkJoin('', '///w', '///w', relroundtrip=False) + self.checkJoin('', 'w', 'w') + + self.checkJoin('//', '', '//') + self.checkJoin('//', '//', '//') + self.checkJoin('//', '//v', '//v') + self.checkJoin('//', '//v/w', '//v/w') + self.checkJoin('//', '/w', '///w') + self.checkJoin('//', '///w', '///w') + self.checkJoin('//', 'w', '///w') + + self.checkJoin('//a', '', '//a') + self.checkJoin('//a', '//', '//a') + self.checkJoin('//a', '//v', '//v') + self.checkJoin('//a', '//v/w', '//v/w') + self.checkJoin('//a', '/w', '//a/w') + self.checkJoin('//a', '///w', '//a/w') + self.checkJoin('//a', 'w', '//a/w') + + for scheme in '', 'http:': + self.checkJoin('http:', scheme + '', 'http:') + self.checkJoin('http:', scheme + '//', 'http:') + self.checkJoin('http:', scheme + '//v', 'http://v') + self.checkJoin('http:', scheme + '//v/w', 'http://v/w') + self.checkJoin('http:', scheme + '/w', 'http:/w') + self.checkJoin('http:', scheme + '///w', 'http:/w') + self.checkJoin('http:', scheme + 'w', 'http:/w') + + self.checkJoin('http://', scheme + '', 'http://') + self.checkJoin('http://', scheme + '//', 'http://') + self.checkJoin('http://', scheme + '//v', 'http://v') + self.checkJoin('http://', scheme + '//v/w', 'http://v/w') + self.checkJoin('http://', scheme + '/w', 'http:///w') + self.checkJoin('http://', scheme + '///w', 'http:///w') + self.checkJoin('http://', scheme + 'w', 'http:///w') + + self.checkJoin('http://a', scheme + '', 'http://a') + self.checkJoin('http://a', scheme + '//', 'http://a') + self.checkJoin('http://a', scheme + '//v', 'http://v') + self.checkJoin('http://a', scheme + '//v/w', 'http://v/w') + self.checkJoin('http://a', scheme + '/w', 'http://a/w') + self.checkJoin('http://a', scheme + '///w', 'http://a/w') + self.checkJoin('http://a', scheme + 'w', 'http://a/w') + + self.checkJoin('/b/c', '', '/b/c') + self.checkJoin('/b/c', '//', '/b/c') + self.checkJoin('/b/c', '//v', '//v') + self.checkJoin('/b/c', '//v/w', '//v/w') + self.checkJoin('/b/c', '/w', '/w') + self.checkJoin('/b/c', '///w', '/w') + self.checkJoin('/b/c', 'w', '/b/w') + + self.checkJoin('///b/c', '', '///b/c') + self.checkJoin('///b/c', '//', '///b/c') + self.checkJoin('///b/c', '//v', '//v') + self.checkJoin('///b/c', '//v/w', '//v/w') + self.checkJoin('///b/c', '/w', '///w') + self.checkJoin('///b/c', '///w', '///w') + self.checkJoin('///b/c', 'w', '///b/w') + def test_RFC2732(self): str_cases = [ ('http://Test.python.org:5432/foo/', 'test.python.org', 5432), diff --git a/Lib/urllib/parse.py b/Lib/urllib/parse.py index 5b00ab25c6b4ca..a721d777c82f82 100644 --- a/Lib/urllib/parse.py +++ b/Lib/urllib/parse.py @@ -577,9 +577,9 @@ def urljoin(base, url, allow_fragments=True): if scheme is None: scheme = bscheme - if scheme != bscheme or scheme not in uses_relative: + if scheme != bscheme or (scheme and scheme not in uses_relative): return _coerce_result(url) - if scheme in uses_netloc: + if not scheme or scheme in uses_netloc: if netloc: return _coerce_result(_urlunsplit(scheme, netloc, path, query, fragment)) diff --git a/Misc/NEWS.d/next/Library/2024-10-25-20-52-15.gh-issue-125926.pp8rtZ.rst b/Misc/NEWS.d/next/Library/2024-10-25-20-52-15.gh-issue-125926.pp8rtZ.rst new file mode 100644 index 00000000000000..7f98bcdc38e566 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-10-25-20-52-15.gh-issue-125926.pp8rtZ.rst @@ -0,0 +1,4 @@ +Fix :func:`urllib.parse.urljoin` for base URI with undefined authority. +Although :rfc:`3986` only specify reference resolution for absolute base +URI, :func:`!urljoin` should continue to return sensible result for relative +base URI. From d46d3f2ec783004f0927c9f5e6211a570360cf3b Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Thu, 7 Nov 2024 00:06:14 -0800 Subject: [PATCH 085/219] Cleanup multiprocessing comment and unusual import error message (#126532) Define constants as constants rather than calling `list(range(2))`. Explain which values must remain in sync via comments. --- Lib/multiprocessing/synchronize.py | 15 +++++++-------- Modules/_multiprocessing/semaphore.c | 1 + 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Lib/multiprocessing/synchronize.py b/Lib/multiprocessing/synchronize.py index 3ccbfe311c71f3..1917a8bd51dcab 100644 --- a/Lib/multiprocessing/synchronize.py +++ b/Lib/multiprocessing/synchronize.py @@ -21,22 +21,21 @@ from . import process from . import util -# Try to import the mp.synchronize module cleanly, if it fails -# raise ImportError for platforms lacking a working sem_open implementation. -# See issue 3770 +# TODO: Do any platforms still lack a functioning sem_open? try: from _multiprocessing import SemLock, sem_unlink -except (ImportError): +except ImportError: raise ImportError("This platform lacks a functioning sem_open" + - " implementation, therefore, the required" + - " synchronization primitives needed will not" + - " function, see issue 3770.") + " implementation. https://github.com/python/cpython/issues/48020.") # # Constants # -RECURSIVE_MUTEX, SEMAPHORE = list(range(2)) +# These match the enum in Modules/_multiprocessing/semaphore.c +RECURSIVE_MUTEX = 0 +SEMAPHORE = 1 + SEM_VALUE_MAX = _multiprocessing.SemLock.SEM_VALUE_MAX # diff --git a/Modules/_multiprocessing/semaphore.c b/Modules/_multiprocessing/semaphore.c index 4de4ee6c78fbd1..9eef7c25636899 100644 --- a/Modules/_multiprocessing/semaphore.c +++ b/Modules/_multiprocessing/semaphore.c @@ -15,6 +15,7 @@ #ifdef HAVE_MP_SEMAPHORE +// These match the values in Lib/multiprocessing/synchronize.py enum { RECURSIVE_MUTEX, SEMAPHORE }; typedef struct { From 75f7cf91ec5afc6091a0fd442a1f0435c19300b2 Mon Sep 17 00:00:00 2001 From: Duprat Date: Thu, 7 Nov 2024 09:10:57 +0100 Subject: [PATCH 086/219] gh-125679: multiprocessing Lock and RLock - fix invalid representation string on MacOSX. (#125680) --- Lib/multiprocessing/synchronize.py | 4 +- Lib/test/_test_multiprocessing.py | 122 ++++++++++++++++++ ...-11-06-23-40-28.gh-issue-125679.Qq9xF5.rst | 2 + 3 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-11-06-23-40-28.gh-issue-125679.Qq9xF5.rst diff --git a/Lib/multiprocessing/synchronize.py b/Lib/multiprocessing/synchronize.py index 1917a8bd51dcab..4f72373c951abc 100644 --- a/Lib/multiprocessing/synchronize.py +++ b/Lib/multiprocessing/synchronize.py @@ -173,7 +173,7 @@ def __repr__(self): name = process.current_process().name if threading.current_thread().name != 'MainThread': name += '|' + threading.current_thread().name - elif self._semlock._get_value() == 1: + elif not self._semlock._is_zero(): name = 'None' elif self._semlock._count() > 0: name = 'SomeOtherThread' @@ -199,7 +199,7 @@ def __repr__(self): if threading.current_thread().name != 'MainThread': name += '|' + threading.current_thread().name count = self._semlock._count() - elif self._semlock._get_value() == 1: + elif not self._semlock._is_zero(): name, count = 'None', 0 elif self._semlock._count() > 0: name, count = 'SomeOtherThread', 'nonzero' diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index 77b618c684475a..38ddb62c693fc0 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -1363,6 +1363,66 @@ def test_closed_queue_put_get_exceptions(self): class _TestLock(BaseTestCase): + @staticmethod + def _acquire(lock, l=None): + lock.acquire() + if l is not None: + l.append(repr(lock)) + + @staticmethod + def _acquire_event(lock, event): + lock.acquire() + event.set() + time.sleep(1.0) + + def test_repr_lock(self): + if self.TYPE != 'processes': + self.skipTest('test not appropriate for {}'.format(self.TYPE)) + + lock = self.Lock() + self.assertEqual(f'', repr(lock)) + + lock.acquire() + self.assertEqual(f'', repr(lock)) + lock.release() + + tname = 'T1' + l = [] + t = threading.Thread(target=self._acquire, + args=(lock, l), + name=tname) + t.start() + time.sleep(0.1) + self.assertEqual(f'', l[0]) + lock.release() + + t = threading.Thread(target=self._acquire, + args=(lock,), + name=tname) + t.start() + time.sleep(0.1) + self.assertEqual('', repr(lock)) + lock.release() + + pname = 'P1' + l = multiprocessing.Manager().list() + p = self.Process(target=self._acquire, + args=(lock, l), + name=pname) + p.start() + p.join() + self.assertEqual(f'', l[0]) + + lock = self.Lock() + event = self.Event() + p = self.Process(target=self._acquire_event, + args=(lock, event), + name='P2') + p.start() + event.wait() + self.assertEqual(f'', repr(lock)) + p.terminate() + def test_lock(self): lock = self.Lock() self.assertEqual(lock.acquire(), True) @@ -1370,6 +1430,68 @@ def test_lock(self): self.assertEqual(lock.release(), None) self.assertRaises((ValueError, threading.ThreadError), lock.release) + @staticmethod + def _acquire_release(lock, timeout, l=None, n=1): + for _ in range(n): + lock.acquire() + if l is not None: + l.append(repr(lock)) + time.sleep(timeout) + for _ in range(n): + lock.release() + + def test_repr_rlock(self): + if self.TYPE != 'processes': + self.skipTest('test not appropriate for {}'.format(self.TYPE)) + + lock = self.RLock() + self.assertEqual('', repr(lock)) + + n = 3 + for _ in range(n): + lock.acquire() + self.assertEqual(f'', repr(lock)) + for _ in range(n): + lock.release() + + t, l = [], [] + for i in range(n): + t.append(threading.Thread(target=self._acquire_release, + args=(lock, 0.1, l, i+1), + name=f'T{i+1}')) + t[-1].start() + for t_ in t: + t_.join() + for i in range(n): + self.assertIn(f'', l) + + + t = threading.Thread(target=self._acquire_release, + args=(lock, 0.2), + name=f'T1') + t.start() + time.sleep(0.1) + self.assertEqual('', repr(lock)) + time.sleep(0.2) + + pname = 'P1' + l = multiprocessing.Manager().list() + p = self.Process(target=self._acquire_release, + args=(lock, 0.1, l), + name=pname) + p.start() + p.join() + self.assertEqual(f'', l[0]) + + event = self.Event() + lock = self.RLock() + p = self.Process(target=self._acquire_event, + args=(lock, event)) + p.start() + event.wait() + self.assertEqual('', repr(lock)) + p.join() + def test_rlock(self): lock = self.RLock() self.assertEqual(lock.acquire(), True) diff --git a/Misc/NEWS.d/next/Library/2024-11-06-23-40-28.gh-issue-125679.Qq9xF5.rst b/Misc/NEWS.d/next/Library/2024-11-06-23-40-28.gh-issue-125679.Qq9xF5.rst new file mode 100644 index 00000000000000..ac6851e2689692 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-11-06-23-40-28.gh-issue-125679.Qq9xF5.rst @@ -0,0 +1,2 @@ +The :class:`multiprocessing.Lock` and :class:`multiprocessing.RLock` +``repr`` values no longer say "unknown" on macOS. From a5b94d066016be63d632cccee0ec2a2eb24536dc Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Thu, 7 Nov 2024 10:49:58 +0100 Subject: [PATCH 087/219] gh-96398: Improve accuracy of compiler checks in configure.ac (#117815) The following variables are now used in compiler checks: - $ac_cv_gcc_compat is set to 'yes' for GCC compatible compilers (the C preprocessor defines the __GNUC__ macro) - for compiler basename checks, use $CC_BASENAME (may contain platform triplets) - for the rest, use $ac_cv_cc_name (does not contain platform triplets) --- configure | 167 +++++++++++++++++++++------------------------------ configure.ac | 163 ++++++++++++++++++------------------------------- 2 files changed, 127 insertions(+), 203 deletions(-) diff --git a/configure b/configure index e0ab304570dfd4..1d5c0941247c30 100755 --- a/configure +++ b/configure @@ -6193,6 +6193,8 @@ printf "%s\n" "$ac_cv_path_EGREP" >&6; } +CC_BASENAME=$(expr "//$CC" : '.*/\(.*\)') + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for CC compiler name" >&5 printf %s "checking for CC compiler name... " >&6; } if test ${ac_cv_cc_name+y} @@ -6220,9 +6222,10 @@ EOF if $CPP $CPPFLAGS conftest.c >conftest.out 2>/dev/null; then ac_cv_cc_name=`grep -v '^#' conftest.out | grep -v '^ *$' | tr -d ' '` - if test $(expr "//$CC" : '.*/\(.*\)') = "mpicc"; then - ac_cv_cc_name="mpicc" - fi + if test "x$CC_BASENAME" = xmpicc +then : + ac_cv_cc_name=mpicc +fi else ac_cv_cc_name="unknown" fi @@ -6440,7 +6443,7 @@ printf "%s\n" "$ac_cv_gcc_compat" >&6; } preset_cxx="$CXX" if test -z "$CXX" then - case "$CC" in + case "$ac_cv_cc_name" in gcc) if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}g++", so it can be a program name with args. set dummy ${ac_tool_prefix}g++; ac_word=$2 @@ -6657,7 +6660,7 @@ else CXX="$ac_cv_path_CXX" fi ;; - clang|*/clang) if test -n "$ac_tool_prefix"; then + clang) if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}clang++", so it can be a program name with args. set dummy ${ac_tool_prefix}clang++; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 @@ -6765,7 +6768,7 @@ else CXX="$ac_cv_path_CXX" fi ;; - icc|*/icc) if test -n "$ac_tool_prefix"; then + icc) if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}icpc", so it can be a program name with args. set dummy ${ac_tool_prefix}icpc; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 @@ -7374,7 +7377,7 @@ rmdir CaseSensitiveTestDir case $ac_sys_system in hp*|HP*) - case $CC in + case $ac_cv_cc_name in cc|*/cc) CC="$CC -Ae";; esac;; esac @@ -7467,7 +7470,7 @@ printf "%s\n" "$EXPORTSYMS" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 printf %s "checking for GNU ld... " >&6; } ac_prog=ld -if test "$GCC" = yes; then +if test "$ac_cv_cc_name" = "gcc"; then ac_prog=`$CC -print-prog-name=ld` fi case `"$ac_prog" -V 2>&1 < /dev/null` in @@ -8338,8 +8341,9 @@ if test "$Py_OPT" = 'true' ; then DEF_MAKE_ALL_RULE="profile-opt" REQUIRE_PGO="yes" DEF_MAKE_RULE="build_all" - case $CC in - *gcc*) + if test "x$ac_cv_gcc_compat" = xyes +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -fno-semantic-interposition" >&5 printf %s "checking whether C compiler accepts -fno-semantic-interposition... " >&6; } if test ${ax_cv_check_cflags__Werror__fno_semantic_interposition+y} @@ -8381,8 +8385,8 @@ else $as_nop : fi - ;; - esac + +fi elif test "$ac_sys_system" = "Emscripten" -o "$ac_sys_system" = "WASI"; then DEF_MAKE_ALL_RULE="build_wasm" REQUIRE_PGO="no" @@ -8409,7 +8413,7 @@ printf "%s\n" "$PROFILE_TASK" >&6; } llvm_bin_dir='' llvm_path="${PATH}" -if test "${CC}" = "clang" +if test "${ac_cv_cc_name}" = "clang" then clang_bin=`which clang` # Some systems install clang elsewhere as a symlink to the real path @@ -8467,8 +8471,8 @@ printf "%s\n" "no" >&6; } fi if test "$Py_LTO" = 'true' ; then - case $CC in - *clang*) + case $ac_cv_cc_name in + clang) LDFLAGS_NOLTO="-fno-lto" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -flto=thin" >&5 printf %s "checking whether C compiler accepts -flto=thin... " >&6; } @@ -8748,14 +8752,14 @@ fi ;; esac ;; - *emcc*) + emcc) if test "$Py_LTO_POLICY" != "default"; then as_fn_error $? "emcc supports only default lto." "$LINENO" 5 fi LTOFLAGS="-flto" LTOCFLAGS="-flto" ;; - *gcc*) + gcc) if test $Py_LTO_POLICY = thin then as_fn_error $? "thin lto is not supported under gcc compiler." "$LINENO" 5 @@ -8921,10 +8925,8 @@ printf "%s\n" "$as_me: llvm-profdata found via xcrun: ${LLVM_PROFDATA}" >&6;} fi LLVM_PROF_ERR=no -# GNU Autoconf recommends the use of expr instead of basename. -CC_BASENAME=$(expr "//$CC" : '.*/\(.*\)') -case "$CC_BASENAME" in - *clang*) +case "$ac_cv_cc_name" 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=\"\$(shell pwd)/code.profclangd\"" @@ -8939,31 +8941,13 @@ case "$CC_BASENAME" in fi fi ;; - *gcc*) - case $ac_sys_system in - Darwin*) - PGO_PROF_GEN_FLAG="-fprofile-instr-generate" - PGO_PROF_USE_FLAG="-fprofile-instr-use=\"\$(shell pwd)/code.profclangd\"" - LLVM_PROF_MERGER=" ${LLVM_PROFDATA} merge -output=\"\$(shell pwd)/code.profclangd\" \"\$(shell pwd)\"/*.profclangr " - LLVM_PROF_FILE="LLVM_PROFILE_FILE=\"\$(shell pwd)/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 + gcc) + PGO_PROF_GEN_FLAG="-fprofile-generate" + PGO_PROF_USE_FLAG="-fprofile-use -fprofile-correction" + LLVM_PROF_MERGER="true" + LLVM_PROF_FILE="" ;; - *icc*) + icc) PGO_PROF_GEN_FLAG="-prof-gen" PGO_PROF_USE_FLAG="-prof-use" LLVM_PROF_MERGER="true" @@ -9329,19 +9313,6 @@ printf "%s\n" "$BOLT_APPLY_FLAGS" >&6; } # compiler and platform. BASECFLAGS tweaks need to be made even if the # user set OPT. -case $CC in - *clang*) - cc_is_clang=1 - ;; - *) - if $CC --version 2>&1 | grep -q clang - then - cc_is_clang=1 - else - cc_is_clang= - fi -esac - save_CFLAGS=$CFLAGS CFLAGS="-fstrict-overflow -fno-strict-overflow" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $CC supports -fstrict-overflow and -fno-strict-overflow" >&5 @@ -9465,7 +9436,7 @@ if test "${OPT-unset}" = "unset" then case $GCC in yes) - if test -n "${cc_is_clang}" + if test "${ac_cv_cc_name}" != "clang" then # bpo-30104: disable strict aliasing to compile correctly dtoa.c, # see Makefile.pre.in for more information @@ -9964,8 +9935,9 @@ fi fi -case $GCC in -yes) +if test "x$ac_cv_gcc_compat" = xyes +then : + CFLAGS_NODIST="$CFLAGS_NODIST -std=c11" @@ -10083,8 +10055,8 @@ fi # ICC doesn't recognize the option, but only emits a warning ## XXX does it emit an unused result warning and can it be disabled? - case "$CC_BASENAME" in #( - *icc*) : + case "$ac_cv_cc_name" in #( + icc) : ac_cv_disable_unused_result_warning=no @@ -10489,22 +10461,19 @@ fi Darwin*) # -Wno-long-double, -no-cpp-precomp, and -mno-fused-madd # used to be here, but non-Apple gcc doesn't accept them. - if test "${CC}" = gcc - then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking which compiler should be used" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking which compiler should be used" >&5 printf %s "checking which compiler should be used... " >&6; } - case "${UNIVERSALSDK}" in - */MacOSX10.4u.sdk) - # Build using 10.4 SDK, force usage of gcc when the - # compiler is gcc, otherwise the user will get very - # confusing error messages when building on OSX 10.6 - CC=gcc-4.0 - CPP=cpp-4.0 - ;; - esac - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 + case "${UNIVERSALSDK}" in + */MacOSX10.4u.sdk) + # Build using 10.4 SDK, force usage of gcc when the + # compiler is gcc, otherwise the user will get very + # confusing error messages when building on OSX 10.6 + CC=gcc-4.0 + CPP=cpp-4.0 + ;; + esac + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } - fi LIPO_INTEL64_FLAGS="" if test "${enable_universalsdk}" @@ -10650,9 +10619,9 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \ # end of Darwin* tests ;; esac - ;; -*) +else $as_nop + case $ac_sys_system in OpenUNIX*|UnixWare*) BASECFLAGS="$BASECFLAGS -K pentium,host,inline,loop_unroll,alloca " @@ -10661,18 +10630,18 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \ BASECFLAGS="$BASECFLAGS -belf -Ki486 -DSCO5" ;; esac - ;; -esac -case "$CC_BASENAME" in -*mpicc*) +fi + +case "$ac_cv_cc_name" in +mpicc) CFLAGS_NODIST="$CFLAGS_NODIST" ;; -*icc*) +icc) # ICC needs -fp-model strict or floats behave badly CFLAGS_NODIST="$CFLAGS_NODIST -fp-model strict" ;; -*xlc*) +xlc) CFLAGS_NODIST="$CFLAGS_NODIST -qalias=noansi -qmaxmem=-1" ;; esac @@ -13195,7 +13164,7 @@ then LDSHARED="\$(LIBPL)/ld_so_aix \$(CC) -bI:\$(LIBPL)/python.exp" ;; SunOS/5*) - if test "$GCC" = "yes" ; then + if test "$ac_cv_gcc_compat" = "yes" ; then LDSHARED='$(CC) -shared' LDCXXSHARED='$(CXX) -shared' else @@ -13203,7 +13172,7 @@ then LDCXXSHARED='$(CXX) -G' fi ;; hp*|HP*) - if test "$GCC" = "yes" ; then + if test "$ac_cv_gcc_compat" = "yes" ; then LDSHARED='$(CC) -shared' LDCXXSHARED='$(CXX) -shared' else @@ -13296,7 +13265,7 @@ then LDSHARED='$(CC) -shared' LDCXXSHARED='$(CXX) -shared';; OpenUNIX*|UnixWare*) - if test "$GCC" = "yes" ; then + if test "$ac_cv_gcc_compat" = "yes" ; then LDSHARED='$(CC) -shared' LDCXXSHARED='$(CXX) -shared' else @@ -13340,13 +13309,13 @@ printf %s "checking CCSHARED... " >&6; } if test -z "$CCSHARED" then case $ac_sys_system/$ac_sys_release in - SunOS*) if test "$GCC" = yes; + SunOS*) if test "$ac_cv_gcc_compat" = "yes"; then CCSHARED="-fPIC"; elif test `uname -p` = sparc; then CCSHARED="-xcode=pic32"; else CCSHARED="-Kpic"; fi;; - hp*|HP*) if test "$GCC" = yes; + hp*|HP*) if test "$ac_cv_gcc_compat" = "yes"; then CCSHARED="-fPIC"; else CCSHARED="+z"; fi;; @@ -13361,12 +13330,12 @@ fi;; FreeBSD*|NetBSD*|OpenBSD*|DragonFly*) CCSHARED="-fPIC";; Haiku*) CCSHARED="-fPIC";; OpenUNIX*|UnixWare*) - if test "$GCC" = "yes" + if test "$ac_cv_gcc_compat" = "yes" then CCSHARED="-fPIC" else CCSHARED="-KPIC" fi;; SCO_SV*) - if test "$GCC" = "yes" + if test "$ac_cv_gcc_compat" = "yes" then CCSHARED="-fPIC" else CCSHARED="-Kpic -belf" fi;; @@ -13426,13 +13395,13 @@ printf "%s\n" "#define THREAD_STACK_SIZE 0x$stack_size" >>confdefs.h then LINKFORSHARED="-Wl,--export-dynamic" fi;; - SunOS/5*) case $CC in - *gcc*) + SunOS/5*) if test "$ac_cv_gcc_compat" = "yes"; then if $CC -Xlinker --help 2>&1 | grep export-dynamic >/dev/null then LINKFORSHARED="-Xlinker --export-dynamic" - fi;; - esac;; + fi + fi + ;; CYGWIN*) if test $enable_shared = "no" then @@ -15323,7 +15292,7 @@ esac fi elif test $ac_cv_sizeof_size_t -eq 4; then if test "$ac_cv_gcc_asm_for_x87" = yes -a "$libmpdec_system" != sunos; then - case $CC in #( + case $ac_cv_cc_name in #( *gcc*) : libmpdec_machine=ppro ;; #( *clang*) : @@ -28206,8 +28175,8 @@ if test "$ac_cv_gcc_asm_for_x87" = yes; then # Some versions of gcc miscompile inline asm: # http://gcc.gnu.org/bugzilla/show_bug.cgi?id=46491 # http://gcc.gnu.org/ml/gcc/2010-11/msg00366.html - case $CC in - *gcc*) + case $ac_cv_cc_name in + gcc) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for gcc ipa-pure-const bug" >&5 printf %s "checking for gcc ipa-pure-const bug... " >&6; } saved_cflags="$CFLAGS" diff --git a/configure.ac b/configure.ac index da7d1ef68eefa8..ce5a5eb9c2891f 100644 --- a/configure.ac +++ b/configure.ac @@ -1048,6 +1048,9 @@ AC_PROG_GREP AC_PROG_SED AC_PROG_EGREP +dnl GNU Autoconf recommends the use of expr instead of basename. +AS_VAR_SET([CC_BASENAME], [$(expr "//$CC" : '.*/\(.*\)')]) + dnl detect compiler name dnl check for xlc before clang, newer xlc's can use clang as frontend. dnl check for GCC last, other compilers set __GNUC__, too. @@ -1073,9 +1076,7 @@ EOF if $CPP $CPPFLAGS conftest.c >conftest.out 2>/dev/null; then ac_cv_cc_name=`grep -v '^#' conftest.out | grep -v '^ *$' | tr -d ' '` - if test $(expr "//$CC" : '.*/\(.*\)') = "mpicc"; then - ac_cv_cc_name="mpicc" - fi + AS_VAR_IF([CC_BASENAME], [mpicc], [ac_cv_cc_name=mpicc]) else ac_cv_cc_name="unknown" fi @@ -1104,11 +1105,11 @@ AC_SUBST([CXX]) preset_cxx="$CXX" if test -z "$CXX" then - case "$CC" in + case "$ac_cv_cc_name" in gcc) AC_PATH_TOOL([CXX], [g++], [g++], [notfound]) ;; cc) AC_PATH_TOOL([CXX], [c++], [c++], [notfound]) ;; - clang|*/clang) AC_PATH_TOOL([CXX], [clang++], [clang++], [notfound]) ;; - icc|*/icc) AC_PATH_TOOL([CXX], [icpc], [icpc], [notfound]) ;; + clang) AC_PATH_TOOL([CXX], [clang++], [clang++], [notfound]) ;; + icc) AC_PATH_TOOL([CXX], [icpc], [icpc], [notfound]) ;; esac if test "$CXX" = "notfound" then @@ -1381,7 +1382,7 @@ rmdir CaseSensitiveTestDir case $ac_sys_system in hp*|HP*) - case $CC in + case $ac_cv_cc_name in cc|*/cc) CC="$CC -Ae";; esac;; esac @@ -1467,7 +1468,7 @@ AC_MSG_RESULT([$EXPORTSYMS]) AC_SUBST([GNULD]) AC_MSG_CHECKING([for GNU ld]) ac_prog=ld -if test "$GCC" = yes; then +if test "$ac_cv_cc_name" = "gcc"; then ac_prog=`$CC -print-prog-name=ld` fi case `"$ac_prog" -V 2>&1 < /dev/null` in @@ -1874,14 +1875,12 @@ if test "$Py_OPT" = 'true' ; then DEF_MAKE_ALL_RULE="profile-opt" REQUIRE_PGO="yes" DEF_MAKE_RULE="build_all" - case $CC in - *gcc*) + AS_VAR_IF([ac_cv_gcc_compat], [yes], [ AX_CHECK_COMPILE_FLAG([-fno-semantic-interposition],[ CFLAGS_NODIST="$CFLAGS_NODIST -fno-semantic-interposition" LDFLAGS_NODIST="$LDFLAGS_NODIST -fno-semantic-interposition" ], [], [-Werror]) - ;; - esac + ]) elif test "$ac_sys_system" = "Emscripten" -o "$ac_sys_system" = "WASI"; then dnl Emscripten does not support shared extensions yet. Build dnl "python.[js,wasm]", "pybuilddir.txt", and "platform" files. @@ -1908,7 +1907,7 @@ AC_MSG_RESULT([$PROFILE_TASK]) llvm_bin_dir='' llvm_path="${PATH}" -if test "${CC}" = "clang" +if test "${ac_cv_cc_name}" = "clang" then clang_bin=`which clang` # Some systems install clang elsewhere as a symlink to the real path @@ -1955,8 +1954,8 @@ esac ], [AC_MSG_RESULT([no])]) if test "$Py_LTO" = 'true' ; then - case $CC in - *clang*) + case $ac_cv_cc_name in + clang) LDFLAGS_NOLTO="-fno-lto" dnl Clang linker requires -flto in order to link objects with LTO information. dnl Thin LTO is faster and works for object files with full LTO information, too. @@ -2019,14 +2018,14 @@ if test "$Py_LTO" = 'true' ; then ;; esac ;; - *emcc*) + emcc) if test "$Py_LTO_POLICY" != "default"; then AC_MSG_ERROR([emcc supports only default lto.]) fi LTOFLAGS="-flto" LTOCFLAGS="-flto" ;; - *gcc*) + gcc) if test $Py_LTO_POLICY = thin then AC_MSG_ERROR([thin lto is not supported under gcc compiler.]) @@ -2085,10 +2084,8 @@ then fi LLVM_PROF_ERR=no -# GNU Autoconf recommends the use of expr instead of basename. -AS_VAR_SET([CC_BASENAME], [$(expr "//$CC" : '.*/\(.*\)')]) -case "$CC_BASENAME" in - *clang*) +case "$ac_cv_cc_name" 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=\"\$(shell pwd)/code.profclangd\"" @@ -2107,35 +2104,13 @@ case "$CC_BASENAME" in fi fi ;; - *gcc*) - case $ac_sys_system in - Darwin*) - PGO_PROF_GEN_FLAG="-fprofile-instr-generate" - PGO_PROF_USE_FLAG="-fprofile-instr-use=\"\$(shell pwd)/code.profclangd\"" - LLVM_PROF_MERGER=m4_normalize(" - ${LLVM_PROFDATA} merge - -output=\"\$(shell pwd)/code.profclangd\" - \"\$(shell pwd)\"/*.profclangr - ") - LLVM_PROF_FILE="LLVM_PROFILE_FILE=\"\$(shell pwd)/code-%p.profclangr\"" - if test "${LLVM_PROF_FOUND}" = "not-found" - then - LLVM_PROF_ERR=yes - if test "${REQUIRE_PGO}" = "yes" - then - AC_MSG_ERROR([llvm-profdata is required for a --enable-optimizations build but could not be found.]) - fi - fi - ;; - *) - PGO_PROF_GEN_FLAG="-fprofile-generate" - PGO_PROF_USE_FLAG="-fprofile-use -fprofile-correction" - LLVM_PROF_MERGER="true" - LLVM_PROF_FILE="" - ;; - esac + gcc) + PGO_PROF_GEN_FLAG="-fprofile-generate" + PGO_PROF_USE_FLAG="-fprofile-use -fprofile-correction" + LLVM_PROF_MERGER="true" + LLVM_PROF_FILE="" ;; - *icc*) + icc) PGO_PROF_GEN_FLAG="-prof-gen" PGO_PROF_USE_FLAG="-prof-use" LLVM_PROF_MERGER="true" @@ -2259,19 +2234,6 @@ AC_MSG_RESULT([$BOLT_APPLY_FLAGS]) # compiler and platform. BASECFLAGS tweaks need to be made even if the # user set OPT. -case $CC in - *clang*) - cc_is_clang=1 - ;; - *) - if $CC --version 2>&1 | grep -q clang - then - cc_is_clang=1 - else - cc_is_clang= - fi -esac - dnl Historically, some of our code assumed that signed integer overflow dnl is defined behaviour via twos-complement. dnl Set STRICT_OVERFLOW_CFLAGS and NO_STRICT_OVERFLOW_CFLAGS depending on compiler support. @@ -2346,7 +2308,7 @@ if test "${OPT-unset}" = "unset" then case $GCC in yes) - if test -n "${cc_is_clang}" + if test "${ac_cv_cc_name}" != "clang" then # bpo-30104: disable strict aliasing to compile correctly dtoa.c, # see Makefile.pre.in for more information @@ -2526,8 +2488,7 @@ then AX_CHECK_COMPILE_FLAG([-D_FORTIFY_SOURCE=3], [CFLAGS_NODIST="$CFLAGS_NODIST -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3"], [AC_MSG_WARN([-D_FORTIFY_SOURCE=3 not supported])], [-Werror]) fi -case $GCC in -yes) +AS_VAR_IF([ac_cv_gcc_compat], [yes], [ CFLAGS_NODIST="$CFLAGS_NODIST -std=c11" PY_CHECK_CC_WARNING([enable], [extra], [if we can add -Wextra]) @@ -2568,8 +2529,8 @@ yes) # ICC doesn't recognize the option, but only emits a warning ## XXX does it emit an unused result warning and can it be disabled? - AS_CASE(["$CC_BASENAME"], - [*icc*], [ac_cv_disable_unused_result_warning=no] + AS_CASE(["$ac_cv_cc_name"], + [icc], [ac_cv_disable_unused_result_warning=no] [PY_CHECK_CC_WARNING([disable], [unused-result])]) AS_VAR_IF([ac_cv_disable_unused_result_warning], [yes], [BASECFLAGS="$BASECFLAGS -Wno-unused-result" @@ -2662,20 +2623,17 @@ yes) Darwin*) # -Wno-long-double, -no-cpp-precomp, and -mno-fused-madd # used to be here, but non-Apple gcc doesn't accept them. - if test "${CC}" = gcc - then - AC_MSG_CHECKING([which compiler should be used]) - case "${UNIVERSALSDK}" in - */MacOSX10.4u.sdk) - # Build using 10.4 SDK, force usage of gcc when the - # compiler is gcc, otherwise the user will get very - # confusing error messages when building on OSX 10.6 - CC=gcc-4.0 - CPP=cpp-4.0 - ;; - esac - AC_MSG_RESULT([$CC]) - fi + AC_MSG_CHECKING([which compiler should be used]) + case "${UNIVERSALSDK}" in + */MacOSX10.4u.sdk) + # Build using 10.4 SDK, force usage of gcc when the + # compiler is gcc, otherwise the user will get very + # confusing error messages when building on OSX 10.6 + CC=gcc-4.0 + CPP=cpp-4.0 + ;; + esac + AC_MSG_RESULT([$CC]) LIPO_INTEL64_FLAGS="" if test "${enable_universalsdk}" @@ -2800,9 +2758,7 @@ yes) # end of Darwin* tests ;; esac - ;; - -*) +], [ case $ac_sys_system in OpenUNIX*|UnixWare*) BASECFLAGS="$BASECFLAGS -K pentium,host,inline,loop_unroll,alloca " @@ -2811,18 +2767,17 @@ yes) BASECFLAGS="$BASECFLAGS -belf -Ki486 -DSCO5" ;; esac - ;; -esac +]) -case "$CC_BASENAME" in -*mpicc*) +case "$ac_cv_cc_name" in +mpicc) CFLAGS_NODIST="$CFLAGS_NODIST" ;; -*icc*) +icc) # ICC needs -fp-model strict or floats behave badly CFLAGS_NODIST="$CFLAGS_NODIST -fp-model strict" ;; -*xlc*) +xlc) CFLAGS_NODIST="$CFLAGS_NODIST -qalias=noansi -qmaxmem=-1" ;; esac @@ -3430,7 +3385,7 @@ then LDSHARED="\$(LIBPL)/ld_so_aix \$(CC) -bI:\$(LIBPL)/python.exp" ;; SunOS/5*) - if test "$GCC" = "yes" ; then + if test "$ac_cv_gcc_compat" = "yes" ; then LDSHARED='$(CC) -shared' LDCXXSHARED='$(CXX) -shared' else @@ -3438,7 +3393,7 @@ then LDCXXSHARED='$(CXX) -G' fi ;; hp*|HP*) - if test "$GCC" = "yes" ; then + if test "$ac_cv_gcc_compat" = "yes" ; then LDSHARED='$(CC) -shared' LDCXXSHARED='$(CXX) -shared' else @@ -3531,7 +3486,7 @@ then LDSHARED='$(CC) -shared' LDCXXSHARED='$(CXX) -shared';; OpenUNIX*|UnixWare*) - if test "$GCC" = "yes" ; then + if test "$ac_cv_gcc_compat" = "yes" ; then LDSHARED='$(CC) -shared' LDCXXSHARED='$(CXX) -shared' else @@ -3571,13 +3526,13 @@ AC_MSG_CHECKING([CCSHARED]) if test -z "$CCSHARED" then case $ac_sys_system/$ac_sys_release in - SunOS*) if test "$GCC" = yes; + SunOS*) if test "$ac_cv_gcc_compat" = "yes"; then CCSHARED="-fPIC"; elif test `uname -p` = sparc; then CCSHARED="-xcode=pic32"; else CCSHARED="-Kpic"; fi;; - hp*|HP*) if test "$GCC" = yes; + hp*|HP*) if test "$ac_cv_gcc_compat" = "yes"; then CCSHARED="-fPIC"; else CCSHARED="+z"; fi;; @@ -3589,12 +3544,12 @@ then FreeBSD*|NetBSD*|OpenBSD*|DragonFly*) CCSHARED="-fPIC";; Haiku*) CCSHARED="-fPIC";; OpenUNIX*|UnixWare*) - if test "$GCC" = "yes" + if test "$ac_cv_gcc_compat" = "yes" then CCSHARED="-fPIC" else CCSHARED="-KPIC" fi;; SCO_SV*) - if test "$GCC" = "yes" + if test "$ac_cv_gcc_compat" = "yes" then CCSHARED="-fPIC" else CCSHARED="-Kpic -belf" fi;; @@ -3652,13 +3607,13 @@ then then LINKFORSHARED="-Wl,--export-dynamic" fi;; - SunOS/5*) case $CC in - *gcc*) + SunOS/5*) if test "$ac_cv_gcc_compat" = "yes"; then if $CC -Xlinker --help 2>&1 | grep export-dynamic >/dev/null then LINKFORSHARED="-Xlinker --export-dynamic" - fi;; - esac;; + fi + fi + ;; CYGWIN*) if test $enable_shared = "no" then @@ -4228,7 +4183,7 @@ AS_VAR_IF( fi elif test $ac_cv_sizeof_size_t -eq 4; then if test "$ac_cv_gcc_asm_for_x87" = yes -a "$libmpdec_system" != sunos; then - AS_CASE([$CC], + AS_CASE([$ac_cv_cc_name], [*gcc*], [libmpdec_machine=ppro], [*clang*], [libmpdec_machine=ppro], [libmpdec_machine=ansi32] @@ -7150,8 +7105,8 @@ if test "$ac_cv_gcc_asm_for_x87" = yes; then # Some versions of gcc miscompile inline asm: # http://gcc.gnu.org/bugzilla/show_bug.cgi?id=46491 # http://gcc.gnu.org/ml/gcc/2010-11/msg00366.html - case $CC in - *gcc*) + case $ac_cv_cc_name in + gcc) AC_MSG_CHECKING([for gcc ipa-pure-const bug]) saved_cflags="$CFLAGS" CFLAGS="-O2" From 1fe67df8e373a5177143e4a310c83438e79f9b77 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Thu, 7 Nov 2024 01:57:01 -0800 Subject: [PATCH 088/219] gh-48020: [docs] Remove the logging howto suggested future FileHandler multiprocessing support (GH-126531) Docs: Remove the logging howto potential promise of multiprocessing support in the future. Stick to the facts and suggestions, don't provide hope where we're not going to implement complexity that we'd rather the user implement themselves when needed. --- Doc/howto/logging-cookbook.rst | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index 321ec0c0f73871..3cd2f1d96a7b34 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -1267,11 +1267,8 @@ to adapt in your own applications. You could also write your own handler which uses the :class:`~multiprocessing.Lock` class from the :mod:`multiprocessing` module to serialize access to the -file from your processes. The existing :class:`FileHandler` and subclasses do -not make use of :mod:`multiprocessing` at present, though they may do so in the -future. Note that at present, the :mod:`multiprocessing` module does not provide -working lock functionality on all platforms (see -https://bugs.python.org/issue3770). +file from your processes. The stdlib :class:`FileHandler` and subclasses do +not make use of :mod:`multiprocessing`. .. currentmodule:: logging.handlers From 78ad7e632248dc989378cabeb797b9f3d940d9f2 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Thu, 7 Nov 2024 11:06:27 +0100 Subject: [PATCH 089/219] gh-126499: test_ssl: Don't assume err.reason is a string (GH-126501) The skipping machinery called `getattr(err, "reason", "")` on an arbitrary exception. As intermittent Buildbot failures show, sometimes it's set to None. Convert it to string for this specific check. --- Lib/test/test_ssl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index de5110a1cc4b6d..ca9dac97c8e213 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -5029,7 +5029,7 @@ def non_linux_skip_if_other_okay_error(self, err): return # Expect the full test setup to always work on Linux. if (isinstance(err, ConnectionResetError) or (isinstance(err, OSError) and err.errno == errno.EINVAL) or - re.search('wrong.version.number', getattr(err, "reason", ""), re.I)): + re.search('wrong.version.number', str(getattr(err, "reason", "")), re.I)): # On Windows the TCP RST leads to a ConnectionResetError # (ECONNRESET) which Linux doesn't appear to surface to userspace. # If wrap_socket() winds up on the "if connected:" path and doing From c9cda1608edf7664c10f4f467e24591062c2fe62 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Thu, 7 Nov 2024 11:07:02 +0100 Subject: [PATCH 090/219] gh-126500: test_ssl: Don't stop ThreadedEchoServer on OSError in ConnectionHandler; rely on __exit__ (GH-126503) If `read()` in the ConnectionHandler thread raises `OSError` (except `ConnectionError`), the ConnectionHandler shuts down the entire ThreadedEchoServer, preventing further connections. It also does that for `EPROTOTYPE` in `wrap_conn`. As far as I can see, this is done to avoid the server thread getting stuck, forgotten, in its accept loop. However, since 2011 (5b95eb90a7167285b6544b50865227c584943c9a) the server is used as a context manager, and its `__exit__` does `stop()` and `join()`. (I'm not sure if we *always* used `with` since that commit, but currently we do.) Make sure that the context manager *is* used, and remove the `server.stop()` calls from ConnectionHandler. --- Lib/test/test_ssl.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index ca9dac97c8e213..59f37b3f9a7575 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -2299,7 +2299,6 @@ def wrap_conn(self): # See also http://erickt.github.io/blog/2014/11/19/adventures-in-debugging-a-potential-osx-kernel-bug/ if e.errno != errno.EPROTOTYPE and sys.platform != "darwin": self.running = False - self.server.stop() self.close() return False else: @@ -2436,10 +2435,6 @@ def run(self): self.close() self.running = False - # normally, we'd just stop here, but for the test - # harness, we want to stop the server - self.server.stop() - def __init__(self, certificate=None, ssl_version=None, certreqs=None, cacerts=None, chatty=True, connectionchatty=False, starttls_server=False, @@ -2473,21 +2468,33 @@ def __init__(self, certificate=None, ssl_version=None, self.conn_errors = [] threading.Thread.__init__(self) self.daemon = True + self._in_context = False def __enter__(self): + if self._in_context: + raise ValueError('Re-entering ThreadedEchoServer context') + self._in_context = True self.start(threading.Event()) self.flag.wait() return self def __exit__(self, *args): + assert self._in_context + self._in_context = False self.stop() self.join() def start(self, flag=None): + if not self._in_context: + raise ValueError( + 'ThreadedEchoServer must be used as a context manager') self.flag = flag threading.Thread.start(self) def run(self): + if not self._in_context: + raise ValueError( + 'ThreadedEchoServer must be used as a context manager') self.sock.settimeout(1.0) self.sock.listen(5) self.active = True From 85036c8d612007356d2118eb25b460505078b023 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Thu, 7 Nov 2024 10:48:27 +0000 Subject: [PATCH 091/219] GH-126222: Fix `_PyUop_num_popped` (GH-126507) --- Include/internal/pycore_uop_metadata.h | 112 +++++++++--------- Lib/test/test_capi/test_opt.py | 8 ++ ...-11-06-16-34-11.gh-issue-126222.9NBfTn.rst | 3 + .../cases_generator/uop_metadata_generator.py | 2 + 4 files changed, 69 insertions(+), 56 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-11-06-16-34-11.gh-issue-126222.9NBfTn.rst diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index ade297201f0ac2..98a41d1f23f569 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -658,7 +658,7 @@ int _PyUop_num_popped(int opcode, int oparg) case _TO_BOOL: return 1; case _TO_BOOL_BOOL: - return 1; + return 0; case _TO_BOOL_INT: return 1; case _TO_BOOL_LIST: @@ -672,11 +672,11 @@ int _PyUop_num_popped(int opcode, int oparg) case _UNARY_INVERT: return 1; case _GUARD_BOTH_INT: - return 2; + return 0; case _GUARD_NOS_INT: - return 2; + return 0; case _GUARD_TOS_INT: - return 1; + return 0; case _BINARY_OP_MULTIPLY_INT: return 2; case _BINARY_OP_ADD_INT: @@ -684,11 +684,11 @@ int _PyUop_num_popped(int opcode, int oparg) case _BINARY_OP_SUBTRACT_INT: return 2; case _GUARD_BOTH_FLOAT: - return 2; + return 0; case _GUARD_NOS_FLOAT: - return 2; + return 0; case _GUARD_TOS_FLOAT: - return 1; + return 0; case _BINARY_OP_MULTIPLY_FLOAT: return 2; case _BINARY_OP_ADD_FLOAT: @@ -696,7 +696,7 @@ int _PyUop_num_popped(int opcode, int oparg) case _BINARY_OP_SUBTRACT_FLOAT: return 2; case _GUARD_BOTH_UNICODE: - return 2; + return 0; case _BINARY_OP_ADD_UNICODE: return 2; case _BINARY_OP_INPLACE_ADD_UNICODE: @@ -716,13 +716,13 @@ int _PyUop_num_popped(int opcode, int oparg) case _BINARY_SUBSCR_DICT: return 2; case _BINARY_SUBSCR_CHECK_FUNC: - return 2; + return 0; case _BINARY_SUBSCR_INIT_CALL: return 2; case _LIST_APPEND: - return 2 + (oparg-1); + return 1; case _SET_ADD: - return 2 + (oparg-1); + return 1; case _STORE_SUBSCR: return 3; case _STORE_SUBSCR_LIST_INT: @@ -740,11 +740,11 @@ int _PyUop_num_popped(int opcode, int oparg) case _GET_AITER: return 1; case _GET_ANEXT: - return 1; + return 0; case _GET_AWAITABLE: return 1; case _SEND_GEN_FRAME: - return 2; + return 1; case _YIELD_VALUE: return 1; case _POP_EXCEPT: @@ -812,9 +812,9 @@ int _PyUop_num_popped(int opcode, int oparg) case _BUILD_LIST: return oparg; case _LIST_EXTEND: - return 2 + (oparg-1); + return 1; case _SET_UPDATE: - return 2 + (oparg-1); + return 1; case _BUILD_SET: return oparg; case _BUILD_MAP: @@ -822,11 +822,11 @@ int _PyUop_num_popped(int opcode, int oparg) case _SETUP_ANNOTATIONS: return 0; case _DICT_UPDATE: - return 2 + (oparg - 1); + return 1; case _DICT_MERGE: - return 5 + (oparg - 1); + return 1; case _MAP_ADD: - return 3 + (oparg - 1); + return 2; case _LOAD_SUPER_ATTR_ATTR: return 3; case _LOAD_SUPER_ATTR_METHOD: @@ -834,9 +834,9 @@ int _PyUop_num_popped(int opcode, int oparg) case _LOAD_ATTR: return 1; case _GUARD_TYPE_VERSION: - return 1; + return 0; case _CHECK_MANAGED_OBJECT_HAS_VALUES: - return 1; + return 0; case _LOAD_ATTR_INSTANCE_VALUE_0: return 1; case _LOAD_ATTR_INSTANCE_VALUE_1: @@ -844,11 +844,11 @@ int _PyUop_num_popped(int opcode, int oparg) case _LOAD_ATTR_INSTANCE_VALUE: return 1; case _CHECK_ATTR_MODULE: - return 1; + return 0; case _LOAD_ATTR_MODULE: return 1; case _CHECK_ATTR_WITH_HINT: - return 1; + return 0; case _LOAD_ATTR_WITH_HINT: return 1; case _LOAD_ATTR_SLOT_0: @@ -858,7 +858,7 @@ int _PyUop_num_popped(int opcode, int oparg) case _LOAD_ATTR_SLOT: return 1; case _CHECK_ATTR_CLASS: - return 1; + return 0; case _LOAD_ATTR_CLASS_0: return 1; case _LOAD_ATTR_CLASS_1: @@ -868,7 +868,7 @@ int _PyUop_num_popped(int opcode, int oparg) case _LOAD_ATTR_PROPERTY_FRAME: return 1; case _GUARD_DORV_NO_DICT: - return 1; + return 0; case _STORE_ATTR_INSTANCE_VALUE: return 2; case _STORE_ATTR_WITH_HINT: @@ -894,59 +894,59 @@ int _PyUop_num_popped(int opcode, int oparg) case _CHECK_EG_MATCH: return 2; case _CHECK_EXC_MATCH: - return 2; + return 1; case _IMPORT_NAME: return 2; case _IMPORT_FROM: - return 1; + return 0; case _IS_NONE: return 1; case _GET_LEN: - return 1; + return 0; case _MATCH_CLASS: return 3; case _MATCH_MAPPING: - return 1; + return 0; case _MATCH_SEQUENCE: - return 1; + return 0; case _MATCH_KEYS: - return 2; + return 0; case _GET_ITER: return 1; case _GET_YIELD_FROM_ITER: return 1; case _FOR_ITER_TIER_TWO: - return 1; + return 0; case _ITER_CHECK_LIST: - return 1; + return 0; case _GUARD_NOT_EXHAUSTED_LIST: - return 1; + return 0; case _ITER_NEXT_LIST: - return 1; + return 0; case _ITER_CHECK_TUPLE: - return 1; + return 0; case _GUARD_NOT_EXHAUSTED_TUPLE: - return 1; + return 0; case _ITER_NEXT_TUPLE: - return 1; + return 0; case _ITER_CHECK_RANGE: - return 1; + return 0; case _GUARD_NOT_EXHAUSTED_RANGE: - return 1; + return 0; case _ITER_NEXT_RANGE: - return 1; + return 0; case _FOR_ITER_GEN_FRAME: - return 1; + return 0; case _LOAD_SPECIAL: return 1; case _WITH_EXCEPT_START: - return 5; + return 0; case _PUSH_EXC_INFO: return 1; case _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT: - return 1; + return 0; case _GUARD_KEYS_VERSION: - return 1; + return 0; case _LOAD_ATTR_METHOD_WITH_VALUES: return 1; case _LOAD_ATTR_METHOD_NO_DICT: @@ -956,7 +956,7 @@ int _PyUop_num_popped(int opcode, int oparg) case _LOAD_ATTR_NONDESCRIPTOR_NO_DICT: return 1; case _CHECK_ATTR_METHOD_LAZY_DICT: - return 1; + return 0; case _LOAD_ATTR_METHOD_LAZY_DICT: return 1; case _MAYBE_EXPAND_METHOD: @@ -964,25 +964,25 @@ int _PyUop_num_popped(int opcode, int oparg) case _PY_FRAME_GENERAL: return 2 + oparg; case _CHECK_FUNCTION_VERSION: - return 2 + oparg; + return 0; case _CHECK_METHOD_VERSION: - return 2 + oparg; + return 0; case _EXPAND_METHOD: return 2 + oparg; case _CHECK_IS_NOT_PY_CALLABLE: - return 2 + oparg; + return 0; case _CALL_NON_PY_GENERAL: return 2 + oparg; case _CHECK_CALL_BOUND_METHOD_EXACT_ARGS: - return 2 + oparg; + return 0; case _INIT_CALL_BOUND_METHOD_EXACT_ARGS: return 2 + oparg; case _CHECK_PEP_523: return 0; case _CHECK_FUNCTION_EXACT_ARGS: - return 2 + oparg; + return 0; case _CHECK_STACK_SPACE: - return 2 + oparg; + return 0; case _INIT_CALL_PY_EXACT_ARGS_0: return 2 + oparg; case _INIT_CALL_PY_EXACT_ARGS_1: @@ -1036,17 +1036,17 @@ int _PyUop_num_popped(int opcode, int oparg) case _PY_FRAME_KW: return 3 + oparg; case _CHECK_FUNCTION_VERSION_KW: - return 3 + oparg; + return 0; case _CHECK_METHOD_VERSION_KW: - return 3 + oparg; + return 0; case _EXPAND_METHOD_KW: return 3 + oparg; case _CHECK_IS_NOT_PY_CALLABLE_KW: - return 3 + oparg; + return 0; case _CALL_KW_NON_PY: return 3 + oparg; case _MAKE_CALLARGS_A_TUPLE: - return 3 + (oparg & 1); + return 1 + (oparg & 1); case _MAKE_FUNCTION: return 1; case _SET_FUNCTION_ATTRIBUTE: @@ -1062,7 +1062,7 @@ int _PyUop_num_popped(int opcode, int oparg) case _FORMAT_WITH_SPEC: return 2; case _COPY: - return 1 + (oparg-1); + return 0; case _BINARY_OP: return 2; case _SWAP: diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index c352325ff3d08a..7b3d9e4fd1126f 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -1486,6 +1486,14 @@ def fn(a): fn(A()) + def test_jit_error_pops(self): + """ + Tests that the correct number of pops are inserted into the + exit stub + """ + items = 17 * [None] + [[]] + with self.assertRaises(TypeError): + {item for item in items} if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-06-16-34-11.gh-issue-126222.9NBfTn.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-06-16-34-11.gh-issue-126222.9NBfTn.rst new file mode 100644 index 00000000000000..ebf6673782f02c --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-06-16-34-11.gh-issue-126222.9NBfTn.rst @@ -0,0 +1,3 @@ +Do not include count of "peek" items in ``_PyUop_num_popped``. This ensures +that the correct number of items are popped from the stack when a micro-op +exits with an error. diff --git a/Tools/cases_generator/uop_metadata_generator.py b/Tools/cases_generator/uop_metadata_generator.py index 7b3325ada4a49f..6eb022899d6cae 100644 --- a/Tools/cases_generator/uop_metadata_generator.py +++ b/Tools/cases_generator/uop_metadata_generator.py @@ -51,6 +51,8 @@ def generate_names_and_flags(analysis: Analysis, out: CWriter) -> None: if uop.is_viable() and uop.properties.tier != 1: stack = Stack() for var in reversed(uop.stack.inputs): + if var.peek: + break stack.pop(var) popped = (-stack.base_offset).to_c() out.emit(f"case {uop.name}:\n") From e3510bd3dd9ea8f2a30cb1128470aee3a48d8880 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 7 Nov 2024 10:29:31 -0500 Subject: [PATCH 092/219] Doc: C API: Demote sections to subsections for consistency (#126535) The entire file should be a single section; the headings below the first heading should be subsections. --- Doc/c-api/typeobj.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index 8a185486fe44f1..ba58cc1c26c70b 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -2230,7 +2230,7 @@ This is done by filling a :c:type:`PyType_Spec` structure and calling .. _number-structs: Number Object Structures -======================== +------------------------ .. sectionauthor:: Amaury Forgeot d'Arc @@ -2344,7 +2344,7 @@ Number Object Structures .. _mapping-structs: Mapping Object Structures -========================= +------------------------- .. sectionauthor:: Amaury Forgeot d'Arc @@ -2381,7 +2381,7 @@ Mapping Object Structures .. _sequence-structs: Sequence Object Structures -========================== +-------------------------- .. sectionauthor:: Amaury Forgeot d'Arc @@ -2461,7 +2461,7 @@ Sequence Object Structures .. _buffer-structs: Buffer Object Structures -======================== +------------------------ .. sectionauthor:: Greg J. Stein .. sectionauthor:: Benjamin Peterson @@ -2556,7 +2556,7 @@ Buffer Object Structures Async Object Structures -======================= +----------------------- .. sectionauthor:: Yury Selivanov @@ -2624,7 +2624,7 @@ Async Object Structures .. _slot-typedefs: Slot Type typedefs -================== +------------------ .. c:type:: PyObject *(*allocfunc)(PyTypeObject *cls, Py_ssize_t nitems) @@ -2733,7 +2733,7 @@ Slot Type typedefs .. _typedef-examples: Examples -======== +-------- The following are simple examples of Python type definitions. They include common usage you may encounter. Some demonstrate tricky corner From 19c248185343dfad046bbe4046b2b900e7405666 Mon Sep 17 00:00:00 2001 From: Valerii <81074936+valerii-chirkov@users.noreply.github.com> Date: Thu, 7 Nov 2024 20:35:29 +0500 Subject: [PATCH 093/219] gh-126529: Update devguide links to relative filenames in InternalDocs (#126530) Update devguide links to relative filenames in InternalDocs/parser.md and InternalDocs/compiler.md. --- InternalDocs/compiler.md | 4 ++-- InternalDocs/parser.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/InternalDocs/compiler.md b/InternalDocs/compiler.md index 0da4670c792cb5..37964bd99428df 100644 --- a/InternalDocs/compiler.md +++ b/InternalDocs/compiler.md @@ -42,10 +42,10 @@ The definitions for literal tokens (such as `:`, numbers, etc.) can be found in See Also: -* [Guide to the parser](https://devguide.python.org/internals/parser/index.html) +* [Guide to the parser](parser.md) for a detailed description of the parser. -* [Changing CPython’s grammar](https://devguide.python.org/developer-workflow/grammar/#grammar) +* [Changing CPython’s grammar](changing_grammar.md) for a detailed description of the grammar. diff --git a/InternalDocs/parser.md b/InternalDocs/parser.md index a0c70b46087d1a..348988b7c2f003 100644 --- a/InternalDocs/parser.md +++ b/InternalDocs/parser.md @@ -17,7 +17,7 @@ Therefore, changes to the Python language are made by modifying the [grammar file](../Grammar/python.gram). Developers rarely need to modify the generator itself. -See [Changing CPython's grammar](./changing_grammar.md) +See [Changing CPython's grammar](changing_grammar.md) for a detailed description of the grammar and the process for changing it. How PEG parsers work From 3d9f9ae5a7c4739fe319aa436ab1834d6765b0ac Mon Sep 17 00:00:00 2001 From: Aditya Borikar Date: Thu, 7 Nov 2024 07:37:41 -0800 Subject: [PATCH 094/219] Chore: Fix typo in `pyarena.c` (#126527) --- Python/pyarena.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/pyarena.c b/Python/pyarena.c index 7ab370163b2b93..28970f9d0670f4 100644 --- a/Python/pyarena.c +++ b/Python/pyarena.c @@ -4,7 +4,7 @@ /* A simple arena block structure. Measurements with standard library modules suggest the average - allocation is about 20 bytes and that most compiles use a single + allocation is about 20 bytes and that most compilers use a single block. */ From 9357fdcaf0b08dac9396c17e8695b420fad887f8 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 7 Nov 2024 09:32:42 -0700 Subject: [PATCH 095/219] gh-76785: Minor Cleanup of "Cross-interpreter" Code (gh-126457) The primary objective here is to allow some later changes to be cleaner. Mostly this involves renaming things and moving a few things around. * CrossInterpreterData -> XIData * crossinterpdatafunc -> xidatafunc * split out pycore_crossinterp_data_registry.h * add _PyXIData_lookup_t --- Include/internal/pycore_crossinterp.h | 114 ++++------ .../pycore_crossinterp_data_registry.h | 36 +++ Include/internal/pycore_runtime_init.h | 6 +- Makefile.pre.in | 1 + Modules/_interpchannelsmodule.c | 70 +++--- Modules/_interpqueuesmodule.c | 53 ++--- Modules/_interpreters_common.h | 10 +- Modules/_interpretersmodule.c | 24 +- Modules/_testinternalcapi.c | 22 +- PCbuild/pythoncore.vcxproj | 1 + PCbuild/pythoncore.vcxproj.filters | 3 + Python/crossinterp.c | 131 +++++------ Python/crossinterp_data_lookup.h | 206 +++++++++--------- Python/pystate.c | 2 +- Tools/c-analyzer/cpython/_parser.py | 1 + 15 files changed, 343 insertions(+), 337 deletions(-) create mode 100644 Include/internal/pycore_crossinterp_data_registry.h diff --git a/Include/internal/pycore_crossinterp.h b/Include/internal/pycore_crossinterp.h index 2dd165eae74850..e91e911feb38cc 100644 --- a/Include/internal/pycore_crossinterp.h +++ b/Include/internal/pycore_crossinterp.h @@ -38,28 +38,28 @@ extern int _Py_CallInInterpreterAndRawFree( /* cross-interpreter data */ /**************************/ -typedef struct _xid _PyCrossInterpreterData; -typedef PyObject *(*xid_newobjectfunc)(_PyCrossInterpreterData *); +typedef struct _xid _PyXIData_t; +typedef PyObject *(*xid_newobjectfunc)(_PyXIData_t *); typedef void (*xid_freefunc)(void *); -// _PyCrossInterpreterData is similar to Py_buffer as an effectively +// _PyXIData_t is similar to Py_buffer as an effectively // opaque struct that holds data outside the object machinery. This // is necessary to pass safely between interpreters in the same process. struct _xid { // data is the cross-interpreter-safe derivation of a Python object - // (see _PyObject_GetCrossInterpreterData). It will be NULL if the + // (see _PyObject_GetXIData). It will be NULL if the // new_object func (below) encodes the data. void *data; // obj is the Python object from which the data was derived. This // is non-NULL only if the data remains bound to the object in some // way, such that the object must be "released" (via a decref) when // the data is released. In that case the code that sets the field, - // likely a registered "crossinterpdatafunc", is responsible for + // likely a registered "xidatafunc", is responsible for // ensuring it owns the reference (i.e. incref). PyObject *obj; // interp is the ID of the owning interpreter of the original // object. It corresponds to the active interpreter when - // _PyObject_GetCrossInterpreterData() was called. This should only + // _PyObject_GetXIData() was called. This should only // be set by the cross-interpreter machinery. // // We use the ID rather than the PyInterpreterState to avoid issues @@ -77,96 +77,77 @@ struct _xid { // okay (e.g. bytes) and for those types this field should be set // to NULL. However, for most the data was allocated just for // cross-interpreter use, so it must be freed when - // _PyCrossInterpreterData_Release is called or the memory will + // _PyXIData_Release is called or the memory will // leak. In that case, at the very least this field should be set // to PyMem_RawFree (the default if not explicitly set to NULL). // The call will happen with the original interpreter activated. xid_freefunc free; }; -PyAPI_FUNC(_PyCrossInterpreterData *) _PyCrossInterpreterData_New(void); -PyAPI_FUNC(void) _PyCrossInterpreterData_Free(_PyCrossInterpreterData *data); +PyAPI_FUNC(_PyXIData_t *) _PyXIData_New(void); +PyAPI_FUNC(void) _PyXIData_Free(_PyXIData_t *data); -#define _PyCrossInterpreterData_DATA(DATA) ((DATA)->data) -#define _PyCrossInterpreterData_OBJ(DATA) ((DATA)->obj) -#define _PyCrossInterpreterData_INTERPID(DATA) ((DATA)->interpid) +#define _PyXIData_DATA(DATA) ((DATA)->data) +#define _PyXIData_OBJ(DATA) ((DATA)->obj) +#define _PyXIData_INTERPID(DATA) ((DATA)->interpid) // Users should not need getters for "new_object" or "free". +/* getting cross-interpreter data */ + +typedef int (*xidatafunc)(PyThreadState *tstate, PyObject *, _PyXIData_t *); + +typedef struct _xid_lookup_state _PyXIData_lookup_t; + +PyAPI_FUNC(xidatafunc) _PyXIData_Lookup(PyObject *); +PyAPI_FUNC(int) _PyObject_CheckXIData(PyObject *); +PyAPI_FUNC(int) _PyObject_GetXIData(PyObject *, _PyXIData_t *); + + +/* using cross-interpreter data */ + +PyAPI_FUNC(PyObject *) _PyXIData_NewObject(_PyXIData_t *); +PyAPI_FUNC(int) _PyXIData_Release(_PyXIData_t *); +PyAPI_FUNC(int) _PyXIData_ReleaseAndRawFree(_PyXIData_t *); + + /* defining cross-interpreter data */ -PyAPI_FUNC(void) _PyCrossInterpreterData_Init( - _PyCrossInterpreterData *data, +PyAPI_FUNC(void) _PyXIData_Init( + _PyXIData_t *data, PyInterpreterState *interp, void *shared, PyObject *obj, xid_newobjectfunc new_object); -PyAPI_FUNC(int) _PyCrossInterpreterData_InitWithSize( - _PyCrossInterpreterData *, +PyAPI_FUNC(int) _PyXIData_InitWithSize( + _PyXIData_t *, PyInterpreterState *interp, const size_t, PyObject *, xid_newobjectfunc); -PyAPI_FUNC(void) _PyCrossInterpreterData_Clear( - PyInterpreterState *, _PyCrossInterpreterData *); +PyAPI_FUNC(void) _PyXIData_Clear( PyInterpreterState *, _PyXIData_t *); // Normally the Init* functions are sufficient. The only time // additional initialization might be needed is to set the "free" func, // though that should be infrequent. -#define _PyCrossInterpreterData_SET_FREE(DATA, FUNC) \ +#define _PyXIData_SET_FREE(DATA, FUNC) \ do { \ (DATA)->free = (FUNC); \ } while (0) // Additionally, some shareable types are essentially light wrappers -// around other shareable types. The crossinterpdatafunc of the wrapper +// around other shareable types. The xidatafunc of the wrapper // can often be implemented by calling the wrapped object's -// crossinterpdatafunc and then changing the "new_object" function. -// We have _PyCrossInterpreterData_SET_NEW_OBJECT() here for that, +// xidatafunc and then changing the "new_object" function. +// We have _PyXIData_SET_NEW_OBJECT() here for that, // but might be better to have a function like -// _PyCrossInterpreterData_AdaptToWrapper() instead. -#define _PyCrossInterpreterData_SET_NEW_OBJECT(DATA, FUNC) \ +// _PyXIData_AdaptToWrapper() instead. +#define _PyXIData_SET_NEW_OBJECT(DATA, FUNC) \ do { \ (DATA)->new_object = (FUNC); \ } while (0) -/* using cross-interpreter data */ - -PyAPI_FUNC(int) _PyObject_CheckCrossInterpreterData(PyObject *); -PyAPI_FUNC(int) _PyObject_GetCrossInterpreterData(PyObject *, _PyCrossInterpreterData *); -PyAPI_FUNC(PyObject *) _PyCrossInterpreterData_NewObject(_PyCrossInterpreterData *); -PyAPI_FUNC(int) _PyCrossInterpreterData_Release(_PyCrossInterpreterData *); -PyAPI_FUNC(int) _PyCrossInterpreterData_ReleaseAndRawFree(_PyCrossInterpreterData *); - - /* cross-interpreter data registry */ -// For now we use a global registry of shareable classes. An -// alternative would be to add a tp_* slot for a class's -// crossinterpdatafunc. It would be simpler and more efficient. - -typedef int (*crossinterpdatafunc)(PyThreadState *tstate, PyObject *, - _PyCrossInterpreterData *); - -struct _xidregitem; - -struct _xidregitem { - struct _xidregitem *prev; - struct _xidregitem *next; - /* This can be a dangling pointer, but only if weakref is set. */ - PyTypeObject *cls; - /* This is NULL for builtin types. */ - PyObject *weakref; - size_t refcount; - crossinterpdatafunc getdata; -}; - -struct _xidregistry { - int global; /* builtin types or heap types */ - int initialized; - PyMutex mutex; - struct _xidregitem *head; -}; - -PyAPI_FUNC(int) _PyCrossInterpreterData_RegisterClass(PyTypeObject *, crossinterpdatafunc); -PyAPI_FUNC(int) _PyCrossInterpreterData_UnregisterClass(PyTypeObject *); -PyAPI_FUNC(crossinterpdatafunc) _PyCrossInterpreterData_Lookup(PyObject *); +#define Py_CORE_CROSSINTERP_DATA_REGISTRY_H +#include "pycore_crossinterp_data_registry.h" +#undef Py_CORE_CROSSINTERP_DATA_REGISTRY_H /*****************************/ @@ -175,14 +156,12 @@ PyAPI_FUNC(crossinterpdatafunc) _PyCrossInterpreterData_Lookup(PyObject *); struct _xi_runtime_state { // builtin types - // XXX Remove this field once we have a tp_* slot. - struct _xidregistry registry; + _PyXIData_lookup_t data_lookup; }; struct _xi_state { // heap types - // XXX Remove this field once we have a tp_* slot. - struct _xidregistry registry; + _PyXIData_lookup_t data_lookup; // heap types PyObject *PyExc_NotShareableError; @@ -190,7 +169,6 @@ struct _xi_state { extern PyStatus _PyXI_Init(PyInterpreterState *interp); extern void _PyXI_Fini(PyInterpreterState *interp); - extern PyStatus _PyXI_InitTypes(PyInterpreterState *interp); extern void _PyXI_FiniTypes(PyInterpreterState *interp); diff --git a/Include/internal/pycore_crossinterp_data_registry.h b/Include/internal/pycore_crossinterp_data_registry.h new file mode 100644 index 00000000000000..2990c6af62e952 --- /dev/null +++ b/Include/internal/pycore_crossinterp_data_registry.h @@ -0,0 +1,36 @@ +#ifndef Py_CORE_CROSSINTERP_DATA_REGISTRY_H +# error "this header must not be included directly" +#endif + + +// For now we use a global registry of shareable classes. An +// alternative would be to add a tp_* slot for a class's +// xidatafunc. It would be simpler and more efficient. + +struct _xidregitem; + +struct _xidregitem { + struct _xidregitem *prev; + struct _xidregitem *next; + /* This can be a dangling pointer, but only if weakref is set. */ + PyTypeObject *cls; + /* This is NULL for builtin types. */ + PyObject *weakref; + size_t refcount; + xidatafunc getdata; +}; + +struct _xidregistry { + int global; /* builtin types or heap types */ + int initialized; + PyMutex mutex; + struct _xidregitem *head; +}; + +PyAPI_FUNC(int) _PyXIData_RegisterClass(PyTypeObject *, xidatafunc); +PyAPI_FUNC(int) _PyXIData_UnregisterClass(PyTypeObject *); + +struct _xid_lookup_state { + // XXX Remove this field once we have a tp_* slot. + struct _xidregistry registry; +}; diff --git a/Include/internal/pycore_runtime_init.h b/Include/internal/pycore_runtime_init.h index e99febab2f3d57..bd3d704cb77730 100644 --- a/Include/internal/pycore_runtime_init.h +++ b/Include/internal/pycore_runtime_init.h @@ -50,8 +50,10 @@ extern PyTypeObject _PyExc_MemoryError; .next_id = -1, \ }, \ .xi = { \ - .registry = { \ - .global = 1, \ + .data_lookup = { \ + .registry = { \ + .global = 1, \ + }, \ }, \ }, \ /* A TSS key must be initialized with Py_tss_NEEDS_INIT \ diff --git a/Makefile.pre.in b/Makefile.pre.in index c650ecaf7be137..a337223d4d8608 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1203,6 +1203,7 @@ PYTHON_HEADERS= \ $(srcdir)/Include/internal/pycore_context.h \ $(srcdir)/Include/internal/pycore_critical_section.h \ $(srcdir)/Include/internal/pycore_crossinterp.h \ + $(srcdir)/Include/internal/pycore_crossinterp_data_registry.h \ $(srcdir)/Include/internal/pycore_debug_offsets.h \ $(srcdir)/Include/internal/pycore_descrobject.h \ $(srcdir)/Include/internal/pycore_dict.h \ diff --git a/Modules/_interpchannelsmodule.c b/Modules/_interpchannelsmodule.c index 5dc032b46cac9a..b8d7dfb87cce0e 100644 --- a/Modules/_interpchannelsmodule.c +++ b/Modules/_interpchannelsmodule.c @@ -6,7 +6,7 @@ #endif #include "Python.h" -#include "pycore_crossinterp.h" // struct _xid +#include "pycore_crossinterp.h" // _PyXIData_t #include "pycore_interp.h" // _PyInterpreterState_LookUpID() #include "pycore_pystate.h" // _PyInterpreterState_GetIDObject() @@ -59,7 +59,7 @@ _globals (static struct globals): first (struct _channelitem *): next (struct _channelitem *): ... - data (_PyCrossInterpreterData *): + data (_PyXIData_t *): data (void *) obj (PyObject *) interpid (int64_t) @@ -80,10 +80,10 @@ The above state includes the following allocations by the module: * 1 struct _channelqueue * for each item in each channel: * 1 struct _channelitem - * 1 _PyCrossInterpreterData + * 1 _PyXIData_t The only objects in that global state are the references held by each -channel's queue, which are safely managed via the _PyCrossInterpreterData_*() +channel's queue, which are safely managed via the _PyXIData_*() API.. The module does not create any objects that are shared globally. */ @@ -102,7 +102,7 @@ API.. The module does not create any objects that are shared globally. #define XID_FREE 2 static int -_release_xid_data(_PyCrossInterpreterData *data, int flags) +_release_xid_data(_PyXIData_t *data, int flags) { int ignoreexc = flags & XID_IGNORE_EXC; PyObject *exc; @@ -111,10 +111,10 @@ _release_xid_data(_PyCrossInterpreterData *data, int flags) } int res; if (flags & XID_FREE) { - res = _PyCrossInterpreterData_ReleaseAndRawFree(data); + res = _PyXIData_ReleaseAndRawFree(data); } else { - res = _PyCrossInterpreterData_Release(data); + res = _PyXIData_Release(data); } if (res < 0) { /* The owning interpreter is already destroyed. */ @@ -519,7 +519,7 @@ typedef struct _channelitem { This is necessary because item->data might be NULL, meaning the interpreter has been destroyed. */ int64_t interpid; - _PyCrossInterpreterData *data; + _PyXIData_t *data; _waiting_t *waiting; int unboundop; struct _channelitem *next; @@ -533,7 +533,7 @@ _channelitem_ID(_channelitem *item) static void _channelitem_init(_channelitem *item, - int64_t interpid, _PyCrossInterpreterData *data, + int64_t interpid, _PyXIData_t *data, _waiting_t *waiting, int unboundop) { if (interpid < 0) { @@ -541,8 +541,8 @@ _channelitem_init(_channelitem *item, } else { assert(data == NULL - || _PyCrossInterpreterData_INTERPID(data) < 0 - || interpid == _PyCrossInterpreterData_INTERPID(data)); + || _PyXIData_INTERPID(data) < 0 + || interpid == _PyXIData_INTERPID(data)); } *item = (_channelitem){ .interpid = interpid, @@ -580,7 +580,7 @@ _channelitem_clear(_channelitem *item) } static _channelitem * -_channelitem_new(int64_t interpid, _PyCrossInterpreterData *data, +_channelitem_new(int64_t interpid, _PyXIData_t *data, _waiting_t *waiting, int unboundop) { _channelitem *item = GLOBAL_MALLOC(_channelitem); @@ -611,7 +611,7 @@ _channelitem_free_all(_channelitem *item) static void _channelitem_popped(_channelitem *item, - _PyCrossInterpreterData **p_data, _waiting_t **p_waiting, + _PyXIData_t **p_data, _waiting_t **p_waiting, int *p_unboundop) { assert(item->waiting == NULL || item->waiting->status == WAITING_ACQUIRED); @@ -634,7 +634,7 @@ _channelitem_clear_interpreter(_channelitem *item) assert(item->unboundop != UNBOUND_REMOVE); return 0; } - assert(_PyCrossInterpreterData_INTERPID(item->data) == item->interpid); + assert(_PyXIData_INTERPID(item->data) == item->interpid); switch (item->unboundop) { case UNBOUND_REMOVE: @@ -691,7 +691,7 @@ _channelqueue_free(_channelqueue *queue) static int _channelqueue_put(_channelqueue *queue, - int64_t interpid, _PyCrossInterpreterData *data, + int64_t interpid, _PyXIData_t *data, _waiting_t *waiting, int unboundop) { _channelitem *item = _channelitem_new(interpid, data, waiting, unboundop); @@ -717,7 +717,7 @@ _channelqueue_put(_channelqueue *queue, static int _channelqueue_get(_channelqueue *queue, - _PyCrossInterpreterData **p_data, _waiting_t **p_waiting, + _PyXIData_t **p_data, _waiting_t **p_waiting, int *p_unboundop) { _channelitem *item = queue->first; @@ -769,7 +769,7 @@ _channelqueue_find(_channelqueue *queue, _channelitem_id_t itemid, static void _channelqueue_remove(_channelqueue *queue, _channelitem_id_t itemid, - _PyCrossInterpreterData **p_data, _waiting_t **p_waiting) + _PyXIData_t **p_data, _waiting_t **p_waiting) { _channelitem *prev = NULL; _channelitem *item = NULL; @@ -1128,8 +1128,7 @@ _channel_free(_channel_state *chan) static int _channel_add(_channel_state *chan, int64_t interpid, - _PyCrossInterpreterData *data, _waiting_t *waiting, - int unboundop) + _PyXIData_t *data, _waiting_t *waiting, int unboundop) { int res = -1; PyThread_acquire_lock(chan->mutex, WAIT_LOCK); @@ -1156,8 +1155,7 @@ _channel_add(_channel_state *chan, int64_t interpid, static int _channel_next(_channel_state *chan, int64_t interpid, - _PyCrossInterpreterData **p_data, _waiting_t **p_waiting, - int *p_unboundop) + _PyXIData_t **p_data, _waiting_t **p_waiting, int *p_unboundop) { int err = 0; PyThread_acquire_lock(chan->mutex, WAIT_LOCK); @@ -1193,7 +1191,7 @@ _channel_next(_channel_state *chan, int64_t interpid, static void _channel_remove(_channel_state *chan, _channelitem_id_t itemid) { - _PyCrossInterpreterData *data = NULL; + _PyXIData_t *data = NULL; _waiting_t *waiting = NULL; PyThread_acquire_lock(chan->mutex, WAIT_LOCK); @@ -1776,12 +1774,12 @@ channel_send(_channels *channels, int64_t cid, PyObject *obj, } // Convert the object to cross-interpreter data. - _PyCrossInterpreterData *data = GLOBAL_MALLOC(_PyCrossInterpreterData); + _PyXIData_t *data = GLOBAL_MALLOC(_PyXIData_t); if (data == NULL) { PyThread_release_lock(mutex); return -1; } - if (_PyObject_GetCrossInterpreterData(obj, data) != 0) { + if (_PyObject_GetXIData(obj, data) != 0) { PyThread_release_lock(mutex); GLOBAL_FREE(data); return -1; @@ -1904,7 +1902,7 @@ channel_recv(_channels *channels, int64_t cid, PyObject **res, int *p_unboundop) // Past this point we are responsible for releasing the mutex. // Pop off the next item from the channel. - _PyCrossInterpreterData *data = NULL; + _PyXIData_t *data = NULL; _waiting_t *waiting = NULL; err = _channel_next(chan, interpid, &data, &waiting, p_unboundop); PyThread_release_lock(mutex); @@ -1919,7 +1917,7 @@ channel_recv(_channels *channels, int64_t cid, PyObject **res, int *p_unboundop) } // Convert the data back to an object. - PyObject *obj = _PyCrossInterpreterData_NewObject(data); + PyObject *obj = _PyXIData_NewObject(data); if (obj == NULL) { assert(PyErr_Occurred()); // It was allocated in channel_send(), so we free it. @@ -2545,10 +2543,9 @@ struct _channelid_xid { }; static PyObject * -_channelid_from_xid(_PyCrossInterpreterData *data) +_channelid_from_xid(_PyXIData_t *data) { - struct _channelid_xid *xid = \ - (struct _channelid_xid *)_PyCrossInterpreterData_DATA(data); + struct _channelid_xid *xid = (struct _channelid_xid *)_PyXIData_DATA(data); // It might not be imported yet, so we can't use _get_current_module(). PyObject *mod = PyImport_ImportModule(MODULE_NAME_STR); @@ -2594,18 +2591,16 @@ _channelid_from_xid(_PyCrossInterpreterData *data) } static int -_channelid_shared(PyThreadState *tstate, PyObject *obj, - _PyCrossInterpreterData *data) +_channelid_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *data) { - if (_PyCrossInterpreterData_InitWithSize( + if (_PyXIData_InitWithSize( data, tstate->interp, sizeof(struct _channelid_xid), obj, _channelid_from_xid ) < 0) { return -1; } - struct _channelid_xid *xid = \ - (struct _channelid_xid *)_PyCrossInterpreterData_DATA(data); + struct _channelid_xid *xid = (struct _channelid_xid *)_PyXIData_DATA(data); xid->cid = ((channelid *)obj)->cid; xid->end = ((channelid *)obj)->end; xid->resolve = ((channelid *)obj)->resolve; @@ -2745,7 +2740,7 @@ _get_current_channelend_type(int end) } static PyObject * -_channelend_from_xid(_PyCrossInterpreterData *data) +_channelend_from_xid(_PyXIData_t *data) { channelid *cidobj = (channelid *)_channelid_from_xid(data); if (cidobj == NULL) { @@ -2762,8 +2757,7 @@ _channelend_from_xid(_PyCrossInterpreterData *data) } static int -_channelend_shared(PyThreadState *tstate, PyObject *obj, - _PyCrossInterpreterData *data) +_channelend_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *data) { PyObject *cidobj = PyObject_GetAttrString(obj, "_id"); if (cidobj == NULL) { @@ -2774,7 +2768,7 @@ _channelend_shared(PyThreadState *tstate, PyObject *obj, if (res < 0) { return -1; } - _PyCrossInterpreterData_SET_NEW_OBJECT(data, _channelend_from_xid); + _PyXIData_SET_NEW_OBJECT(data, _channelend_from_xid); return 0; } diff --git a/Modules/_interpqueuesmodule.c b/Modules/_interpqueuesmodule.c index 297a1763a98ce6..8d0e223db7ff19 100644 --- a/Modules/_interpqueuesmodule.c +++ b/Modules/_interpqueuesmodule.c @@ -6,7 +6,7 @@ #endif #include "Python.h" -#include "pycore_crossinterp.h" // struct _xid +#include "pycore_crossinterp.h" // _PyXIData_t #define REGISTERS_HEAP_TYPES #define HAS_UNBOUND_ITEMS @@ -30,7 +30,7 @@ #define XID_FREE 2 static int -_release_xid_data(_PyCrossInterpreterData *data, int flags) +_release_xid_data(_PyXIData_t *data, int flags) { int ignoreexc = flags & XID_IGNORE_EXC; PyObject *exc; @@ -39,10 +39,10 @@ _release_xid_data(_PyCrossInterpreterData *data, int flags) } int res; if (flags & XID_FREE) { - res = _PyCrossInterpreterData_ReleaseAndRawFree(data); + res = _PyXIData_ReleaseAndRawFree(data); } else { - res = _PyCrossInterpreterData_Release(data); + res = _PyXIData_Release(data); } if (res < 0) { /* The owning interpreter is already destroyed. */ @@ -400,7 +400,7 @@ typedef struct _queueitem { This is necessary because item->data might be NULL, meaning the interpreter has been destroyed. */ int64_t interpid; - _PyCrossInterpreterData *data; + _PyXIData_t *data; int fmt; int unboundop; struct _queueitem *next; @@ -408,16 +408,15 @@ typedef struct _queueitem { static void _queueitem_init(_queueitem *item, - int64_t interpid, _PyCrossInterpreterData *data, - int fmt, int unboundop) + int64_t interpid, _PyXIData_t *data, int fmt, int unboundop) { if (interpid < 0) { interpid = _get_interpid(data); } else { assert(data == NULL - || _PyCrossInterpreterData_INTERPID(data) < 0 - || interpid == _PyCrossInterpreterData_INTERPID(data)); + || _PyXIData_INTERPID(data) < 0 + || interpid == _PyXIData_INTERPID(data)); } assert(check_unbound(unboundop)); *item = (_queueitem){ @@ -447,8 +446,7 @@ _queueitem_clear(_queueitem *item) } static _queueitem * -_queueitem_new(int64_t interpid, _PyCrossInterpreterData *data, - int fmt, int unboundop) +_queueitem_new(int64_t interpid, _PyXIData_t *data, int fmt, int unboundop) { _queueitem *item = GLOBAL_MALLOC(_queueitem); if (item == NULL) { @@ -478,7 +476,7 @@ _queueitem_free_all(_queueitem *item) static void _queueitem_popped(_queueitem *item, - _PyCrossInterpreterData **p_data, int *p_fmt, int *p_unboundop) + _PyXIData_t **p_data, int *p_fmt, int *p_unboundop) { *p_data = item->data; *p_fmt = item->fmt; @@ -498,7 +496,7 @@ _queueitem_clear_interpreter(_queueitem *item) assert(item->unboundop != UNBOUND_REMOVE); return 0; } - assert(_PyCrossInterpreterData_INTERPID(item->data) == item->interpid); + assert(_PyXIData_INTERPID(item->data) == item->interpid); switch (item->unboundop) { case UNBOUND_REMOVE: @@ -633,7 +631,7 @@ _queue_unlock(_queue *queue) } static int -_queue_add(_queue *queue, int64_t interpid, _PyCrossInterpreterData *data, +_queue_add(_queue *queue, int64_t interpid, _PyXIData_t *data, int fmt, int unboundop) { int err = _queue_lock(queue); @@ -671,7 +669,7 @@ _queue_add(_queue *queue, int64_t interpid, _PyCrossInterpreterData *data, static int _queue_next(_queue *queue, - _PyCrossInterpreterData **p_data, int *p_fmt, int *p_unboundop) + _PyXIData_t **p_data, int *p_fmt, int *p_unboundop) { int err = _queue_lock(queue); if (err < 0) { @@ -1138,17 +1136,17 @@ queue_put(_queues *queues, int64_t qid, PyObject *obj, int fmt, int unboundop) assert(queue != NULL); // Convert the object to cross-interpreter data. - _PyCrossInterpreterData *data = GLOBAL_MALLOC(_PyCrossInterpreterData); + _PyXIData_t *data = GLOBAL_MALLOC(_PyXIData_t); if (data == NULL) { _queue_unmark_waiter(queue, queues->mutex); return -1; } - if (_PyObject_GetCrossInterpreterData(obj, data) != 0) { + if (_PyObject_GetXIData(obj, data) != 0) { _queue_unmark_waiter(queue, queues->mutex); GLOBAL_FREE(data); return -1; } - assert(_PyCrossInterpreterData_INTERPID(data) == \ + assert(_PyXIData_INTERPID(data) == \ PyInterpreterState_GetID(PyInterpreterState_Get())); // Add the data to the queue. @@ -1184,7 +1182,7 @@ queue_get(_queues *queues, int64_t qid, assert(queue != NULL); // Pop off the next item from the queue. - _PyCrossInterpreterData *data = NULL; + _PyXIData_t *data = NULL; err = _queue_next(queue, &data, p_fmt, p_unboundop); _queue_unmark_waiter(queue, queues->mutex); if (err != 0) { @@ -1196,7 +1194,7 @@ queue_get(_queues *queues, int64_t qid, } // Convert the data back to an object. - PyObject *obj = _PyCrossInterpreterData_NewObject(data); + PyObject *obj = _PyXIData_NewObject(data); if (obj == NULL) { assert(PyErr_Occurred()); // It was allocated in queue_put(), so we free it. @@ -1258,8 +1256,7 @@ queue_get_count(_queues *queues, int64_t qid, Py_ssize_t *p_count) /* external Queue objects ***************************************************/ -static int _queueobj_shared(PyThreadState *, - PyObject *, _PyCrossInterpreterData *); +static int _queueobj_shared(PyThreadState *, PyObject *, _PyXIData_t *); static int set_external_queue_type(module_state *state, PyTypeObject *queue_type) @@ -1339,9 +1336,9 @@ _queueid_xid_free(void *data) } static PyObject * -_queueobj_from_xid(_PyCrossInterpreterData *data) +_queueobj_from_xid(_PyXIData_t *data) { - int64_t qid = *(int64_t *)_PyCrossInterpreterData_DATA(data); + int64_t qid = *(int64_t *)_PyXIData_DATA(data); PyObject *qidobj = PyLong_FromLongLong(qid); if (qidobj == NULL) { return NULL; @@ -1367,8 +1364,7 @@ _queueobj_from_xid(_PyCrossInterpreterData *data) } static int -_queueobj_shared(PyThreadState *tstate, PyObject *queueobj, - _PyCrossInterpreterData *data) +_queueobj_shared(PyThreadState *tstate, PyObject *queueobj, _PyXIData_t *data) { PyObject *qidobj = PyObject_GetAttrString(queueobj, "_id"); if (qidobj == NULL) { @@ -1388,9 +1384,8 @@ _queueobj_shared(PyThreadState *tstate, PyObject *queueobj, if (raw == NULL) { return -1; } - _PyCrossInterpreterData_Init(data, tstate->interp, raw, NULL, - _queueobj_from_xid); - _PyCrossInterpreterData_SET_FREE(data, _queueid_xid_free); + _PyXIData_Init(data, tstate->interp, raw, NULL, _queueobj_from_xid); + _PyXIData_SET_FREE(data, _queueid_xid_free); return 0; } diff --git a/Modules/_interpreters_common.h b/Modules/_interpreters_common.h index 0d2e0c9efd3837..b0e31a33734dab 100644 --- a/Modules/_interpreters_common.h +++ b/Modules/_interpreters_common.h @@ -6,27 +6,27 @@ static int -ensure_xid_class(PyTypeObject *cls, crossinterpdatafunc getdata) +ensure_xid_class(PyTypeObject *cls, xidatafunc getdata) { //assert(cls->tp_flags & Py_TPFLAGS_HEAPTYPE); - return _PyCrossInterpreterData_RegisterClass(cls, getdata); + return _PyXIData_RegisterClass(cls, getdata); } #ifdef REGISTERS_HEAP_TYPES static int clear_xid_class(PyTypeObject *cls) { - return _PyCrossInterpreterData_UnregisterClass(cls); + return _PyXIData_UnregisterClass(cls); } #endif static inline int64_t -_get_interpid(_PyCrossInterpreterData *data) +_get_interpid(_PyXIData_t *data) { int64_t interpid; if (data != NULL) { - interpid = _PyCrossInterpreterData_INTERPID(data); + interpid = _PyXIData_INTERPID(data); assert(!PyErr_Occurred()); } else { diff --git a/Modules/_interpretersmodule.c b/Modules/_interpretersmodule.c index 63f2bb38768511..95acdd69e53260 100644 --- a/Modules/_interpretersmodule.c +++ b/Modules/_interpretersmodule.c @@ -7,7 +7,7 @@ #include "Python.h" #include "pycore_abstract.h" // _PyIndex_Check() -#include "pycore_crossinterp.h" // struct _xid +#include "pycore_crossinterp.h" // _PyXIData_t #include "pycore_interp.h" // _PyInterpreterState_IDIncref() #include "pycore_initconfig.h" // _PyErr_SetFromPyStatus() #include "pycore_modsupport.h" // _PyArg_BadArgument() @@ -84,18 +84,18 @@ typedef struct { } XIBufferViewObject; static PyObject * -xibufferview_from_xid(PyTypeObject *cls, _PyCrossInterpreterData *data) +xibufferview_from_xid(PyTypeObject *cls, _PyXIData_t *data) { - assert(_PyCrossInterpreterData_DATA(data) != NULL); - assert(_PyCrossInterpreterData_OBJ(data) == NULL); - assert(_PyCrossInterpreterData_INTERPID(data) >= 0); + assert(_PyXIData_DATA(data) != NULL); + assert(_PyXIData_OBJ(data) == NULL); + assert(_PyXIData_INTERPID(data) >= 0); XIBufferViewObject *self = PyObject_Malloc(sizeof(XIBufferViewObject)); if (self == NULL) { return NULL; } PyObject_Init((PyObject *)self, cls); - self->view = (Py_buffer *)_PyCrossInterpreterData_DATA(data); - self->interpid = _PyCrossInterpreterData_INTERPID(data); + self->view = (Py_buffer *)_PyXIData_DATA(data); + self->interpid = _PyXIData_INTERPID(data); return (PyObject *)self; } @@ -154,7 +154,7 @@ static PyType_Spec XIBufferViewType_spec = { static PyTypeObject * _get_current_xibufferview_type(void); static PyObject * -_memoryview_from_xid(_PyCrossInterpreterData *data) +_memoryview_from_xid(_PyXIData_t *data) { PyTypeObject *cls = _get_current_xibufferview_type(); if (cls == NULL) { @@ -168,8 +168,7 @@ _memoryview_from_xid(_PyCrossInterpreterData *data) } static int -_memoryview_shared(PyThreadState *tstate, PyObject *obj, - _PyCrossInterpreterData *data) +_memoryview_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *data) { Py_buffer *view = PyMem_RawMalloc(sizeof(Py_buffer)); if (view == NULL) { @@ -179,8 +178,7 @@ _memoryview_shared(PyThreadState *tstate, PyObject *obj, PyMem_RawFree(view); return -1; } - _PyCrossInterpreterData_Init(data, tstate->interp, view, NULL, - _memoryview_from_xid); + _PyXIData_Init(data, tstate->interp, view, NULL, _memoryview_from_xid); return 0; } @@ -1183,7 +1181,7 @@ object_is_shareable(PyObject *self, PyObject *args, PyObject *kwds) return NULL; } - if (_PyObject_CheckCrossInterpreterData(obj) == 0) { + if (_PyObject_CheckXIData(obj) == 0) { Py_RETURN_TRUE; } PyErr_Clear(); diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index 883f32599fbc99..327a077671047c 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -1787,11 +1787,10 @@ interpreter_refcount_linked(PyObject *self, PyObject *idobj) static void _xid_capsule_destructor(PyObject *capsule) { - _PyCrossInterpreterData *data = \ - (_PyCrossInterpreterData *)PyCapsule_GetPointer(capsule, NULL); + _PyXIData_t *data = (_PyXIData_t *)PyCapsule_GetPointer(capsule, NULL); if (data != NULL) { - assert(_PyCrossInterpreterData_Release(data) == 0); - _PyCrossInterpreterData_Free(data); + assert(_PyXIData_Release(data) == 0); + _PyXIData_Free(data); } } @@ -1803,18 +1802,18 @@ get_crossinterp_data(PyObject *self, PyObject *args) return NULL; } - _PyCrossInterpreterData *data = _PyCrossInterpreterData_New(); + _PyXIData_t *data = _PyXIData_New(); if (data == NULL) { return NULL; } - if (_PyObject_GetCrossInterpreterData(obj, data) != 0) { - _PyCrossInterpreterData_Free(data); + if (_PyObject_GetXIData(obj, data) != 0) { + _PyXIData_Free(data); return NULL; } PyObject *capsule = PyCapsule_New(data, NULL, _xid_capsule_destructor); if (capsule == NULL) { - assert(_PyCrossInterpreterData_Release(data) == 0); - _PyCrossInterpreterData_Free(data); + assert(_PyXIData_Release(data) == 0); + _PyXIData_Free(data); } return capsule; } @@ -1827,12 +1826,11 @@ restore_crossinterp_data(PyObject *self, PyObject *args) return NULL; } - _PyCrossInterpreterData *data = \ - (_PyCrossInterpreterData *)PyCapsule_GetPointer(capsule, NULL); + _PyXIData_t *data = (_PyXIData_t *)PyCapsule_GetPointer(capsule, NULL); if (data == NULL) { return NULL; } - return _PyCrossInterpreterData_NewObject(data); + return _PyXIData_NewObject(data); } diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index f840e7fd61f985..95552cade52b75 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -229,6 +229,7 @@ + diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index a930cd0b0b10c6..1708cf6e0b3a52 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -609,6 +609,9 @@ Include\internal + + Include\internal + Include\internal diff --git a/Python/crossinterp.c b/Python/crossinterp.c index 0aca322d987dba..2daba99988c12a 100644 --- a/Python/crossinterp.c +++ b/Python/crossinterp.c @@ -3,11 +3,14 @@ #include "Python.h" #include "pycore_ceval.h" // _Py_simple_func -#include "pycore_crossinterp.h" // struct _xid +#include "pycore_crossinterp.h" // _PyXIData_t #include "pycore_initconfig.h" // _PyStatus_OK() #include "pycore_namespace.h" //_PyNamespace_New() #include "pycore_pyerrors.h" // _PyErr_Clear() -#include "pycore_weakref.h" // _PyWeakref_GET_REF() + + +#define _PyXI_GET_GLOBAL_STATE(interp) (&(interp)->runtime->xi) +#define _PyXI_GET_STATE(interp) (&(interp)->xi) /**************/ @@ -57,25 +60,24 @@ _Py_CallInInterpreterAndRawFree(PyInterpreterState *interp, /* cross-interpreter data */ /**************************/ -/* registry of {type -> crossinterpdatafunc} */ +/* registry of {type -> xidatafunc} */ /* For now we use a global registry of shareable classes. An alternative would be to add a tp_* slot for a class's - crossinterpdatafunc. It would be simpler and more efficient. */ + xidatafunc. It would be simpler and more efficient. */ -static void xid_lookup_init(PyInterpreterState *); -static void xid_lookup_fini(PyInterpreterState *); -static crossinterpdatafunc lookup_getdata(PyInterpreterState *, PyObject *); +static void xid_lookup_init(_PyXIData_lookup_t *); +static void xid_lookup_fini(_PyXIData_lookup_t *); +static xidatafunc lookup_getdata(PyInterpreterState *, PyObject *); #include "crossinterp_data_lookup.h" /* lifecycle */ -_PyCrossInterpreterData * -_PyCrossInterpreterData_New(void) +_PyXIData_t * +_PyXIData_New(void) { - _PyCrossInterpreterData *xid = PyMem_RawMalloc( - sizeof(_PyCrossInterpreterData)); + _PyXIData_t *xid = PyMem_RawMalloc(sizeof(_PyXIData_t)); if (xid == NULL) { PyErr_NoMemory(); } @@ -83,10 +85,10 @@ _PyCrossInterpreterData_New(void) } void -_PyCrossInterpreterData_Free(_PyCrossInterpreterData *xid) +_PyXIData_Free(_PyXIData_t *xid) { PyInterpreterState *interp = PyInterpreterState_Get(); - _PyCrossInterpreterData_Clear(interp, xid); + _PyXIData_Clear(interp, xid); PyMem_RawFree(xid); } @@ -94,20 +96,20 @@ _PyCrossInterpreterData_Free(_PyCrossInterpreterData *xid) /* defining cross-interpreter data */ static inline void -_xidata_init(_PyCrossInterpreterData *data) +_xidata_init(_PyXIData_t *data) { // If the value is being reused // then _xidata_clear() should have been called already. assert(data->data == NULL); assert(data->obj == NULL); - *data = (_PyCrossInterpreterData){0}; - _PyCrossInterpreterData_INTERPID(data) = -1; + *data = (_PyXIData_t){0}; + _PyXIData_INTERPID(data) = -1; } static inline void -_xidata_clear(_PyCrossInterpreterData *data) +_xidata_clear(_PyXIData_t *data) { - // _PyCrossInterpreterData only has two members that need to be + // _PyXIData_t only has two members that need to be // cleaned up, if set: "data" must be freed and "obj" must be decref'ed. // In both cases the original (owning) interpreter must be used, // which is the caller's responsibility to ensure. @@ -121,10 +123,10 @@ _xidata_clear(_PyCrossInterpreterData *data) } void -_PyCrossInterpreterData_Init(_PyCrossInterpreterData *data, - PyInterpreterState *interp, - void *shared, PyObject *obj, - xid_newobjectfunc new_object) +_PyXIData_Init(_PyXIData_t *data, + PyInterpreterState *interp, + void *shared, PyObject *obj, + xid_newobjectfunc new_object) { assert(data != NULL); assert(new_object != NULL); @@ -132,29 +134,29 @@ _PyCrossInterpreterData_Init(_PyCrossInterpreterData *data, data->data = shared; if (obj != NULL) { assert(interp != NULL); - // released in _PyCrossInterpreterData_Clear() + // released in _PyXIData_Clear() data->obj = Py_NewRef(obj); } // Ideally every object would know its owning interpreter. // Until then, we have to rely on the caller to identify it // (but we don't need it in all cases). - _PyCrossInterpreterData_INTERPID(data) = (interp != NULL) + _PyXIData_INTERPID(data) = (interp != NULL) ? PyInterpreterState_GetID(interp) : -1; data->new_object = new_object; } int -_PyCrossInterpreterData_InitWithSize(_PyCrossInterpreterData *data, - PyInterpreterState *interp, - const size_t size, PyObject *obj, - xid_newobjectfunc new_object) +_PyXIData_InitWithSize(_PyXIData_t *data, + PyInterpreterState *interp, + const size_t size, PyObject *obj, + xid_newobjectfunc new_object) { assert(size > 0); // For now we always free the shared data in the same interpreter // where it was allocated, so the interpreter is required. assert(interp != NULL); - _PyCrossInterpreterData_Init(data, interp, NULL, obj, new_object); + _PyXIData_Init(data, interp, NULL, obj, new_object); data->data = PyMem_RawMalloc(size); if (data->data == NULL) { return -1; @@ -164,14 +166,13 @@ _PyCrossInterpreterData_InitWithSize(_PyCrossInterpreterData *data, } void -_PyCrossInterpreterData_Clear(PyInterpreterState *interp, - _PyCrossInterpreterData *data) +_PyXIData_Clear(PyInterpreterState *interp, _PyXIData_t *data) { assert(data != NULL); // This must be called in the owning interpreter. assert(interp == NULL - || _PyCrossInterpreterData_INTERPID(data) == -1 - || _PyCrossInterpreterData_INTERPID(data) == PyInterpreterState_GetID(interp)); + || _PyXIData_INTERPID(data) == -1 + || _PyXIData_INTERPID(data) == PyInterpreterState_GetID(interp)); _xidata_clear(data); } @@ -179,13 +180,13 @@ _PyCrossInterpreterData_Clear(PyInterpreterState *interp, /* using cross-interpreter data */ static int -_check_xidata(PyThreadState *tstate, _PyCrossInterpreterData *data) +_check_xidata(PyThreadState *tstate, _PyXIData_t *data) { // data->data can be anything, including NULL, so we don't check it. // data->obj may be NULL, so we don't check it. - if (_PyCrossInterpreterData_INTERPID(data) < 0) { + if (_PyXIData_INTERPID(data) < 0) { PyErr_SetString(PyExc_SystemError, "missing interp"); return -1; } @@ -221,10 +222,10 @@ _set_xid_lookup_failure(PyInterpreterState *interp, } int -_PyObject_CheckCrossInterpreterData(PyObject *obj) +_PyObject_CheckXIData(PyObject *obj) { PyInterpreterState *interp = PyInterpreterState_Get(); - crossinterpdatafunc getdata = lookup_getdata(interp, obj); + xidatafunc getdata = lookup_getdata(interp, obj); if (getdata == NULL) { if (!PyErr_Occurred()) { _set_xid_lookup_failure(interp, obj, NULL); @@ -235,18 +236,18 @@ _PyObject_CheckCrossInterpreterData(PyObject *obj) } int -_PyObject_GetCrossInterpreterData(PyObject *obj, _PyCrossInterpreterData *data) +_PyObject_GetXIData(PyObject *obj, _PyXIData_t *data) { PyThreadState *tstate = PyThreadState_Get(); PyInterpreterState *interp = tstate->interp; // Reset data before re-populating. - *data = (_PyCrossInterpreterData){0}; - _PyCrossInterpreterData_INTERPID(data) = -1; + *data = (_PyXIData_t){0}; + _PyXIData_INTERPID(data) = -1; // Call the "getdata" func for the object. Py_INCREF(obj); - crossinterpdatafunc getdata = lookup_getdata(interp, obj); + xidatafunc getdata = lookup_getdata(interp, obj); if (getdata == NULL) { Py_DECREF(obj); if (!PyErr_Occurred()) { @@ -261,9 +262,9 @@ _PyObject_GetCrossInterpreterData(PyObject *obj, _PyCrossInterpreterData *data) } // Fill in the blanks and validate the result. - _PyCrossInterpreterData_INTERPID(data) = PyInterpreterState_GetID(interp); + _PyXIData_INTERPID(data) = PyInterpreterState_GetID(interp); if (_check_xidata(tstate, data) != 0) { - (void)_PyCrossInterpreterData_Release(data); + (void)_PyXIData_Release(data); return -1; } @@ -271,7 +272,7 @@ _PyObject_GetCrossInterpreterData(PyObject *obj, _PyCrossInterpreterData *data) } PyObject * -_PyCrossInterpreterData_NewObject(_PyCrossInterpreterData *data) +_PyXIData_NewObject(_PyXIData_t *data) { return data->new_object(data); } @@ -279,12 +280,12 @@ _PyCrossInterpreterData_NewObject(_PyCrossInterpreterData *data) static int _call_clear_xidata(void *data) { - _xidata_clear((_PyCrossInterpreterData *)data); + _xidata_clear((_PyXIData_t *)data); return 0; } static int -_xidata_release(_PyCrossInterpreterData *data, int rawfree) +_xidata_release(_PyXIData_t *data, int rawfree) { if ((data->data == NULL || data->free == NULL) && data->obj == NULL) { // Nothing to release! @@ -299,7 +300,7 @@ _xidata_release(_PyCrossInterpreterData *data, int rawfree) // Switch to the original interpreter. PyInterpreterState *interp = _PyInterpreterState_LookUpID( - _PyCrossInterpreterData_INTERPID(data)); + _PyXIData_INTERPID(data)); if (interp == NULL) { // The interpreter was already destroyed. // This function shouldn't have been called. @@ -321,13 +322,13 @@ _xidata_release(_PyCrossInterpreterData *data, int rawfree) } int -_PyCrossInterpreterData_Release(_PyCrossInterpreterData *data) +_PyXIData_Release(_PyXIData_t *data) { return _xidata_release(data, 0); } int -_PyCrossInterpreterData_ReleaseAndRawFree(_PyCrossInterpreterData *data) +_PyXIData_ReleaseAndRawFree(_PyXIData_t *data) { return _xidata_release(data, 1); } @@ -446,15 +447,15 @@ _format_TracebackException(PyObject *tbexc) static int -_release_xid_data(_PyCrossInterpreterData *data, int rawfree) +_release_xid_data(_PyXIData_t *data, int rawfree) { PyObject *exc = PyErr_GetRaisedException(); int res = rawfree - ? _PyCrossInterpreterData_Release(data) - : _PyCrossInterpreterData_ReleaseAndRawFree(data); + ? _PyXIData_Release(data) + : _PyXIData_ReleaseAndRawFree(data); if (res < 0) { /* The owning interpreter is already destroyed. */ - _PyCrossInterpreterData_Clear(NULL, data); + _PyXIData_Clear(NULL, data); // XXX Emit a warning? PyErr_Clear(); } @@ -1094,8 +1095,8 @@ _PyXI_ApplyError(_PyXI_error *error) typedef struct _sharednsitem { const char *name; - _PyCrossInterpreterData *data; - // We could have a "PyCrossInterpreterData _data" field, so it would + _PyXIData_t *data; + // We could have a "PyXIData _data" field, so it would // be allocated as part of the item and avoid an extra allocation. // However, doing so adds a bunch of complexity because we must // ensure the item isn't freed before a pending call might happen @@ -1131,7 +1132,7 @@ _sharednsitem_has_value(_PyXI_namespace_item *item, int64_t *p_interpid) return 0; } if (p_interpid != NULL) { - *p_interpid = _PyCrossInterpreterData_INTERPID(item->data); + *p_interpid = _PyXIData_INTERPID(item->data); } return 1; } @@ -1141,12 +1142,12 @@ _sharednsitem_set_value(_PyXI_namespace_item *item, PyObject *value) { assert(_sharednsitem_is_initialized(item)); assert(item->data == NULL); - item->data = PyMem_RawMalloc(sizeof(_PyCrossInterpreterData)); + item->data = PyMem_RawMalloc(sizeof(_PyXIData_t)); if (item->data == NULL) { PyErr_NoMemory(); return -1; } - if (_PyObject_GetCrossInterpreterData(value, item->data) != 0) { + if (_PyObject_GetXIData(value, item->data) != 0) { PyMem_RawFree(item->data); item->data = NULL; // The caller may want to propagate PyExc_NotShareableError @@ -1159,7 +1160,7 @@ _sharednsitem_set_value(_PyXI_namespace_item *item, PyObject *value) static void _sharednsitem_clear_value(_PyXI_namespace_item *item) { - _PyCrossInterpreterData *data = item->data; + _PyXIData_t *data = item->data; if (data != NULL) { item->data = NULL; int rawfree = 1; @@ -1205,7 +1206,7 @@ _sharednsitem_apply(_PyXI_namespace_item *item, PyObject *ns, PyObject *dflt) } PyObject *value; if (item->data != NULL) { - value = _PyCrossInterpreterData_NewObject(item->data); + value = _PyXIData_NewObject(item->data); if (value == NULL) { Py_DECREF(name); return -1; @@ -1776,7 +1777,10 @@ PyStatus _PyXI_Init(PyInterpreterState *interp) { // Initialize the XID lookup state (e.g. registry). - xid_lookup_init(interp); + if (_Py_IsMainInterpreter(interp)) { + xid_lookup_init(&_PyXI_GET_GLOBAL_STATE(interp)->data_lookup); + } + xid_lookup_init(&_PyXI_GET_STATE(interp)->data_lookup); // Initialize exceptions (heap types). if (_init_not_shareable_error_type(interp) < 0) { @@ -1796,7 +1800,10 @@ _PyXI_Fini(PyInterpreterState *interp) _fini_not_shareable_error_type(interp); // Finalize the XID lookup state (e.g. registry). - xid_lookup_fini(interp); + xid_lookup_fini(&_PyXI_GET_STATE(interp)->data_lookup); + if (_Py_IsMainInterpreter(interp)) { + xid_lookup_fini(&_PyXI_GET_GLOBAL_STATE(interp)->data_lookup); + } } PyStatus diff --git a/Python/crossinterp_data_lookup.h b/Python/crossinterp_data_lookup.h index 863919ad42fb97..88c662a3df00d6 100644 --- a/Python/crossinterp_data_lookup.h +++ b/Python/crossinterp_data_lookup.h @@ -1,8 +1,31 @@ +#include "pycore_weakref.h" // _PyWeakref_GET_REF() -static crossinterpdatafunc _lookup_getdata_from_registry( - PyInterpreterState *, PyObject *); -static crossinterpdatafunc +typedef struct _xidregistry dlregistry_t; +typedef struct _xidregitem dlregitem_t; + + +// forward +static void _xidregistry_init(dlregistry_t *); +static void _xidregistry_fini(dlregistry_t *); +static xidatafunc _lookup_getdata_from_registry(PyInterpreterState *, PyObject *); + + +/* used in crossinterp.c */ + +static void +xid_lookup_init(_PyXIData_lookup_t *state) +{ + _xidregistry_init(&state->registry); +} + +static void +xid_lookup_fini(_PyXIData_lookup_t *state) +{ + _xidregistry_fini(&state->registry); +} + +static xidatafunc lookup_getdata(PyInterpreterState *interp, PyObject *obj) { /* Cross-interpreter objects are looked up by exact match on the class. @@ -11,8 +34,11 @@ lookup_getdata(PyInterpreterState *interp, PyObject *obj) return _lookup_getdata_from_registry(interp, obj); } -crossinterpdatafunc -_PyCrossInterpreterData_Lookup(PyObject *obj) + +/* exported API */ + +xidatafunc +_PyXIData_Lookup(PyObject *obj) { PyInterpreterState *interp = PyInterpreterState_Get(); return lookup_getdata(interp, obj); @@ -20,20 +46,20 @@ _PyCrossInterpreterData_Lookup(PyObject *obj) /***********************************************/ -/* a registry of {type -> crossinterpdatafunc} */ +/* a registry of {type -> xidatafunc} */ /***********************************************/ /* For now we use a global registry of shareable classes. An alternative would be to add a tp_* slot for a class's - crossinterpdatafunc. It would be simpler and more efficient. */ + xidatafunc. It would be simpler and more efficient. */ /* registry lifecycle */ -static void _register_builtins_for_crossinterpreter_data(struct _xidregistry *); +static void _register_builtins_for_crossinterpreter_data(dlregistry_t *); static void -_xidregistry_init(struct _xidregistry *registry) +_xidregistry_init(dlregistry_t *registry) { if (registry->initialized) { return; @@ -47,10 +73,10 @@ _xidregistry_init(struct _xidregistry *registry) } } -static void _xidregistry_clear(struct _xidregistry *); +static void _xidregistry_clear(dlregistry_t *); static void -_xidregistry_fini(struct _xidregistry *registry) +_xidregistry_fini(dlregistry_t *registry) { if (!registry->initialized) { return; @@ -60,32 +86,11 @@ _xidregistry_fini(struct _xidregistry *registry) _xidregistry_clear(registry); } -static inline struct _xidregistry * _get_global_xidregistry(_PyRuntimeState *); -static inline struct _xidregistry * _get_xidregistry(PyInterpreterState *); - -static void -xid_lookup_init(PyInterpreterState *interp) -{ - if (_Py_IsMainInterpreter(interp)) { - _xidregistry_init(_get_global_xidregistry(interp->runtime)); - } - _xidregistry_init(_get_xidregistry(interp)); -} - -static void -xid_lookup_fini(PyInterpreterState *interp) -{ - _xidregistry_fini(_get_xidregistry(interp)); - if (_Py_IsMainInterpreter(interp)) { - _xidregistry_fini(_get_global_xidregistry(interp->runtime)); - } -} - /* registry thread safety */ static void -_xidregistry_lock(struct _xidregistry *registry) +_xidregistry_lock(dlregistry_t *registry) { if (registry->global) { PyMutex_Lock(®istry->mutex); @@ -94,7 +99,7 @@ _xidregistry_lock(struct _xidregistry *registry) } static void -_xidregistry_unlock(struct _xidregistry *registry) +_xidregistry_unlock(dlregistry_t *registry) { if (registry->global) { PyMutex_Unlock(®istry->mutex); @@ -104,35 +109,34 @@ _xidregistry_unlock(struct _xidregistry *registry) /* accessing the registry */ -static inline struct _xidregistry * +static inline dlregistry_t * _get_global_xidregistry(_PyRuntimeState *runtime) { - return &runtime->xi.registry; + return &runtime->xi.data_lookup.registry; } -static inline struct _xidregistry * +static inline dlregistry_t * _get_xidregistry(PyInterpreterState *interp) { - return &interp->xi.registry; + return &interp->xi.data_lookup.registry; } -static inline struct _xidregistry * +static inline dlregistry_t * _get_xidregistry_for_type(PyInterpreterState *interp, PyTypeObject *cls) { - struct _xidregistry *registry = _get_global_xidregistry(interp->runtime); + dlregistry_t *registry = _get_global_xidregistry(interp->runtime); if (cls->tp_flags & Py_TPFLAGS_HEAPTYPE) { registry = _get_xidregistry(interp); } return registry; } -static struct _xidregitem * _xidregistry_remove_entry( - struct _xidregistry *, struct _xidregitem *); +static dlregitem_t* _xidregistry_remove_entry(dlregistry_t *, dlregitem_t *); -static struct _xidregitem * -_xidregistry_find_type(struct _xidregistry *xidregistry, PyTypeObject *cls) +static dlregitem_t * +_xidregistry_find_type(dlregistry_t *xidregistry, PyTypeObject *cls) { - struct _xidregitem *cur = xidregistry->head; + dlregitem_t *cur = xidregistry->head; while (cur != NULL) { if (cur->weakref != NULL) { // cur is/was a heap type. @@ -155,16 +159,16 @@ _xidregistry_find_type(struct _xidregistry *xidregistry, PyTypeObject *cls) return NULL; } -static crossinterpdatafunc +static xidatafunc _lookup_getdata_from_registry(PyInterpreterState *interp, PyObject *obj) { PyTypeObject *cls = Py_TYPE(obj); - struct _xidregistry *xidregistry = _get_xidregistry_for_type(interp, cls); + dlregistry_t *xidregistry = _get_xidregistry_for_type(interp, cls); _xidregistry_lock(xidregistry); - struct _xidregitem *matched = _xidregistry_find_type(xidregistry, cls); - crossinterpdatafunc func = matched != NULL ? matched->getdata : NULL; + dlregitem_t *matched = _xidregistry_find_type(xidregistry, cls); + xidatafunc func = matched != NULL ? matched->getdata : NULL; _xidregistry_unlock(xidregistry); return func; @@ -174,14 +178,14 @@ _lookup_getdata_from_registry(PyInterpreterState *interp, PyObject *obj) /* updating the registry */ static int -_xidregistry_add_type(struct _xidregistry *xidregistry, - PyTypeObject *cls, crossinterpdatafunc getdata) +_xidregistry_add_type(dlregistry_t *xidregistry, + PyTypeObject *cls, xidatafunc getdata) { - struct _xidregitem *newhead = PyMem_RawMalloc(sizeof(struct _xidregitem)); + dlregitem_t *newhead = PyMem_RawMalloc(sizeof(dlregitem_t)); if (newhead == NULL) { return -1; } - *newhead = (struct _xidregitem){ + *newhead = (dlregitem_t){ // We do not keep a reference, to avoid keeping the class alive. .cls = cls, .refcount = 1, @@ -203,11 +207,10 @@ _xidregistry_add_type(struct _xidregistry *xidregistry, return 0; } -static struct _xidregitem * -_xidregistry_remove_entry(struct _xidregistry *xidregistry, - struct _xidregitem *entry) +static dlregitem_t * +_xidregistry_remove_entry(dlregistry_t *xidregistry, dlregitem_t *entry) { - struct _xidregitem *next = entry->next; + dlregitem_t *next = entry->next; if (entry->prev != NULL) { assert(entry->prev->next == entry); entry->prev->next = next; @@ -225,12 +228,12 @@ _xidregistry_remove_entry(struct _xidregistry *xidregistry, } static void -_xidregistry_clear(struct _xidregistry *xidregistry) +_xidregistry_clear(dlregistry_t *xidregistry) { - struct _xidregitem *cur = xidregistry->head; + dlregitem_t *cur = xidregistry->head; xidregistry->head = NULL; while (cur != NULL) { - struct _xidregitem *next = cur->next; + dlregitem_t *next = cur->next; Py_XDECREF(cur->weakref); PyMem_RawFree(cur); cur = next; @@ -238,8 +241,7 @@ _xidregistry_clear(struct _xidregistry *xidregistry) } int -_PyCrossInterpreterData_RegisterClass(PyTypeObject *cls, - crossinterpdatafunc getdata) +_PyXIData_RegisterClass(PyTypeObject *cls, xidatafunc getdata) { if (!PyType_Check(cls)) { PyErr_Format(PyExc_ValueError, "only classes may be registered"); @@ -252,10 +254,10 @@ _PyCrossInterpreterData_RegisterClass(PyTypeObject *cls, int res = 0; PyInterpreterState *interp = _PyInterpreterState_GET(); - struct _xidregistry *xidregistry = _get_xidregistry_for_type(interp, cls); + dlregistry_t *xidregistry = _get_xidregistry_for_type(interp, cls); _xidregistry_lock(xidregistry); - struct _xidregitem *matched = _xidregistry_find_type(xidregistry, cls); + dlregitem_t *matched = _xidregistry_find_type(xidregistry, cls); if (matched != NULL) { assert(matched->getdata == getdata); matched->refcount += 1; @@ -270,14 +272,14 @@ _PyCrossInterpreterData_RegisterClass(PyTypeObject *cls, } int -_PyCrossInterpreterData_UnregisterClass(PyTypeObject *cls) +_PyXIData_UnregisterClass(PyTypeObject *cls) { int res = 0; PyInterpreterState *interp = _PyInterpreterState_GET(); - struct _xidregistry *xidregistry = _get_xidregistry_for_type(interp, cls); + dlregistry_t *xidregistry = _get_xidregistry_for_type(interp, cls); _xidregistry_lock(xidregistry); - struct _xidregitem *matched = _xidregistry_find_type(xidregistry, cls); + dlregitem_t *matched = _xidregistry_find_type(xidregistry, cls); if (matched != NULL) { assert(matched->refcount > 0); matched->refcount -= 1; @@ -304,17 +306,16 @@ struct _shared_bytes_data { }; static PyObject * -_new_bytes_object(_PyCrossInterpreterData *data) +_new_bytes_object(_PyXIData_t *data) { struct _shared_bytes_data *shared = (struct _shared_bytes_data *)(data->data); return PyBytes_FromStringAndSize(shared->bytes, shared->len); } static int -_bytes_shared(PyThreadState *tstate, PyObject *obj, - _PyCrossInterpreterData *data) +_bytes_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *data) { - if (_PyCrossInterpreterData_InitWithSize( + if (_PyXIData_InitWithSize( data, tstate->interp, sizeof(struct _shared_bytes_data), obj, _new_bytes_object ) < 0) @@ -323,7 +324,7 @@ _bytes_shared(PyThreadState *tstate, PyObject *obj, } struct _shared_bytes_data *shared = (struct _shared_bytes_data *)data->data; if (PyBytes_AsStringAndSize(obj, &shared->bytes, &shared->len) < 0) { - _PyCrossInterpreterData_Clear(tstate->interp, data); + _PyXIData_Clear(tstate->interp, data); return -1; } return 0; @@ -338,17 +339,16 @@ struct _shared_str_data { }; static PyObject * -_new_str_object(_PyCrossInterpreterData *data) +_new_str_object(_PyXIData_t *data) { struct _shared_str_data *shared = (struct _shared_str_data *)(data->data); return PyUnicode_FromKindAndData(shared->kind, shared->buffer, shared->len); } static int -_str_shared(PyThreadState *tstate, PyObject *obj, - _PyCrossInterpreterData *data) +_str_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *data) { - if (_PyCrossInterpreterData_InitWithSize( + if (_PyXIData_InitWithSize( data, tstate->interp, sizeof(struct _shared_str_data), obj, _new_str_object ) < 0) @@ -365,14 +365,13 @@ _str_shared(PyThreadState *tstate, PyObject *obj, // int static PyObject * -_new_long_object(_PyCrossInterpreterData *data) +_new_long_object(_PyXIData_t *data) { return PyLong_FromSsize_t((Py_ssize_t)(data->data)); } static int -_long_shared(PyThreadState *tstate, PyObject *obj, - _PyCrossInterpreterData *data) +_long_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *data) { /* Note that this means the size of shareable ints is bounded by * sys.maxsize. Hence on 32-bit architectures that is half the @@ -385,8 +384,7 @@ _long_shared(PyThreadState *tstate, PyObject *obj, } return -1; } - _PyCrossInterpreterData_Init(data, tstate->interp, (void *)value, NULL, - _new_long_object); + _PyXIData_Init(data, tstate->interp, (void *)value, NULL, _new_long_object); // data->obj and data->free remain NULL return 0; } @@ -394,17 +392,16 @@ _long_shared(PyThreadState *tstate, PyObject *obj, // float static PyObject * -_new_float_object(_PyCrossInterpreterData *data) +_new_float_object(_PyXIData_t *data) { double * value_ptr = data->data; return PyFloat_FromDouble(*value_ptr); } static int -_float_shared(PyThreadState *tstate, PyObject *obj, - _PyCrossInterpreterData *data) +_float_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *data) { - if (_PyCrossInterpreterData_InitWithSize( + if (_PyXIData_InitWithSize( data, tstate->interp, sizeof(double), NULL, _new_float_object ) < 0) @@ -419,18 +416,16 @@ _float_shared(PyThreadState *tstate, PyObject *obj, // None static PyObject * -_new_none_object(_PyCrossInterpreterData *data) +_new_none_object(_PyXIData_t *data) { // XXX Singleton refcounts are problematic across interpreters... return Py_NewRef(Py_None); } static int -_none_shared(PyThreadState *tstate, PyObject *obj, - _PyCrossInterpreterData *data) +_none_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *data) { - _PyCrossInterpreterData_Init(data, tstate->interp, NULL, NULL, - _new_none_object); + _PyXIData_Init(data, tstate->interp, NULL, NULL, _new_none_object); // data->data, data->obj and data->free remain NULL return 0; } @@ -438,7 +433,7 @@ _none_shared(PyThreadState *tstate, PyObject *obj, // bool static PyObject * -_new_bool_object(_PyCrossInterpreterData *data) +_new_bool_object(_PyXIData_t *data) { if (data->data){ Py_RETURN_TRUE; @@ -447,10 +442,9 @@ _new_bool_object(_PyCrossInterpreterData *data) } static int -_bool_shared(PyThreadState *tstate, PyObject *obj, - _PyCrossInterpreterData *data) +_bool_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *data) { - _PyCrossInterpreterData_Init(data, tstate->interp, + _PyXIData_Init(data, tstate->interp, (void *) (Py_IsTrue(obj) ? (uintptr_t) 1 : (uintptr_t) 0), NULL, _new_bool_object); // data->obj and data->free remain NULL @@ -461,11 +455,11 @@ _bool_shared(PyThreadState *tstate, PyObject *obj, struct _shared_tuple_data { Py_ssize_t len; - _PyCrossInterpreterData **data; + _PyXIData_t **data; }; static PyObject * -_new_tuple_object(_PyCrossInterpreterData *data) +_new_tuple_object(_PyXIData_t *data) { struct _shared_tuple_data *shared = (struct _shared_tuple_data *)(data->data); PyObject *tuple = PyTuple_New(shared->len); @@ -474,7 +468,7 @@ _new_tuple_object(_PyCrossInterpreterData *data) } for (Py_ssize_t i = 0; i < shared->len; i++) { - PyObject *item = _PyCrossInterpreterData_NewObject(shared->data[i]); + PyObject *item = _PyXIData_NewObject(shared->data[i]); if (item == NULL){ Py_DECREF(tuple); return NULL; @@ -493,8 +487,8 @@ _tuple_shared_free(void* data) #endif for (Py_ssize_t i = 0; i < shared->len; i++) { if (shared->data[i] != NULL) { - assert(_PyCrossInterpreterData_INTERPID(shared->data[i]) == interpid); - _PyCrossInterpreterData_Release(shared->data[i]); + assert(_PyXIData_INTERPID(shared->data[i]) == interpid); + _PyXIData_Release(shared->data[i]); PyMem_RawFree(shared->data[i]); shared->data[i] = NULL; } @@ -504,8 +498,7 @@ _tuple_shared_free(void* data) } static int -_tuple_shared(PyThreadState *tstate, PyObject *obj, - _PyCrossInterpreterData *data) +_tuple_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *data) { Py_ssize_t len = PyTuple_GET_SIZE(obj); if (len < 0) { @@ -518,14 +511,14 @@ _tuple_shared(PyThreadState *tstate, PyObject *obj, } shared->len = len; - shared->data = (_PyCrossInterpreterData **) PyMem_Calloc(shared->len, sizeof(_PyCrossInterpreterData *)); + shared->data = (_PyXIData_t **) PyMem_Calloc(shared->len, sizeof(_PyXIData_t *)); if (shared->data == NULL) { PyErr_NoMemory(); return -1; } for (Py_ssize_t i = 0; i < shared->len; i++) { - _PyCrossInterpreterData *data = _PyCrossInterpreterData_New(); + _PyXIData_t *data = _PyXIData_New(); if (data == NULL) { goto error; // PyErr_NoMemory already set } @@ -533,7 +526,7 @@ _tuple_shared(PyThreadState *tstate, PyObject *obj, int res = -1; if (!_Py_EnterRecursiveCallTstate(tstate, " while sharing a tuple")) { - res = _PyObject_GetCrossInterpreterData(item, data); + res = _PyObject_GetXIData(item, data); _Py_LeaveRecursiveCallTstate(tstate); } if (res < 0) { @@ -542,8 +535,7 @@ _tuple_shared(PyThreadState *tstate, PyObject *obj, } shared->data[i] = data; } - _PyCrossInterpreterData_Init( - data, tstate->interp, shared, obj, _new_tuple_object); + _PyXIData_Init(data, tstate->interp, shared, obj, _new_tuple_object); data->free = _tuple_shared_free; return 0; @@ -555,7 +547,7 @@ _tuple_shared(PyThreadState *tstate, PyObject *obj, // registration static void -_register_builtins_for_crossinterpreter_data(struct _xidregistry *xidregistry) +_register_builtins_for_crossinterpreter_data(dlregistry_t *xidregistry) { // None if (_xidregistry_add_type(xidregistry, (PyTypeObject *)PyObject_Type(Py_None), _none_shared) != 0) { diff --git a/Python/pystate.c b/Python/pystate.c index ded5fde9c4bb51..24ee73c145cbcc 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -396,7 +396,7 @@ _Py_COMP_DIAG_POP #define LOCKS_INIT(runtime) \ { \ &(runtime)->interpreters.mutex, \ - &(runtime)->xi.registry.mutex, \ + &(runtime)->xi.data_lookup.registry.mutex, \ &(runtime)->unicode_state.ids.mutex, \ &(runtime)->imports.extensions.mutex, \ &(runtime)->ceval.pending_mainthread.mutex, \ diff --git a/Tools/c-analyzer/cpython/_parser.py b/Tools/c-analyzer/cpython/_parser.py index 3a73f65f8ff7b3..21be53e78841d5 100644 --- a/Tools/c-analyzer/cpython/_parser.py +++ b/Tools/c-analyzer/cpython/_parser.py @@ -290,6 +290,7 @@ def clean_lines(text): Modules/_sre/sre_lib.h LOCAL(type) static inline type Modules/_sre/sre_lib.h SRE(F) sre_ucs2_##F Objects/stringlib/codecs.h STRINGLIB_IS_UNICODE 1 +Include/internal/pycore_crossinterp_data_registry.h Py_CORE_CROSSINTERP_DATA_REGISTRY_H 1 # @end=tsv@ ''')[1:] From a38e82bd8c249c126ab033c078170b6dea27a619 Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Thu, 7 Nov 2024 11:39:23 -0500 Subject: [PATCH 096/219] gh-126298: Don't deduplicate slice constants based on equality (#126398) * gh-126298: Don't deduplicated slice constants based on equality * NULL check for PySlice_New * Fix refcounting * Fix refcounting some more * Fix refcounting * Make tests more complete * Fix tests --- Lib/test/test_compile.py | 78 ++++++++++++++++++++++++++++++---------- Objects/codeobject.c | 35 +++++++++++++++++- 2 files changed, 93 insertions(+), 20 deletions(-) diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index 85ae71c1f77b28..519a1207afb1fc 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -2,6 +2,7 @@ import dis import io import itertools +import marshal import math import opcode import os @@ -1385,52 +1386,91 @@ def check_op_count(func, op, expected): self.assertEqual(actual, expected) def check_consts(func, typ, expected): - slice_consts = 0 + expected = set([repr(x) for x in expected]) + all_consts = set() consts = func.__code__.co_consts for instr in dis.Bytecode(func): if instr.opname == "LOAD_CONST" and isinstance(consts[instr.oparg], typ): - slice_consts += 1 - self.assertEqual(slice_consts, expected) + all_consts.add(repr(consts[instr.oparg])) + self.assertEqual(all_consts, expected) def load(): return x[a:b] + x [a:] + x[:b] + x[:] + check_op_count(load, "BINARY_SLICE", 3) + check_op_count(load, "BUILD_SLICE", 0) + check_consts(load, slice, [slice(None, None, None)]) + check_op_count(load, "BINARY_SUBSCR", 1) + def store(): x[a:b] = y x [a:] = y x[:b] = y x[:] = y + check_op_count(store, "STORE_SLICE", 3) + check_op_count(store, "BUILD_SLICE", 0) + check_op_count(store, "STORE_SUBSCR", 1) + check_consts(store, slice, [slice(None, None, None)]) + def long_slice(): return x[a:b:c] + check_op_count(long_slice, "BUILD_SLICE", 1) + check_op_count(long_slice, "BINARY_SLICE", 0) + check_consts(long_slice, slice, []) + check_op_count(long_slice, "BINARY_SUBSCR", 1) + def aug(): x[a:b] += y + check_op_count(aug, "BINARY_SLICE", 1) + check_op_count(aug, "STORE_SLICE", 1) + check_op_count(aug, "BUILD_SLICE", 0) + check_op_count(aug, "BINARY_SUBSCR", 0) + check_op_count(aug, "STORE_SUBSCR", 0) + check_consts(aug, slice, []) + def aug_const(): x[1:2] += y + check_op_count(aug_const, "BINARY_SLICE", 0) + check_op_count(aug_const, "STORE_SLICE", 0) + check_op_count(aug_const, "BINARY_SUBSCR", 1) + check_op_count(aug_const, "STORE_SUBSCR", 1) + check_consts(aug_const, slice, [slice(1, 2)]) + def compound_const_slice(): x[1:2:3, 4:5:6] = y - check_op_count(load, "BINARY_SLICE", 3) - check_op_count(load, "BUILD_SLICE", 0) - check_consts(load, slice, 1) - check_op_count(store, "STORE_SLICE", 3) - check_op_count(store, "BUILD_SLICE", 0) - check_consts(store, slice, 1) - check_op_count(long_slice, "BUILD_SLICE", 1) - check_op_count(long_slice, "BINARY_SLICE", 0) - check_op_count(aug, "BINARY_SLICE", 1) - check_op_count(aug, "STORE_SLICE", 1) - check_op_count(aug, "BUILD_SLICE", 0) - check_op_count(aug_const, "BINARY_SLICE", 0) - check_op_count(aug_const, "STORE_SLICE", 0) - check_consts(aug_const, slice, 1) check_op_count(compound_const_slice, "BINARY_SLICE", 0) check_op_count(compound_const_slice, "BUILD_SLICE", 0) - check_consts(compound_const_slice, slice, 0) - check_consts(compound_const_slice, tuple, 1) + check_op_count(compound_const_slice, "STORE_SLICE", 0) + check_op_count(compound_const_slice, "STORE_SUBSCR", 1) + check_consts(compound_const_slice, slice, []) + check_consts(compound_const_slice, tuple, [(slice(1, 2, 3), slice(4, 5, 6))]) + + def mutable_slice(): + x[[]:] = y + + check_consts(mutable_slice, slice, {}) + + def different_but_equal(): + x[:0] = y + x[:0.0] = y + x[:False] = y + x[:None] = y + + check_consts( + different_but_equal, + slice, + [ + slice(None, 0, None), + slice(None, 0.0, None), + slice(None, False, None), + slice(None, None, None) + ] + ) def test_compare_positions(self): for opname_prefix, op in [ diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 1cf9740af9a209..dba43d5911da95 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -2388,7 +2388,6 @@ _PyCode_ConstantKey(PyObject *op) if (op == Py_None || op == Py_Ellipsis || PyLong_CheckExact(op) || PyUnicode_CheckExact(op) - || PySlice_Check(op) /* code_richcompare() uses _PyCode_ConstantKey() internally */ || PyCode_Check(op)) { @@ -2496,6 +2495,40 @@ _PyCode_ConstantKey(PyObject *op) Py_DECREF(set); return key; } + else if (PySlice_Check(op)) { + PySliceObject *slice = (PySliceObject *)op; + PyObject *start_key = NULL; + PyObject *stop_key = NULL; + PyObject *step_key = NULL; + key = NULL; + + start_key = _PyCode_ConstantKey(slice->start); + if (start_key == NULL) { + goto slice_exit; + } + + stop_key = _PyCode_ConstantKey(slice->stop); + if (stop_key == NULL) { + goto slice_exit; + } + + step_key = _PyCode_ConstantKey(slice->step); + if (step_key == NULL) { + goto slice_exit; + } + + PyObject *slice_key = PySlice_New(start_key, stop_key, step_key); + if (slice_key == NULL) { + goto slice_exit; + } + + key = PyTuple_Pack(2, slice_key, op); + Py_DECREF(slice_key); + slice_exit: + Py_XDECREF(start_key); + Py_XDECREF(stop_key); + Py_XDECREF(step_key); + } else { /* for other types, use the object identifier as a unique identifier * to ensure that they are seen as unequal. */ From 09d6f5dc7824c74672add512619e978844ff8051 Mon Sep 17 00:00:00 2001 From: alm Date: Thu, 7 Nov 2024 20:55:31 +0200 Subject: [PATCH 097/219] GH-126464 Fix JIT CI on aarch64-apple-darwin (GH-126494) --- .github/workflows/jit.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/.github/workflows/jit.yml b/.github/workflows/jit.yml index 897c692118e9a4..35d5d59b762660 100644 --- a/.github/workflows/jit.yml +++ b/.github/workflows/jit.yml @@ -52,7 +52,7 @@ jobs: - x86_64-pc-windows-msvc/msvc - aarch64-pc-windows-msvc/msvc - x86_64-apple-darwin/clang - # - aarch64-apple-darwin/clang + - aarch64-apple-darwin/clang - x86_64-unknown-linux-gnu/gcc - x86_64-unknown-linux-gnu/clang - aarch64-unknown-linux-gnu/gcc @@ -79,11 +79,10 @@ jobs: architecture: x86_64 runner: macos-13 compiler: clang - # GH-126464: A recent change to either GHA or LLVM broke this job: - # - target: aarch64-apple-darwin/clang - # architecture: aarch64 - # runner: macos-14 - # compiler: clang + - target: aarch64-apple-darwin/clang + architecture: aarch64 + runner: macos-14 + compiler: clang - target: x86_64-unknown-linux-gnu/gcc architecture: x86_64 runner: ubuntu-22.04 @@ -132,8 +131,8 @@ jobs: brew update find /usr/local/bin -lname '*/Library/Frameworks/Python.framework/*' -delete brew install llvm@${{ matrix.llvm }} - SDKROOT="$(xcrun --show-sdk-path)" \ - ./configure --enable-experimental-jit ${{ matrix.debug && '--with-pydebug' || '--enable-optimizations --with-lto' }} + export SDKROOT="$(xcrun --show-sdk-path)" + ./configure --enable-experimental-jit ${{ matrix.debug && '--with-pydebug' || '--enable-optimizations --with-lto' }} make all --jobs 4 ./python.exe -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3 From 1f777396f52a4cf7417f56097f10add8042295f4 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 7 Nov 2024 23:40:03 +0200 Subject: [PATCH 098/219] gh-122943: Rework support of var-positional parameter in Argument Clinic (GH-122945) Move creation of a tuple for var-positional parameter out of _PyArg_UnpackKeywordsWithVararg(). Merge _PyArg_UnpackKeywordsWithVararg() with _PyArg_UnpackKeywords(). Add a new parameter in _PyArg_UnpackKeywords(). The "parameters" and "converters" attributes of ParseArgsCodeGen no longer contain the var-positional parameter. It is now available as the "varpos" attribute. Optimize code generation for var-positional parameter and reuse the same generating code for functions with and without keyword parameters. Add special converters for var-positional parameter. "tuple" represents it as a Python tuple and "array" represents it as a continuous array of PyObject*. "object" is a temporary alias of "tuple". --- Include/internal/pycore_modsupport.h | 23 +- Include/internal/pycore_tuple.h | 2 +- Lib/test/clinic.test.c | 206 ++++- Lib/test/test_clinic.py | 105 ++- Modules/_testclinic.c | 332 +++++++- Modules/clinic/_testclinic.c.h | 776 ++++++++++++++++--- Modules/clinic/_testclinic_depr.c.h | 3 +- Modules/clinic/gcmodule.c.h | 33 +- Modules/clinic/mathmodule.c.h | 50 +- Modules/gcmodule.c | 39 +- Modules/mathmodule.c | 43 +- Objects/clinic/setobject.c.h | 103 +-- Objects/clinic/typevarobject.c.h | 31 +- Objects/setobject.c | 74 +- Python/clinic/bltinmodule.c.h | 31 +- Python/getargs.c | 168 +--- Tools/c-analyzer/c_parser/parser/__init__.py | 2 +- Tools/clinic/libclinic/clanguage.py | 16 +- Tools/clinic/libclinic/converter.py | 2 +- Tools/clinic/libclinic/converters.py | 27 + Tools/clinic/libclinic/dsl_parser.py | 9 +- Tools/clinic/libclinic/parse_args.py | 184 +++-- 22 files changed, 1597 insertions(+), 662 deletions(-) diff --git a/Include/internal/pycore_modsupport.h b/Include/internal/pycore_modsupport.h index 11fde814875938..250106ad83a3ec 100644 --- a/Include/internal/pycore_modsupport.h +++ b/Include/internal/pycore_modsupport.h @@ -76,7 +76,7 @@ PyAPI_FUNC(int) _PyArg_ParseStackAndKeywords( ...); // Export for 'math' shared extension -PyAPI_FUNC(PyObject * const *) _PyArg_UnpackKeywords( +PyAPI_FUNC(PyObject * const *) _PyArg_UnpackKeywordsEx( PyObject *const *args, Py_ssize_t nargs, PyObject *kwargs, @@ -85,20 +85,19 @@ PyAPI_FUNC(PyObject * const *) _PyArg_UnpackKeywords( int minpos, int maxpos, int minkw, + int varpos, PyObject **buf); #define _PyArg_UnpackKeywords(args, nargs, kwargs, kwnames, parser, minpos, maxpos, minkw, buf) \ (((minkw) == 0 && (kwargs) == NULL && (kwnames) == NULL && \ - (minpos) <= (nargs) && (nargs) <= (maxpos) && (args) != NULL) ? (args) : \ - _PyArg_UnpackKeywords((args), (nargs), (kwargs), (kwnames), (parser), \ - (minpos), (maxpos), (minkw), (buf))) - -// Export for '_testclinic' shared extension -PyAPI_FUNC(PyObject * const *) _PyArg_UnpackKeywordsWithVararg( - PyObject *const *args, Py_ssize_t nargs, - PyObject *kwargs, PyObject *kwnames, - struct _PyArg_Parser *parser, - int minpos, int maxpos, int minkw, - int vararg, PyObject **buf); + (minpos) <= (nargs) && (nargs) <= (maxpos) && (args) != NULL) ? \ + (args) : \ + _PyArg_UnpackKeywordsEx((args), (nargs), (kwargs), (kwnames), (parser), \ + (minpos), (maxpos), (minkw), 0, (buf))) +#define _PyArg_UnpackKeywordsWithVararg(args, nargs, kwargs, kwnames, parser, minpos, maxpos, minkw, buf) \ + (((minkw) == 0 && (kwargs) == NULL && (kwnames) == NULL && \ + (minpos) <= (nargs) && (args) != NULL) ? (args) : \ + _PyArg_UnpackKeywordsEx((args), (nargs), (kwargs), (kwnames), (parser), \ + (minpos), (maxpos), (minkw), 1, (buf))) #ifdef __cplusplus } diff --git a/Include/internal/pycore_tuple.h b/Include/internal/pycore_tuple.h index dfbbd6fd0c7de5..82b875241f4116 100644 --- a/Include/internal/pycore_tuple.h +++ b/Include/internal/pycore_tuple.h @@ -20,7 +20,7 @@ extern PyStatus _PyTuple_InitGlobalObjects(PyInterpreterState *); #define _PyTuple_ITEMS(op) _Py_RVALUE(_PyTuple_CAST(op)->ob_item) -extern PyObject *_PyTuple_FromArray(PyObject *const *, Py_ssize_t); +PyAPI_FUNC(PyObject *)_PyTuple_FromArray(PyObject *const *, Py_ssize_t); PyAPI_FUNC(PyObject *)_PyTuple_FromStackRefSteal(const union _PyStackRef *, Py_ssize_t); PyAPI_FUNC(PyObject *)_PyTuple_FromArraySteal(PyObject *const *, Py_ssize_t); diff --git a/Lib/test/clinic.test.c b/Lib/test/clinic.test.c index e22324efc490be..80208862f2a09a 100644 --- a/Lib/test/clinic.test.c +++ b/Lib/test/clinic.test.c @@ -4144,6 +4144,45 @@ PyDoc_STRVAR(test_vararg_and_posonly__doc__, "--\n" "\n"); +#define TEST_VARARG_AND_POSONLY_METHODDEF \ + {"test_vararg_and_posonly", _PyCFunction_CAST(test_vararg_and_posonly), METH_FASTCALL, test_vararg_and_posonly__doc__}, + +static PyObject * +test_vararg_and_posonly_impl(PyObject *module, PyObject *a, PyObject *args); + +static PyObject * +test_vararg_and_posonly(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *a; + PyObject *__clinic_args = NULL; + + if (!_PyArg_CheckPositional("test_vararg_and_posonly", nargs, 1, PY_SSIZE_T_MAX)) { + goto exit; + } + a = args[0]; + __clinic_args = _PyTuple_FromArray(args + 1, nargs - 1); + if (__clinic_args == NULL) { + goto exit; + } + return_value = test_vararg_and_posonly_impl(module, a, __clinic_args); + +exit: + /* Cleanup for args */ + Py_XDECREF(__clinic_args); + + return return_value; +} + +static PyObject * +test_vararg_and_posonly_impl(PyObject *module, PyObject *a, PyObject *args) +/*[clinic end generated code: output=0c11c475e240869e input=9cfa748bbff09877]*/ + +PyDoc_STRVAR(test_vararg_and_posonly__doc__, +"test_vararg_and_posonly($module, a, /, *args)\n" +"--\n" +"\n"); + #define TEST_VARARG_AND_POSONLY_METHODDEF \ {"test_vararg_and_posonly", _PyCFunction_CAST(test_vararg_and_posonly), METH_FASTCALL, test_vararg_and_posonly__doc__}, @@ -4163,17 +4202,19 @@ test_vararg_and_posonly(PyObject *module, PyObject *const *args, Py_ssize_t narg goto exit; } a = args[0]; - __clinic_args = args + 1; + __clinic_args = _PyTuple_FromArray(args + 1, nargs - 1); + if (__clinic_args == NULL) { + goto exit; + } return_value = test_vararg_and_posonly_impl(module, a, nvararg, __clinic_args); exit: + /* Cleanup for args */ + Py_XDECREF(__clinic_args); + return return_value; } -static PyObject * -test_vararg_and_posonly_impl(PyObject *module, PyObject *a, Py_ssize_t nargs, - PyObject *const *args) -/*[clinic end generated code: output=dc2dd9483cc0459e input=9cfa748bbff09877]*/ /*[clinic input] test_vararg @@ -4224,26 +4265,34 @@ test_vararg(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[2]; + PyObject *argsbuf[1]; + PyObject * const *fastargs; PyObject *a; PyObject *__clinic_args = NULL; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf); - if (!args) { + fastargs = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!fastargs) { + goto exit; + } + a = fastargs[0]; + __clinic_args = nargs > 1 + ? _PyTuple_FromArray(args + 1, nargs - 1) + : PyTuple_New(0); + if (__clinic_args == NULL) { goto exit; } - a = args[0]; - __clinic_args = args[1]; return_value = test_vararg_impl(module, a, __clinic_args); exit: + /* Cleanup for args */ Py_XDECREF(__clinic_args); + return return_value; } static PyObject * test_vararg_impl(PyObject *module, PyObject *a, PyObject *args) -/*[clinic end generated code: output=1411e464f358a7ba input=81d33815ad1bae6e]*/ +/*[clinic end generated code: output=e7d7da6a7e008125 input=81d33815ad1bae6e]*/ /*[clinic input] test_vararg_with_default @@ -4296,37 +4345,45 @@ test_vararg_with_default(PyObject *module, PyObject *const *args, Py_ssize_t nar .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[3]; + PyObject *argsbuf[2]; + PyObject * const *fastargs; Py_ssize_t noptargs = Py_MIN(nargs, 1) + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; PyObject *a; PyObject *__clinic_args = NULL; int b = 0; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf); - if (!args) { + fastargs = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!fastargs) { goto exit; } - a = args[0]; - __clinic_args = args[1]; + a = fastargs[0]; if (!noptargs) { goto skip_optional_kwonly; } - b = PyObject_IsTrue(args[2]); + b = PyObject_IsTrue(fastargs[1]); if (b < 0) { goto exit; } skip_optional_kwonly: + __clinic_args = nargs > 1 + ? _PyTuple_FromArray(args + 1, nargs - 1) + : PyTuple_New(0); + if (__clinic_args == NULL) { + goto exit; + } return_value = test_vararg_with_default_impl(module, a, __clinic_args, b); exit: + /* Cleanup for args */ Py_XDECREF(__clinic_args); + return return_value; } static PyObject * test_vararg_with_default_impl(PyObject *module, PyObject *a, PyObject *args, int b) -/*[clinic end generated code: output=f09d4b917063ca41 input=6e110b54acd9b22d]*/ +/*[clinic end generated code: output=46781f9920ecedcf input=6e110b54acd9b22d]*/ /*[clinic input] test_vararg_with_only_defaults @@ -4379,22 +4436,22 @@ test_vararg_with_only_defaults(PyObject *module, PyObject *const *args, Py_ssize .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[3]; + PyObject *argsbuf[2]; + PyObject * const *fastargs; Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; PyObject *__clinic_args = NULL; int b = 0; PyObject *c = " "; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, 0, argsbuf); - if (!args) { + fastargs = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, argsbuf); + if (!fastargs) { goto exit; } - __clinic_args = args[0]; if (!noptargs) { goto skip_optional_kwonly; } - if (args[1]) { - b = PyObject_IsTrue(args[1]); + if (fastargs[0]) { + b = PyObject_IsTrue(fastargs[0]); if (b < 0) { goto exit; } @@ -4402,19 +4459,25 @@ test_vararg_with_only_defaults(PyObject *module, PyObject *const *args, Py_ssize goto skip_optional_kwonly; } } - c = args[2]; + c = fastargs[1]; skip_optional_kwonly: + __clinic_args = _PyTuple_FromArray(args, nargs); + if (__clinic_args == NULL) { + goto exit; + } return_value = test_vararg_with_only_defaults_impl(module, __clinic_args, b, c); exit: + /* Cleanup for args */ Py_XDECREF(__clinic_args); + return return_value; } static PyObject * test_vararg_with_only_defaults_impl(PyObject *module, PyObject *args, int b, PyObject *c) -/*[clinic end generated code: output=cc6590b8805d5433 input=fa56a709a035666e]*/ +/*[clinic end generated code: output=d03daf5067039c03 input=fa56a709a035666e]*/ /*[clinic input] test_paramname_module @@ -4927,33 +4990,65 @@ PyDoc_STRVAR(Test___init____doc__, "Varargs init method. For example, nargs is translated to PyTuple_GET_SIZE."); static int -Test___init___impl(TestObj *self, Py_ssize_t nargs, PyObject *const *args); +Test___init___impl(TestObj *self, PyObject *args); static int Test___init__(PyObject *self, PyObject *args, PyObject *kwargs) { int return_value = -1; PyTypeObject *base_tp = TestType; - PyObject *const *__clinic_args = NULL; + PyObject *__clinic_args = NULL; if ((Py_IS_TYPE(self, base_tp) || Py_TYPE(self)->tp_new == base_tp->tp_new) && !_PyArg_NoKeywords("Test", kwargs)) { goto exit; } - if (!_PyArg_CheckPositional("Test", PyTuple_GET_SIZE(args), 0, PY_SSIZE_T_MAX)) { + __clinic_args = Py_NewRef(args); + return_value = Test___init___impl((TestObj *)self, __clinic_args); + +exit: + /* Cleanup for args */ + Py_XDECREF(__clinic_args); + + return return_value; +} + +static int +Test___init___impl(TestObj *self, PyObject *args) +/*[clinic end generated code: output=f172425cec373cd6 input=2a8bd0033c9ac772]*/ + +PyDoc_STRVAR(Test___init____doc__, +"Test(*args)\n" +"--\n" +"\n" +"Varargs init method. For example, nargs is translated to PyTuple_GET_SIZE."); + +static int +Test___init___impl(TestObj *self, Py_ssize_t nargs, PyObject *const *args); + +static int +Test___init__(PyObject *self, PyObject *args, PyObject *kwargs) +{ + int return_value = -1; + PyTypeObject *base_tp = TestType; + PyObject *const *__clinic_args = NULL; + + if ((Py_IS_TYPE(self, base_tp) || + Py_TYPE(self)->tp_new == base_tp->tp_new) && + !_PyArg_NoKeywords("Test", kwargs)) { goto exit; } - __clinic_args = _PyTuple_CAST(args)->ob_item; + __clinic_args = Py_NewRef(args); return_value = Test___init___impl((TestObj *)self, nvararg, __clinic_args); exit: + /* Cleanup for args */ + Py_XDECREF(__clinic_args); + return return_value; } -static int -Test___init___impl(TestObj *self, Py_ssize_t nargs, PyObject *const *args) -/*[clinic end generated code: output=6a64b417c9080a73 input=2a8bd0033c9ac772]*/ /*[clinic input] @@ -4971,32 +5066,63 @@ PyDoc_STRVAR(Test__doc__, "Varargs new method. For example, nargs is translated to PyTuple_GET_SIZE."); static PyObject * -Test_impl(PyTypeObject *type, Py_ssize_t nargs, PyObject *const *args); +Test_impl(PyTypeObject *type, PyObject *args); static PyObject * Test(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; PyTypeObject *base_tp = TestType; - PyObject *const *__clinic_args = NULL; + PyObject *__clinic_args = NULL; if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("Test", kwargs)) { goto exit; } - if (!_PyArg_CheckPositional("Test", PyTuple_GET_SIZE(args), 0, PY_SSIZE_T_MAX)) { + __clinic_args = Py_NewRef(args); + return_value = Test_impl(type, __clinic_args); + +exit: + /* Cleanup for args */ + Py_XDECREF(__clinic_args); + + return return_value; +} + +static PyObject * +Test_impl(PyTypeObject *type, PyObject *args) +/*[clinic end generated code: output=ee1e8892a67abd4a input=70ad829df3dd9b84]*/ + +PyDoc_STRVAR(Test__doc__, +"Test(*args)\n" +"--\n" +"\n" +"Varargs new method. For example, nargs is translated to PyTuple_GET_SIZE."); + +static PyObject * +Test_impl(PyTypeObject *type, Py_ssize_t nargs, PyObject *const *args); + +static PyObject * +Test(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + PyTypeObject *base_tp = TestType; + PyObject *const *__clinic_args = NULL; + + if ((type == base_tp || type->tp_init == base_tp->tp_init) && + !_PyArg_NoKeywords("Test", kwargs)) { goto exit; } - __clinic_args = _PyTuple_CAST(args)->ob_item; + __clinic_args = Py_NewRef(args); return_value = Test_impl(type, nvararg, __clinic_args); exit: + /* Cleanup for args */ + Py_XDECREF(__clinic_args); + return return_value; } -static PyObject * -Test_impl(PyTypeObject *type, Py_ssize_t nargs, PyObject *const *args) -/*[clinic end generated code: output=bf22f942407383a5 input=70ad829df3dd9b84]*/ /*[clinic input] diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index d492ea1d76aa3a..a4317a3ad468d8 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -33,6 +33,15 @@ from libclinic.cli import parse_file, Clinic +def repeat_fn(*functions): + def wrapper(test): + def wrapped(self): + for fn in functions: + with self.subTest(fn=fn): + test(self, fn) + return wrapped + return wrapper + def _make_clinic(*, filename='clinic_tests', limited_capi=False): clang = CLanguage(filename) c = Clinic(clang, filename=filename, limited_capi=limited_capi) @@ -3378,30 +3387,53 @@ def test_keyword_only_parameter(self): ac_tester.keyword_only_parameter(1) self.assertEqual(ac_tester.keyword_only_parameter(a=1), (1,)) - def test_varpos(self): - # fn(*args) - fn = ac_tester.varpos - self.assertEqual(fn(), ((),)) - self.assertEqual(fn(1, 2), ((1, 2),)) - - def test_posonly_varpos(self): - # fn(a, b, /, *args) - fn = ac_tester.posonly_varpos - self.assertRaises(TypeError, fn) - self.assertRaises(TypeError, fn, 1) - self.assertRaises(TypeError, fn, 1, b=2) - self.assertEqual(fn(1, 2), (1, 2, ())) - self.assertEqual(fn(1, 2, 3, 4), (1, 2, (3, 4))) - - def test_posonly_poskw_varpos(self): - # fn(a, /, b, *args) - fn = ac_tester.posonly_poskw_varpos - self.assertRaises(TypeError, fn) - self.assertEqual(fn(1, 2), (1, 2, ())) - self.assertEqual(fn(1, b=2), (1, 2, ())) - self.assertEqual(fn(1, 2, 3, 4), (1, 2, (3, 4))) - self.assertRaises(TypeError, fn, b=4) - self.assertRaises(TypeError, fn, 1, 2, 3, b=4) + if ac_tester is not None: + @repeat_fn(ac_tester.varpos, + ac_tester.varpos_array, + ac_tester.TestClass.varpos_no_fastcall, + ac_tester.TestClass.varpos_array_no_fastcall) + def test_varpos(self, fn): + # fn(*args) + self.assertEqual(fn(), ()) + self.assertEqual(fn(1, 2), (1, 2)) + + @repeat_fn(ac_tester.posonly_varpos, + ac_tester.posonly_varpos_array, + ac_tester.TestClass.posonly_varpos_no_fastcall, + ac_tester.TestClass.posonly_varpos_array_no_fastcall) + def test_posonly_varpos(self, fn): + # fn(a, b, /, *args) + self.assertRaises(TypeError, fn) + self.assertRaises(TypeError, fn, 1) + self.assertRaises(TypeError, fn, 1, b=2) + self.assertEqual(fn(1, 2), (1, 2, ())) + self.assertEqual(fn(1, 2, 3, 4), (1, 2, (3, 4))) + + @repeat_fn(ac_tester.posonly_req_opt_varpos, + ac_tester.posonly_req_opt_varpos_array, + ac_tester.TestClass.posonly_req_opt_varpos_no_fastcall, + ac_tester.TestClass.posonly_req_opt_varpos_array_no_fastcall) + def test_posonly_req_opt_varpos(self, fn): + # fn(a, b=False, /, *args) + self.assertRaises(TypeError, fn) + self.assertRaises(TypeError, fn, a=1) + self.assertEqual(fn(1), (1, False, ())) + self.assertEqual(fn(1, 2), (1, 2, ())) + self.assertEqual(fn(1, 2, 3, 4), (1, 2, (3, 4))) + + @repeat_fn(ac_tester.posonly_poskw_varpos, + ac_tester.posonly_poskw_varpos_array, + ac_tester.TestClass.posonly_poskw_varpos_no_fastcall, + ac_tester.TestClass.posonly_poskw_varpos_array_no_fastcall) + def test_posonly_poskw_varpos(self, fn): + # fn(a, /, b, *args) + self.assertRaises(TypeError, fn) + self.assertEqual(fn(1, 2), (1, 2, ())) + self.assertEqual(fn(1, b=2), (1, 2, ())) + self.assertEqual(fn(1, 2, 3, 4), (1, 2, (3, 4))) + self.assertRaises(TypeError, fn, b=4) + errmsg = re.escape("given by name ('b') and position (2)") + self.assertRaisesRegex(TypeError, errmsg, fn, 1, 2, 3, b=4) def test_poskw_varpos(self): # fn(a, *args) @@ -3409,7 +3441,8 @@ def test_poskw_varpos(self): self.assertRaises(TypeError, fn) self.assertRaises(TypeError, fn, 1, b=2) self.assertEqual(fn(a=1), (1, ())) - self.assertRaises(TypeError, fn, 1, a=2) + errmsg = re.escape("given by name ('a') and position (1)") + self.assertRaisesRegex(TypeError, errmsg, fn, 1, a=2) self.assertEqual(fn(1), (1, ())) self.assertEqual(fn(1, 2, 3, 4), (1, (2, 3, 4))) @@ -3417,7 +3450,8 @@ def test_poskw_varpos_kwonly_opt(self): # fn(a, *args, b=False) fn = ac_tester.poskw_varpos_kwonly_opt self.assertRaises(TypeError, fn) - self.assertRaises(TypeError, fn, 1, a=2) + errmsg = re.escape("given by name ('a') and position (1)") + self.assertRaisesRegex(TypeError, errmsg, fn, 1, a=2) self.assertEqual(fn(1, b=2), (1, (), True)) self.assertEqual(fn(1, 2, 3, 4), (1, (2, 3, 4), False)) self.assertEqual(fn(1, 2, 3, 4, b=5), (1, (2, 3, 4), True)) @@ -3428,7 +3462,8 @@ def test_poskw_varpos_kwonly_opt2(self): # fn(a, *args, b=False, c=False) fn = ac_tester.poskw_varpos_kwonly_opt2 self.assertRaises(TypeError, fn) - self.assertRaises(TypeError, fn, 1, a=2) + errmsg = re.escape("given by name ('a') and position (1)") + self.assertRaisesRegex(TypeError, errmsg, fn, 1, a=2) self.assertEqual(fn(1, b=2), (1, (), 2, False)) self.assertEqual(fn(1, b=2, c=3), (1, (), 2, 3)) self.assertEqual(fn(1, 2, 3), (1, (2, 3), False, False)) @@ -3490,9 +3525,10 @@ def test_null_or_tuple_for_varargs(self): self.assertEqual(fn(covariant=True, name='a'), ('a', (), True)) self.assertRaises(TypeError, fn, covariant=True) - self.assertRaises(TypeError, fn, 1, name='a') - self.assertRaises(TypeError, fn, 1, 2, 3, name='a', covariant=True) - self.assertRaises(TypeError, fn, 1, 2, 3, covariant=True, name='a') + errmsg = re.escape("given by name ('name') and position (1)") + self.assertRaisesRegex(TypeError, errmsg, fn, 1, name='a') + self.assertRaisesRegex(TypeError, errmsg, fn, 1, 2, 3, name='a', covariant=True) + self.assertRaisesRegex(TypeError, errmsg, fn, 1, 2, 3, covariant=True, name='a') def test_cloned_func_exception_message(self): incorrect_arg = -1 # f1() and f2() accept a single str @@ -3568,14 +3604,15 @@ def test_defclass_posonly_varpos(self): cls = ac_tester.TestClass obj = cls() fn = obj.defclass_posonly_varpos - self.assertRaises(TypeError, fn) - self.assertRaises(TypeError, fn, 1) + errmsg = 'takes at least 2 positional arguments' + self.assertRaisesRegex(TypeError, errmsg, fn) + self.assertRaisesRegex(TypeError, errmsg, fn, 1) self.assertEqual(fn(1, 2), (cls, 1, 2, ())) self.assertEqual(fn(1, 2, 3, 4), (cls, 1, 2, (3, 4))) fn = cls.defclass_posonly_varpos self.assertRaises(TypeError, fn) - self.assertRaises(TypeError, fn, obj) - self.assertRaises(TypeError, fn, obj, 1) + self.assertRaisesRegex(TypeError, errmsg, fn, obj) + self.assertRaisesRegex(TypeError, errmsg, fn, obj, 1) self.assertEqual(fn(obj, 1, 2), (cls, 1, 2, ())) self.assertEqual(fn(obj, 1, 2, 3, 4), (cls, 1, 2, (3, 4))) diff --git a/Modules/_testclinic.c b/Modules/_testclinic.c index e3c8ba9b0b5074..320c8ddf214dcd 100644 --- a/Modules/_testclinic.c +++ b/Modules/_testclinic.c @@ -59,19 +59,20 @@ pack_arguments_newref(int argc, ...) } static PyObject * -pack_varargs_to_tuple(Py_ssize_t varargssize, PyObject *const *args) +pack_arguments_2pos_varpos(PyObject *a, PyObject *b, + PyObject * const *args, Py_ssize_t args_length) +/*[clinic end generated code: output=267032f41bd039cc input=86ee3064b7853e86]*/ { - assert(!PyErr_Occurred()); - PyObject *tuple = PyTuple_New(varargssize); - if (!tuple) { + PyObject *tuple = _PyTuple_FromArray(args, args_length); + if (tuple == NULL) { return NULL; } - for (Py_ssize_t i = 0; i < varargssize; i++) { - PyTuple_SET_ITEM(tuple, i, Py_NewRef(args[i])); - } - return tuple; + PyObject *result = pack_arguments_newref(3, a, b, tuple); + Py_DECREF(tuple); + return result; } + /* Pack arguments to a tuple. * `wrapper` is function which converts primitive type to PyObject. * `arg_type` is type that arguments should be converted to before wrapped. */ @@ -984,16 +985,10 @@ varpos [clinic start generated code]*/ static PyObject * -varpos_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args) -/*[clinic end generated code: output=b65096f423fb5dcc input=f87cd674145d394c]*/ +varpos_impl(PyObject *module, PyObject *args) +/*[clinic end generated code: output=7b0b9545872bdca4 input=f87cd674145d394c]*/ { - PyObject *vararg_tuple = pack_varargs_to_tuple(nargs, args); - if (!vararg_tuple) { - return NULL; - } - PyObject *result = pack_arguments_newref(1, vararg_tuple); - Py_DECREF(vararg_tuple); - return result; + return Py_NewRef(args); } @@ -1009,16 +1004,29 @@ posonly_varpos static PyObject * posonly_varpos_impl(PyObject *module, PyObject *a, PyObject *b, - Py_ssize_t nargs, PyObject *const *args) -/*[clinic end generated code: output=d10d43d86d117ab3 input=c9fd7895cfbaabba]*/ + PyObject *args) +/*[clinic end generated code: output=5dae5eb2a0d623cd input=c9fd7895cfbaabba]*/ { - PyObject *vararg_tuple = pack_varargs_to_tuple(nargs, args); - if (!vararg_tuple) { - return NULL; - } - PyObject *result = pack_arguments_newref(3, a, b, vararg_tuple); - Py_DECREF(vararg_tuple); - return result; + return pack_arguments_newref(3, a, b, args); +} + + +/*[clinic input] +posonly_req_opt_varpos + + a: object + b: object = False + / + *args: object + +[clinic start generated code]*/ + +static PyObject * +posonly_req_opt_varpos_impl(PyObject *module, PyObject *a, PyObject *b, + PyObject *args) +/*[clinic end generated code: output=67f82f90838e166a input=a49bd64740171e1c]*/ +{ + return pack_arguments_newref(3, a, b, args); } @@ -1130,6 +1138,81 @@ varpos_kwonly_req_opt_impl(PyObject *module, PyObject *args, PyObject *a, } +/*[clinic input] +varpos_array + + *args: array + +[clinic start generated code]*/ + +static PyObject * +varpos_array_impl(PyObject *module, PyObject * const *args, + Py_ssize_t args_length) +/*[clinic end generated code: output=a25f42f39c9b13ad input=97b8bdcf87e019c7]*/ +{ + return _PyTuple_FromArray(args, args_length); +} + + +/*[clinic input] +posonly_varpos_array + + a: object + b: object + / + *args: array + +[clinic start generated code]*/ + +static PyObject * +posonly_varpos_array_impl(PyObject *module, PyObject *a, PyObject *b, + PyObject * const *args, Py_ssize_t args_length) +/*[clinic end generated code: output=267032f41bd039cc input=86ee3064b7853e86]*/ +{ + return pack_arguments_2pos_varpos(a, b, args, args_length); +} + + +/*[clinic input] +posonly_req_opt_varpos_array + + a: object + b: object = False + / + *args: array + +[clinic start generated code]*/ + +static PyObject * +posonly_req_opt_varpos_array_impl(PyObject *module, PyObject *a, PyObject *b, + PyObject * const *args, + Py_ssize_t args_length) +/*[clinic end generated code: output=f2f93c77ead93699 input=b01d7728164fd93e]*/ +{ + return pack_arguments_2pos_varpos(a, b, args, args_length); +} + + +/*[clinic input] +posonly_poskw_varpos_array + + a: object + / + b: object + *args: array + +[clinic start generated code]*/ + +static PyObject * +posonly_poskw_varpos_array_impl(PyObject *module, PyObject *a, PyObject *b, + PyObject * const *args, + Py_ssize_t args_length) +/*[clinic end generated code: output=155811a8b2d65a12 input=5fb08cdc6afb9d7c]*/ +{ + return pack_arguments_2pos_varpos(a, b, args, args_length); +} + + /*[clinic input] gh_32092_oob @@ -1183,9 +1266,8 @@ Proof-of-concept of GH-99233 refcount error bug. [clinic start generated code]*/ static PyObject * -gh_99233_refcount_impl(PyObject *module, Py_ssize_t nargs, - PyObject *const *args) -/*[clinic end generated code: output=b570007e61e5c670 input=eecfdc2092d90dc3]*/ +gh_99233_refcount_impl(PyObject *module, PyObject *args) +/*[clinic end generated code: output=585855abfbca9a7f input=eecfdc2092d90dc3]*/ { Py_RETURN_NONE; } @@ -1295,9 +1377,9 @@ clone_with_conv_f2_impl(PyObject *module, custom_t path) /*[clinic input] -class _testclinic.TestClass "PyObject *" "PyObject" +class _testclinic.TestClass "PyObject *" "&PyBaseObject_Type" [clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=668a591c65bec947]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=c991635bb3c91f1a]*/ /*[clinic input] _testclinic.TestClass.get_defining_class @@ -1360,11 +1442,192 @@ _testclinic_TestClass_defclass_posonly_varpos_impl(PyObject *self, return pack_arguments_newref(4, cls, a, b, args); } + +/*[clinic input] +@classmethod +_testclinic.TestClass.__new__ as varpos_no_fastcall + + *args: object + +[clinic start generated code]*/ + +static PyObject * +varpos_no_fastcall_impl(PyTypeObject *type, PyObject *args) +/*[clinic end generated code: output=04e94f2898bb2dde input=b0447ebab3e81001]*/ +{ + return Py_NewRef(args); +} + + +/*[clinic input] +@classmethod +_testclinic.TestClass.__new__ as posonly_varpos_no_fastcall + + a: object + b: object + / + *args: object + +[clinic start generated code]*/ + +static PyObject * +posonly_varpos_no_fastcall_impl(PyTypeObject *type, PyObject *a, PyObject *b, + PyObject *args) +/*[clinic end generated code: output=b0a0425719f69f5a input=d2ec37a06b3c2389]*/ +{ + return pack_arguments_newref(3, a, b, args); +} + + +/*[clinic input] +@classmethod +_testclinic.TestClass.__new__ as posonly_req_opt_varpos_no_fastcall + + a: object + b: object = False + / + *args: object + +[clinic start generated code]*/ + +static PyObject * +posonly_req_opt_varpos_no_fastcall_impl(PyTypeObject *type, PyObject *a, + PyObject *b, PyObject *args) +/*[clinic end generated code: output=3c44915b1a554e2d input=e9e74686a5e6a06d]*/ +{ + return pack_arguments_newref(3, a, b, args); +} + + +/*[clinic input] +@classmethod +_testclinic.TestClass.__new__ as posonly_poskw_varpos_no_fastcall + + a: object + / + b: object + *args: object + +[clinic start generated code]*/ + +static PyObject * +posonly_poskw_varpos_no_fastcall_impl(PyTypeObject *type, PyObject *a, + PyObject *b, PyObject *args) +/*[clinic end generated code: output=6ad74bed4bdc7f96 input=fa931c38184213aa]*/ +{ + return pack_arguments_newref(3, a, b, args); +} + + +/*[clinic input] +@classmethod +_testclinic.TestClass.__new__ as varpos_array_no_fastcall + + *args: array + +[clinic start generated code]*/ + +static PyObject * +varpos_array_no_fastcall_impl(PyTypeObject *type, PyObject * const *args, + Py_ssize_t args_length) +/*[clinic end generated code: output=f99d984346c60d42 input=368d8eea6de48c12]*/ +{ + return _PyTuple_FromArray(args, args_length); +} + + +/*[clinic input] +@classmethod +_testclinic.TestClass.__new__ as posonly_varpos_array_no_fastcall + + a: object + b: object + / + *args: array + +[clinic start generated code]*/ + +static PyObject * +posonly_varpos_array_no_fastcall_impl(PyTypeObject *type, PyObject *a, + PyObject *b, PyObject * const *args, + Py_ssize_t args_length) +/*[clinic end generated code: output=1eec4da1fb5b5978 input=7330c8d819a23548]*/ +{ + return pack_arguments_2pos_varpos(a, b, args, args_length); +} + + +/*[clinic input] +@classmethod +_testclinic.TestClass.__new__ as posonly_req_opt_varpos_array_no_fastcall + + a: object + b: object = False + / + *args: array + +[clinic start generated code]*/ + +static PyObject * +posonly_req_opt_varpos_array_no_fastcall_impl(PyTypeObject *type, + PyObject *a, PyObject *b, + PyObject * const *args, + Py_ssize_t args_length) +/*[clinic end generated code: output=88041c2176135218 input=7f5fd34ee5f9e0bf]*/ +{ + return pack_arguments_2pos_varpos(a, b, args, args_length); +} + + +/*[clinic input] +@classmethod +_testclinic.TestClass.__new__ as posonly_poskw_varpos_array_no_fastcall + + a: object + / + b: object + *args: array + +[clinic start generated code]*/ + +static PyObject * +posonly_poskw_varpos_array_no_fastcall_impl(PyTypeObject *type, PyObject *a, + PyObject *b, + PyObject * const *args, + Py_ssize_t args_length) +/*[clinic end generated code: output=70eda18c3667681e input=2b0fcd7bd9bb865c]*/ +{ + return pack_arguments_2pos_varpos(a, b, args, args_length); +} + static struct PyMethodDef test_class_methods[] = { _TESTCLINIC_TESTCLASS_GET_DEFINING_CLASS_METHODDEF _TESTCLINIC_TESTCLASS_GET_DEFINING_CLASS_ARG_METHODDEF _TESTCLINIC_TESTCLASS_DEFCLASS_VARPOS_METHODDEF _TESTCLINIC_TESTCLASS_DEFCLASS_POSONLY_VARPOS_METHODDEF + + {"varpos_no_fastcall", _PyCFunction_CAST(varpos_no_fastcall), + METH_VARARGS|METH_KEYWORDS|METH_CLASS, ""}, + {"posonly_varpos_no_fastcall", _PyCFunction_CAST(posonly_varpos_no_fastcall), + METH_VARARGS|METH_KEYWORDS|METH_CLASS, ""}, + {"posonly_req_opt_varpos_no_fastcall", _PyCFunction_CAST(posonly_req_opt_varpos_no_fastcall), + METH_VARARGS|METH_KEYWORDS|METH_CLASS, ""}, + {"posonly_poskw_varpos_no_fastcall", _PyCFunction_CAST(posonly_poskw_varpos_no_fastcall), + METH_VARARGS|METH_KEYWORDS|METH_CLASS, ""}, + + {"varpos_array_no_fastcall", + _PyCFunction_CAST(varpos_array_no_fastcall), + METH_VARARGS|METH_KEYWORDS|METH_CLASS, ""}, + {"posonly_varpos_array_no_fastcall", + _PyCFunction_CAST(posonly_varpos_array_no_fastcall), + METH_VARARGS|METH_KEYWORDS|METH_CLASS, ""}, + {"posonly_req_opt_varpos_array_no_fastcall", + _PyCFunction_CAST(posonly_req_opt_varpos_array_no_fastcall), + METH_VARARGS|METH_KEYWORDS|METH_CLASS, ""}, + {"posonly_poskw_varpos_array_no_fastcall", + _PyCFunction_CAST(posonly_poskw_varpos_array_no_fastcall), + METH_VARARGS|METH_KEYWORDS|METH_CLASS, ""}, + {NULL, NULL} }; @@ -2023,12 +2286,19 @@ static PyMethodDef tester_methods[] = { VARPOS_METHODDEF POSONLY_VARPOS_METHODDEF + POSONLY_REQ_OPT_VARPOS_METHODDEF POSONLY_POSKW_VARPOS_METHODDEF POSKW_VARPOS_METHODDEF POSKW_VARPOS_KWONLY_OPT_METHODDEF POSKW_VARPOS_KWONLY_OPT2_METHODDEF VARPOS_KWONLY_OPT_METHODDEF VARPOS_KWONLY_REQ_OPT_METHODDEF + + VARPOS_ARRAY_METHODDEF + POSONLY_VARPOS_ARRAY_METHODDEF + POSONLY_REQ_OPT_VARPOS_ARRAY_METHODDEF + POSONLY_POSKW_VARPOS_ARRAY_METHODDEF + GH_32092_OOB_METHODDEF GH_32092_KW_PASS_METHODDEF GH_99233_REFCOUNT_METHODDEF diff --git a/Modules/clinic/_testclinic.c.h b/Modules/clinic/_testclinic.c.h index 7e29998c7db520..0f5ae5ec869753 100644 --- a/Modules/clinic/_testclinic.c.h +++ b/Modules/clinic/_testclinic.c.h @@ -9,6 +9,7 @@ preserve #include "pycore_long.h" // _PyLong_UnsignedShort_Converter() #include "pycore_modsupport.h" // _PyArg_CheckPositional() #include "pycore_runtime.h" // _Py_ID() +#include "pycore_tuple.h" // _PyTuple_FromArray() PyDoc_STRVAR(test_empty_function__doc__, "test_empty_function($module, /)\n" @@ -2530,22 +2531,24 @@ PyDoc_STRVAR(varpos__doc__, {"varpos", _PyCFunction_CAST(varpos), METH_FASTCALL, varpos__doc__}, static PyObject * -varpos_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args); +varpos_impl(PyObject *module, PyObject *args); static PyObject * varpos(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - Py_ssize_t nvararg = nargs - 0; - PyObject *const *__clinic_args = NULL; + PyObject *__clinic_args = NULL; - if (!_PyArg_CheckPositional("varpos", nargs, 0, PY_SSIZE_T_MAX)) { + __clinic_args = _PyTuple_FromArray(args, nargs); + if (__clinic_args == NULL) { goto exit; } - __clinic_args = args + 0; - return_value = varpos_impl(module, nvararg, __clinic_args); + return_value = varpos_impl(module, __clinic_args); exit: + /* Cleanup for args */ + Py_XDECREF(__clinic_args); + return return_value; } @@ -2559,26 +2562,75 @@ PyDoc_STRVAR(posonly_varpos__doc__, static PyObject * posonly_varpos_impl(PyObject *module, PyObject *a, PyObject *b, - Py_ssize_t nargs, PyObject *const *args); + PyObject *args); static PyObject * posonly_varpos(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - Py_ssize_t nvararg = nargs - 2; PyObject *a; PyObject *b; - PyObject *const *__clinic_args = NULL; + PyObject *__clinic_args = NULL; if (!_PyArg_CheckPositional("posonly_varpos", nargs, 2, PY_SSIZE_T_MAX)) { goto exit; } a = args[0]; b = args[1]; - __clinic_args = args + 2; - return_value = posonly_varpos_impl(module, a, b, nvararg, __clinic_args); + __clinic_args = _PyTuple_FromArray(args + 2, nargs - 2); + if (__clinic_args == NULL) { + goto exit; + } + return_value = posonly_varpos_impl(module, a, b, __clinic_args); exit: + /* Cleanup for args */ + Py_XDECREF(__clinic_args); + + return return_value; +} + +PyDoc_STRVAR(posonly_req_opt_varpos__doc__, +"posonly_req_opt_varpos($module, a, b=False, /, *args)\n" +"--\n" +"\n"); + +#define POSONLY_REQ_OPT_VARPOS_METHODDEF \ + {"posonly_req_opt_varpos", _PyCFunction_CAST(posonly_req_opt_varpos), METH_FASTCALL, posonly_req_opt_varpos__doc__}, + +static PyObject * +posonly_req_opt_varpos_impl(PyObject *module, PyObject *a, PyObject *b, + PyObject *args); + +static PyObject * +posonly_req_opt_varpos(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *a; + PyObject *b = Py_False; + PyObject *__clinic_args = NULL; + + if (!_PyArg_CheckPositional("posonly_req_opt_varpos", nargs, 1, PY_SSIZE_T_MAX)) { + goto exit; + } + a = args[0]; + if (nargs < 2) { + goto skip_optional; + } + b = args[1]; +skip_optional: + __clinic_args = nargs > 2 + ? _PyTuple_FromArray(args + 2, nargs - 2) + : PyTuple_New(0); + if (__clinic_args == NULL) { + goto exit; + } + return_value = posonly_req_opt_varpos_impl(module, a, b, __clinic_args); + +exit: + /* Cleanup for args */ + Py_XDECREF(__clinic_args); + return return_value; } @@ -2623,22 +2675,30 @@ posonly_poskw_varpos(PyObject *module, PyObject *const *args, Py_ssize_t nargs, .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[3]; + PyObject *argsbuf[2]; + PyObject * const *fastargs; PyObject *a; PyObject *b; PyObject *__clinic_args = NULL; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, 2, argsbuf); - if (!args) { + fastargs = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + if (!fastargs) { + goto exit; + } + a = fastargs[0]; + b = fastargs[1]; + __clinic_args = nargs > 2 + ? _PyTuple_FromArray(args + 2, nargs - 2) + : PyTuple_New(0); + if (__clinic_args == NULL) { goto exit; } - a = args[0]; - b = args[1]; - __clinic_args = args[2]; return_value = posonly_poskw_varpos_impl(module, a, b, __clinic_args); exit: + /* Cleanup for args */ Py_XDECREF(__clinic_args); + return return_value; } @@ -2682,20 +2742,28 @@ poskw_varpos(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[2]; + PyObject *argsbuf[1]; + PyObject * const *fastargs; PyObject *a; PyObject *__clinic_args = NULL; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf); - if (!args) { + fastargs = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!fastargs) { + goto exit; + } + a = fastargs[0]; + __clinic_args = nargs > 1 + ? _PyTuple_FromArray(args + 1, nargs - 1) + : PyTuple_New(0); + if (__clinic_args == NULL) { goto exit; } - a = args[0]; - __clinic_args = args[1]; return_value = poskw_varpos_impl(module, a, __clinic_args); exit: + /* Cleanup for args */ Py_XDECREF(__clinic_args); + return return_value; } @@ -2740,30 +2808,38 @@ poskw_varpos_kwonly_opt(PyObject *module, PyObject *const *args, Py_ssize_t narg .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[3]; + PyObject *argsbuf[2]; + PyObject * const *fastargs; Py_ssize_t noptargs = Py_MIN(nargs, 1) + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; PyObject *a; PyObject *__clinic_args = NULL; int b = 0; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf); - if (!args) { + fastargs = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!fastargs) { goto exit; } - a = args[0]; - __clinic_args = args[1]; + a = fastargs[0]; if (!noptargs) { goto skip_optional_kwonly; } - b = PyObject_IsTrue(args[2]); + b = PyObject_IsTrue(fastargs[1]); if (b < 0) { goto exit; } skip_optional_kwonly: + __clinic_args = nargs > 1 + ? _PyTuple_FromArray(args + 1, nargs - 1) + : PyTuple_New(0); + if (__clinic_args == NULL) { + goto exit; + } return_value = poskw_varpos_kwonly_opt_impl(module, a, __clinic_args, b); exit: + /* Cleanup for args */ Py_XDECREF(__clinic_args); + return return_value; } @@ -2808,34 +2884,42 @@ poskw_varpos_kwonly_opt2(PyObject *module, PyObject *const *args, Py_ssize_t nar .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[4]; + PyObject *argsbuf[3]; + PyObject * const *fastargs; Py_ssize_t noptargs = Py_MIN(nargs, 1) + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; PyObject *a; PyObject *__clinic_args = NULL; PyObject *b = Py_False; PyObject *c = Py_False; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf); - if (!args) { + fastargs = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!fastargs) { goto exit; } - a = args[0]; - __clinic_args = args[1]; + a = fastargs[0]; if (!noptargs) { goto skip_optional_kwonly; } - if (args[2]) { - b = args[2]; + if (fastargs[1]) { + b = fastargs[1]; if (!--noptargs) { goto skip_optional_kwonly; } } - c = args[3]; + c = fastargs[2]; skip_optional_kwonly: + __clinic_args = nargs > 1 + ? _PyTuple_FromArray(args + 1, nargs - 1) + : PyTuple_New(0); + if (__clinic_args == NULL) { + goto exit; + } return_value = poskw_varpos_kwonly_opt2_impl(module, a, __clinic_args, b, c); exit: + /* Cleanup for args */ Py_XDECREF(__clinic_args); + return return_value; } @@ -2879,25 +2963,31 @@ varpos_kwonly_opt(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyO .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[2]; + PyObject *argsbuf[1]; + PyObject * const *fastargs; Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; PyObject *__clinic_args = NULL; PyObject *b = Py_False; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, 0, argsbuf); - if (!args) { + fastargs = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, argsbuf); + if (!fastargs) { goto exit; } - __clinic_args = args[0]; if (!noptargs) { goto skip_optional_kwonly; } - b = args[1]; + b = fastargs[0]; skip_optional_kwonly: + __clinic_args = _PyTuple_FromArray(args, nargs); + if (__clinic_args == NULL) { + goto exit; + } return_value = varpos_kwonly_opt_impl(module, __clinic_args, b); exit: + /* Cleanup for args */ Py_XDECREF(__clinic_args); + return return_value; } @@ -2942,34 +3032,202 @@ varpos_kwonly_req_opt(PyObject *module, PyObject *const *args, Py_ssize_t nargs, .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[4]; + PyObject *argsbuf[3]; + PyObject * const *fastargs; Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; PyObject *__clinic_args = NULL; PyObject *a; PyObject *b = Py_False; PyObject *c = Py_False; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 0, 0, 1, 0, argsbuf); - if (!args) { + fastargs = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 0, 0, 1, argsbuf); + if (!fastargs) { goto exit; } - __clinic_args = args[0]; - a = args[1]; + a = fastargs[0]; if (!noptargs) { goto skip_optional_kwonly; } - if (args[2]) { - b = args[2]; + if (fastargs[1]) { + b = fastargs[1]; if (!--noptargs) { goto skip_optional_kwonly; } } - c = args[3]; + c = fastargs[2]; skip_optional_kwonly: + __clinic_args = _PyTuple_FromArray(args, nargs); + if (__clinic_args == NULL) { + goto exit; + } return_value = varpos_kwonly_req_opt_impl(module, __clinic_args, a, b, c); exit: + /* Cleanup for args */ Py_XDECREF(__clinic_args); + + return return_value; +} + +PyDoc_STRVAR(varpos_array__doc__, +"varpos_array($module, /, *args)\n" +"--\n" +"\n"); + +#define VARPOS_ARRAY_METHODDEF \ + {"varpos_array", _PyCFunction_CAST(varpos_array), METH_FASTCALL, varpos_array__doc__}, + +static PyObject * +varpos_array_impl(PyObject *module, PyObject * const *args, + Py_ssize_t args_length); + +static PyObject * +varpos_array(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject * const *__clinic_args; + Py_ssize_t args_length; + + __clinic_args = args; + args_length = nargs; + return_value = varpos_array_impl(module, __clinic_args, args_length); + + return return_value; +} + +PyDoc_STRVAR(posonly_varpos_array__doc__, +"posonly_varpos_array($module, a, b, /, *args)\n" +"--\n" +"\n"); + +#define POSONLY_VARPOS_ARRAY_METHODDEF \ + {"posonly_varpos_array", _PyCFunction_CAST(posonly_varpos_array), METH_FASTCALL, posonly_varpos_array__doc__}, + +static PyObject * +posonly_varpos_array_impl(PyObject *module, PyObject *a, PyObject *b, + PyObject * const *args, Py_ssize_t args_length); + +static PyObject * +posonly_varpos_array(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *a; + PyObject *b; + PyObject * const *__clinic_args; + Py_ssize_t args_length; + + if (!_PyArg_CheckPositional("posonly_varpos_array", nargs, 2, PY_SSIZE_T_MAX)) { + goto exit; + } + a = args[0]; + b = args[1]; + __clinic_args = args + 2; + args_length = nargs - 2; + return_value = posonly_varpos_array_impl(module, a, b, __clinic_args, args_length); + +exit: + return return_value; +} + +PyDoc_STRVAR(posonly_req_opt_varpos_array__doc__, +"posonly_req_opt_varpos_array($module, a, b=False, /, *args)\n" +"--\n" +"\n"); + +#define POSONLY_REQ_OPT_VARPOS_ARRAY_METHODDEF \ + {"posonly_req_opt_varpos_array", _PyCFunction_CAST(posonly_req_opt_varpos_array), METH_FASTCALL, posonly_req_opt_varpos_array__doc__}, + +static PyObject * +posonly_req_opt_varpos_array_impl(PyObject *module, PyObject *a, PyObject *b, + PyObject * const *args, + Py_ssize_t args_length); + +static PyObject * +posonly_req_opt_varpos_array(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *a; + PyObject *b = Py_False; + PyObject * const *__clinic_args; + Py_ssize_t args_length; + + if (!_PyArg_CheckPositional("posonly_req_opt_varpos_array", nargs, 1, PY_SSIZE_T_MAX)) { + goto exit; + } + a = args[0]; + if (nargs < 2) { + goto skip_optional; + } + b = args[1]; +skip_optional: + __clinic_args = nargs > 2 ? args + 2 : args; + args_length = Py_MAX(0, nargs - 2); + return_value = posonly_req_opt_varpos_array_impl(module, a, b, __clinic_args, args_length); + +exit: + return return_value; +} + +PyDoc_STRVAR(posonly_poskw_varpos_array__doc__, +"posonly_poskw_varpos_array($module, a, /, b, *args)\n" +"--\n" +"\n"); + +#define POSONLY_POSKW_VARPOS_ARRAY_METHODDEF \ + {"posonly_poskw_varpos_array", _PyCFunction_CAST(posonly_poskw_varpos_array), METH_FASTCALL|METH_KEYWORDS, posonly_poskw_varpos_array__doc__}, + +static PyObject * +posonly_poskw_varpos_array_impl(PyObject *module, PyObject *a, PyObject *b, + PyObject * const *args, + Py_ssize_t args_length); + +static PyObject * +posonly_poskw_varpos_array(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { _Py_LATIN1_CHR('b'), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"", "b", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "posonly_poskw_varpos_array", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + PyObject * const *fastargs; + PyObject *a; + PyObject *b; + PyObject * const *__clinic_args; + Py_ssize_t args_length; + + fastargs = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + if (!fastargs) { + goto exit; + } + a = fastargs[0]; + b = fastargs[1]; + __clinic_args = nargs > 2 ? args + 2 : args; + args_length = Py_MAX(0, nargs - 2); + return_value = posonly_poskw_varpos_array_impl(module, a, b, __clinic_args, args_length); + +exit: return return_value; } @@ -3015,7 +3273,8 @@ gh_32092_oob(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[5]; + PyObject *argsbuf[4]; + PyObject * const *fastargs; Py_ssize_t noptargs = Py_MIN(nargs, 2) + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 2; PyObject *pos1; PyObject *pos2; @@ -3023,28 +3282,35 @@ gh_32092_oob(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject PyObject *kw1 = Py_None; PyObject *kw2 = Py_None; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, 2, argsbuf); - if (!args) { + fastargs = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + if (!fastargs) { goto exit; } - pos1 = args[0]; - pos2 = args[1]; - varargs = args[2]; + pos1 = fastargs[0]; + pos2 = fastargs[1]; if (!noptargs) { goto skip_optional_kwonly; } - if (args[3]) { - kw1 = args[3]; + if (fastargs[2]) { + kw1 = fastargs[2]; if (!--noptargs) { goto skip_optional_kwonly; } } - kw2 = args[4]; + kw2 = fastargs[3]; skip_optional_kwonly: + varargs = nargs > 2 + ? _PyTuple_FromArray(args + 2, nargs - 2) + : PyTuple_New(0); + if (varargs == NULL) { + goto exit; + } return_value = gh_32092_oob_impl(module, pos1, pos2, varargs, kw1, kw2); exit: + /* Cleanup for varargs */ Py_XDECREF(varargs); + return return_value; } @@ -3090,27 +3356,35 @@ gh_32092_kw_pass(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[3]; + PyObject *argsbuf[2]; + PyObject * const *fastargs; Py_ssize_t noptargs = Py_MIN(nargs, 1) + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; PyObject *pos; PyObject *__clinic_args = NULL; PyObject *kw = Py_None; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf); - if (!args) { + fastargs = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!fastargs) { goto exit; } - pos = args[0]; - __clinic_args = args[1]; + pos = fastargs[0]; if (!noptargs) { goto skip_optional_kwonly; } - kw = args[2]; + kw = fastargs[1]; skip_optional_kwonly: + __clinic_args = nargs > 1 + ? _PyTuple_FromArray(args + 1, nargs - 1) + : PyTuple_New(0); + if (__clinic_args == NULL) { + goto exit; + } return_value = gh_32092_kw_pass_impl(module, pos, __clinic_args, kw); exit: + /* Cleanup for args */ Py_XDECREF(__clinic_args); + return return_value; } @@ -3124,23 +3398,24 @@ PyDoc_STRVAR(gh_99233_refcount__doc__, {"gh_99233_refcount", _PyCFunction_CAST(gh_99233_refcount), METH_FASTCALL, gh_99233_refcount__doc__}, static PyObject * -gh_99233_refcount_impl(PyObject *module, Py_ssize_t nargs, - PyObject *const *args); +gh_99233_refcount_impl(PyObject *module, PyObject *args); static PyObject * gh_99233_refcount(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - Py_ssize_t nvararg = nargs - 0; - PyObject *const *__clinic_args = NULL; + PyObject *__clinic_args = NULL; - if (!_PyArg_CheckPositional("gh_99233_refcount", nargs, 0, PY_SSIZE_T_MAX)) { + __clinic_args = _PyTuple_FromArray(args, nargs); + if (__clinic_args == NULL) { goto exit; } - __clinic_args = args + 0; - return_value = gh_99233_refcount_impl(module, nvararg, __clinic_args); + return_value = gh_99233_refcount_impl(module, __clinic_args); exit: + /* Cleanup for args */ + Py_XDECREF(__clinic_args); + return return_value; } @@ -3220,30 +3495,38 @@ null_or_tuple_for_varargs(PyObject *module, PyObject *const *args, Py_ssize_t na .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[3]; + PyObject *argsbuf[2]; + PyObject * const *fastargs; Py_ssize_t noptargs = Py_MIN(nargs, 1) + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; PyObject *name; PyObject *constraints = NULL; int covariant = 0; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf); - if (!args) { + fastargs = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!fastargs) { goto exit; } - name = args[0]; - constraints = args[1]; + name = fastargs[0]; if (!noptargs) { goto skip_optional_kwonly; } - covariant = PyObject_IsTrue(args[2]); + covariant = PyObject_IsTrue(fastargs[1]); if (covariant < 0) { goto exit; } skip_optional_kwonly: + constraints = nargs > 1 + ? _PyTuple_FromArray(args + 1, nargs - 1) + : PyTuple_New(0); + if (constraints == NULL) { + goto exit; + } return_value = null_or_tuple_for_varargs_impl(module, name, constraints, covariant); exit: + /* Cleanup for constraints */ Py_XDECREF(constraints); + return return_value; } @@ -3613,17 +3896,23 @@ _testclinic_TestClass_defclass_varpos(PyObject *self, PyTypeObject *cls, PyObjec }; #undef KWTUPLE PyObject *argsbuf[1]; + PyObject * const *fastargs; PyObject *__clinic_args = NULL; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, 0, argsbuf); - if (!args) { + fastargs = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, argsbuf); + if (!fastargs) { + goto exit; + } + __clinic_args = _PyTuple_FromArray(args, nargs); + if (__clinic_args == NULL) { goto exit; } - __clinic_args = args[0]; return_value = _testclinic_TestClass_defclass_varpos_impl(self, cls, __clinic_args); exit: + /* Cleanup for args */ Py_XDECREF(__clinic_args); + return return_value; } @@ -3658,22 +3947,335 @@ _testclinic_TestClass_defclass_posonly_varpos(PyObject *self, PyTypeObject *cls, .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[3]; + PyObject *argsbuf[2]; + PyObject * const *fastargs; PyObject *a; PyObject *b; PyObject *__clinic_args = NULL; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, 2, argsbuf); - if (!args) { + fastargs = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + if (!fastargs) { + goto exit; + } + a = fastargs[0]; + b = fastargs[1]; + __clinic_args = _PyTuple_FromArray(args + 2, nargs - 2); + if (__clinic_args == NULL) { goto exit; } - a = args[0]; - b = args[1]; - __clinic_args = args[2]; return_value = _testclinic_TestClass_defclass_posonly_varpos_impl(self, cls, a, b, __clinic_args); exit: + /* Cleanup for args */ + Py_XDECREF(__clinic_args); + + return return_value; +} + +static PyObject * +varpos_no_fastcall_impl(PyTypeObject *type, PyObject *args); + +static PyObject * +varpos_no_fastcall(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + PyTypeObject *base_tp = &PyBaseObject_Type; + PyObject *__clinic_args = NULL; + + if ((type == base_tp || type->tp_init == base_tp->tp_init) && + !_PyArg_NoKeywords("TestClass", kwargs)) { + goto exit; + } + __clinic_args = Py_NewRef(args); + return_value = varpos_no_fastcall_impl(type, __clinic_args); + +exit: + /* Cleanup for args */ + Py_XDECREF(__clinic_args); + + return return_value; +} + +static PyObject * +posonly_varpos_no_fastcall_impl(PyTypeObject *type, PyObject *a, PyObject *b, + PyObject *args); + +static PyObject * +posonly_varpos_no_fastcall(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + PyTypeObject *base_tp = &PyBaseObject_Type; + PyObject *a; + PyObject *b; + PyObject *__clinic_args = NULL; + + if ((type == base_tp || type->tp_init == base_tp->tp_init) && + !_PyArg_NoKeywords("TestClass", kwargs)) { + goto exit; + } + if (!_PyArg_CheckPositional("TestClass", PyTuple_GET_SIZE(args), 2, PY_SSIZE_T_MAX)) { + goto exit; + } + a = PyTuple_GET_ITEM(args, 0); + b = PyTuple_GET_ITEM(args, 1); + __clinic_args = PyTuple_GetSlice(args, 2, PY_SSIZE_T_MAX); + if (!__clinic_args) { + goto exit; + } + return_value = posonly_varpos_no_fastcall_impl(type, a, b, __clinic_args); + +exit: + /* Cleanup for args */ + Py_XDECREF(__clinic_args); + + return return_value; +} + +static PyObject * +posonly_req_opt_varpos_no_fastcall_impl(PyTypeObject *type, PyObject *a, + PyObject *b, PyObject *args); + +static PyObject * +posonly_req_opt_varpos_no_fastcall(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + PyTypeObject *base_tp = &PyBaseObject_Type; + PyObject *a; + PyObject *b = Py_False; + PyObject *__clinic_args = NULL; + + if ((type == base_tp || type->tp_init == base_tp->tp_init) && + !_PyArg_NoKeywords("TestClass", kwargs)) { + goto exit; + } + if (!_PyArg_CheckPositional("TestClass", PyTuple_GET_SIZE(args), 1, PY_SSIZE_T_MAX)) { + goto exit; + } + a = PyTuple_GET_ITEM(args, 0); + if (PyTuple_GET_SIZE(args) < 2) { + goto skip_optional; + } + b = PyTuple_GET_ITEM(args, 1); +skip_optional: + __clinic_args = PyTuple_GetSlice(args, 2, PY_SSIZE_T_MAX); + if (!__clinic_args) { + goto exit; + } + return_value = posonly_req_opt_varpos_no_fastcall_impl(type, a, b, __clinic_args); + +exit: + /* Cleanup for args */ + Py_XDECREF(__clinic_args); + + return return_value; +} + +static PyObject * +posonly_poskw_varpos_no_fastcall_impl(PyTypeObject *type, PyObject *a, + PyObject *b, PyObject *args); + +static PyObject * +posonly_poskw_varpos_no_fastcall(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { _Py_LATIN1_CHR('b'), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"", "b", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "TestClass", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + PyObject * const *fastargs; + Py_ssize_t nargs = PyTuple_GET_SIZE(args); + PyObject *a; + PyObject *b; + PyObject *__clinic_args = NULL; + + fastargs = _PyArg_UnpackKeywordsWithVararg(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 2, 2, 0, argsbuf); + if (!fastargs) { + goto exit; + } + a = fastargs[0]; + b = fastargs[1]; + __clinic_args = PyTuple_GetSlice(args, 2, PY_SSIZE_T_MAX); + if (!__clinic_args) { + goto exit; + } + return_value = posonly_poskw_varpos_no_fastcall_impl(type, a, b, __clinic_args); + +exit: + /* Cleanup for args */ Py_XDECREF(__clinic_args); + + return return_value; +} + +static PyObject * +varpos_array_no_fastcall_impl(PyTypeObject *type, PyObject * const *args, + Py_ssize_t args_length); + +static PyObject * +varpos_array_no_fastcall(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + PyTypeObject *base_tp = &PyBaseObject_Type; + PyObject * const *__clinic_args; + Py_ssize_t args_length; + + if ((type == base_tp || type->tp_init == base_tp->tp_init) && + !_PyArg_NoKeywords("TestClass", kwargs)) { + goto exit; + } + __clinic_args = _PyTuple_ITEMS(args); + args_length = PyTuple_GET_SIZE(args); + return_value = varpos_array_no_fastcall_impl(type, __clinic_args, args_length); + +exit: + return return_value; +} + +static PyObject * +posonly_varpos_array_no_fastcall_impl(PyTypeObject *type, PyObject *a, + PyObject *b, PyObject * const *args, + Py_ssize_t args_length); + +static PyObject * +posonly_varpos_array_no_fastcall(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + PyTypeObject *base_tp = &PyBaseObject_Type; + PyObject *a; + PyObject *b; + PyObject * const *__clinic_args; + Py_ssize_t args_length; + + if ((type == base_tp || type->tp_init == base_tp->tp_init) && + !_PyArg_NoKeywords("TestClass", kwargs)) { + goto exit; + } + if (!_PyArg_CheckPositional("TestClass", PyTuple_GET_SIZE(args), 2, PY_SSIZE_T_MAX)) { + goto exit; + } + a = PyTuple_GET_ITEM(args, 0); + b = PyTuple_GET_ITEM(args, 1); + __clinic_args = _PyTuple_ITEMS(args) + 2; + args_length = PyTuple_GET_SIZE(args) - 2; + return_value = posonly_varpos_array_no_fastcall_impl(type, a, b, __clinic_args, args_length); + +exit: + return return_value; +} + +static PyObject * +posonly_req_opt_varpos_array_no_fastcall_impl(PyTypeObject *type, + PyObject *a, PyObject *b, + PyObject * const *args, + Py_ssize_t args_length); + +static PyObject * +posonly_req_opt_varpos_array_no_fastcall(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + PyTypeObject *base_tp = &PyBaseObject_Type; + PyObject *a; + PyObject *b = Py_False; + PyObject * const *__clinic_args; + Py_ssize_t args_length; + + if ((type == base_tp || type->tp_init == base_tp->tp_init) && + !_PyArg_NoKeywords("TestClass", kwargs)) { + goto exit; + } + if (!_PyArg_CheckPositional("TestClass", PyTuple_GET_SIZE(args), 1, PY_SSIZE_T_MAX)) { + goto exit; + } + a = PyTuple_GET_ITEM(args, 0); + if (PyTuple_GET_SIZE(args) < 2) { + goto skip_optional; + } + b = PyTuple_GET_ITEM(args, 1); +skip_optional: + __clinic_args = PyTuple_GET_SIZE(args) > 2 ? _PyTuple_ITEMS(args) + 2 : _PyTuple_ITEMS(args); + args_length = Py_MAX(0, PyTuple_GET_SIZE(args) - 2); + return_value = posonly_req_opt_varpos_array_no_fastcall_impl(type, a, b, __clinic_args, args_length); + +exit: + return return_value; +} + +static PyObject * +posonly_poskw_varpos_array_no_fastcall_impl(PyTypeObject *type, PyObject *a, + PyObject *b, + PyObject * const *args, + Py_ssize_t args_length); + +static PyObject * +posonly_poskw_varpos_array_no_fastcall(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { _Py_LATIN1_CHR('b'), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"", "b", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "TestClass", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + PyObject * const *fastargs; + Py_ssize_t nargs = PyTuple_GET_SIZE(args); + PyObject *a; + PyObject *b; + PyObject * const *__clinic_args; + Py_ssize_t args_length; + + fastargs = _PyArg_UnpackKeywordsWithVararg(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 2, 2, 0, argsbuf); + if (!fastargs) { + goto exit; + } + a = fastargs[0]; + b = fastargs[1]; + __clinic_args = PyTuple_GET_SIZE(args) > 2 ? _PyTuple_ITEMS(args) + 2 : _PyTuple_ITEMS(args); + args_length = Py_MAX(0, PyTuple_GET_SIZE(args) - 2); + return_value = posonly_poskw_varpos_array_no_fastcall_impl(type, a, b, __clinic_args, args_length); + +exit: return return_value; } -/*[clinic end generated code: output=7662d07e7d29cbeb input=a9049054013a1b77]*/ +/*[clinic end generated code: output=ed3408af146a746c input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_testclinic_depr.c.h b/Modules/clinic/_testclinic_depr.c.h index 95a2cc4cb5ed6d..0e374f100d6239 100644 --- a/Modules/clinic/_testclinic_depr.c.h +++ b/Modules/clinic/_testclinic_depr.c.h @@ -9,6 +9,7 @@ preserve #include "pycore_long.h" // _PyLong_UnsignedShort_Converter() #include "pycore_modsupport.h" // _PyArg_CheckPositional() #include "pycore_runtime.h" // _Py_ID() +#include "pycore_tuple.h" // _PyTuple_FromArray() PyDoc_STRVAR(depr_star_new__doc__, "DeprStarNew(a=None)\n" @@ -2393,4 +2394,4 @@ depr_multi(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject * exit: return return_value; } -/*[clinic end generated code: output=ca6da2c7137554be input=a9049054013a1b77]*/ +/*[clinic end generated code: output=5dda27c80df7351e input=a9049054013a1b77]*/ diff --git a/Modules/clinic/gcmodule.c.h b/Modules/clinic/gcmodule.c.h index be3bd35b4ffd44..34b2a79275c279 100644 --- a/Modules/clinic/gcmodule.c.h +++ b/Modules/clinic/gcmodule.c.h @@ -8,6 +8,7 @@ preserve #endif #include "pycore_abstract.h" // _Py_convert_optional_to_ssize_t() #include "pycore_modsupport.h" // _PyArg_UnpackKeywords() +#include "pycore_tuple.h" // _PyTuple_FromArray() PyDoc_STRVAR(gc_enable__doc__, "enable($module, /)\n" @@ -312,23 +313,24 @@ PyDoc_STRVAR(gc_get_referrers__doc__, {"get_referrers", _PyCFunction_CAST(gc_get_referrers), METH_FASTCALL, gc_get_referrers__doc__}, static PyObject * -gc_get_referrers_impl(PyObject *module, Py_ssize_t nargs, - PyObject *const *args); +gc_get_referrers_impl(PyObject *module, PyObject *objs); static PyObject * gc_get_referrers(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - Py_ssize_t nvararg = nargs - 0; - PyObject *const *__clinic_args = NULL; + PyObject *objs = NULL; - if (!_PyArg_CheckPositional("get_referrers", nargs, 0, PY_SSIZE_T_MAX)) { + objs = _PyTuple_FromArray(args, nargs); + if (objs == NULL) { goto exit; } - __clinic_args = args + 0; - return_value = gc_get_referrers_impl(module, nvararg, __clinic_args); + return_value = gc_get_referrers_impl(module, objs); exit: + /* Cleanup for objs */ + Py_XDECREF(objs); + return return_value; } @@ -342,23 +344,24 @@ PyDoc_STRVAR(gc_get_referents__doc__, {"get_referents", _PyCFunction_CAST(gc_get_referents), METH_FASTCALL, gc_get_referents__doc__}, static PyObject * -gc_get_referents_impl(PyObject *module, Py_ssize_t nargs, - PyObject *const *args); +gc_get_referents_impl(PyObject *module, PyObject *objs); static PyObject * gc_get_referents(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - Py_ssize_t nvararg = nargs - 0; - PyObject *const *__clinic_args = NULL; + PyObject *objs = NULL; - if (!_PyArg_CheckPositional("get_referents", nargs, 0, PY_SSIZE_T_MAX)) { + objs = _PyTuple_FromArray(args, nargs); + if (objs == NULL) { goto exit; } - __clinic_args = args + 0; - return_value = gc_get_referents_impl(module, nvararg, __clinic_args); + return_value = gc_get_referents_impl(module, objs); exit: + /* Cleanup for objs */ + Py_XDECREF(objs); + return return_value; } @@ -575,4 +578,4 @@ gc_get_freeze_count(PyObject *module, PyObject *Py_UNUSED(ignored)) exit: return return_value; } -/*[clinic end generated code: output=f488a0d4d6bd3687 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=4f35875870da17c9 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/mathmodule.c.h b/Modules/clinic/mathmodule.c.h index e4bda8a3e62aba..461f77183cd45b 100644 --- a/Modules/clinic/mathmodule.c.h +++ b/Modules/clinic/mathmodule.c.h @@ -18,22 +18,20 @@ PyDoc_STRVAR(math_gcd__doc__, {"gcd", _PyCFunction_CAST(math_gcd), METH_FASTCALL, math_gcd__doc__}, static PyObject * -math_gcd_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args); +math_gcd_impl(PyObject *module, PyObject * const *args, + Py_ssize_t args_length); static PyObject * math_gcd(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - Py_ssize_t nvararg = nargs - 0; - PyObject *const *__clinic_args = NULL; + PyObject * const *__clinic_args; + Py_ssize_t args_length; - if (!_PyArg_CheckPositional("gcd", nargs, 0, PY_SSIZE_T_MAX)) { - goto exit; - } - __clinic_args = args + 0; - return_value = math_gcd_impl(module, nvararg, __clinic_args); + __clinic_args = args; + args_length = nargs; + return_value = math_gcd_impl(module, __clinic_args, args_length); -exit: return return_value; } @@ -47,22 +45,20 @@ PyDoc_STRVAR(math_lcm__doc__, {"lcm", _PyCFunction_CAST(math_lcm), METH_FASTCALL, math_lcm__doc__}, static PyObject * -math_lcm_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args); +math_lcm_impl(PyObject *module, PyObject * const *args, + Py_ssize_t args_length); static PyObject * math_lcm(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - Py_ssize_t nvararg = nargs - 0; - PyObject *const *__clinic_args = NULL; + PyObject * const *__clinic_args; + Py_ssize_t args_length; - if (!_PyArg_CheckPositional("lcm", nargs, 0, PY_SSIZE_T_MAX)) { - goto exit; - } - __clinic_args = args + 0; - return_value = math_lcm_impl(module, nvararg, __clinic_args); + __clinic_args = args; + args_length = nargs; + return_value = math_lcm_impl(module, __clinic_args, args_length); -exit: return return_value; } @@ -430,22 +426,20 @@ PyDoc_STRVAR(math_hypot__doc__, {"hypot", _PyCFunction_CAST(math_hypot), METH_FASTCALL, math_hypot__doc__}, static PyObject * -math_hypot_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args); +math_hypot_impl(PyObject *module, PyObject * const *args, + Py_ssize_t args_length); static PyObject * math_hypot(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - Py_ssize_t nvararg = nargs - 0; - PyObject *const *__clinic_args = NULL; + PyObject * const *__clinic_args; + Py_ssize_t args_length; - if (!_PyArg_CheckPositional("hypot", nargs, 0, PY_SSIZE_T_MAX)) { - goto exit; - } - __clinic_args = args + 0; - return_value = math_hypot_impl(module, nvararg, __clinic_args); + __clinic_args = args; + args_length = nargs; + return_value = math_hypot_impl(module, __clinic_args, args_length); -exit: return return_value; } @@ -1109,4 +1103,4 @@ math_ulp(PyObject *module, PyObject *arg) exit: return return_value; } -/*[clinic end generated code: output=ff99a737c18d9210 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=cb506f61bc5ef862 input=a9049054013a1b77]*/ diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index f5fea5aa4dde08..ad13496b06deaf 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -216,31 +216,21 @@ gc_get_count_impl(PyObject *module) /*[clinic input] gc.get_referrers - *objs as args: object + *objs: tuple Return the list of objects that directly refer to any of 'objs'. [clinic start generated code]*/ static PyObject * -gc_get_referrers_impl(PyObject *module, Py_ssize_t nargs, - PyObject *const *args) -/*[clinic end generated code: output=1d44a7695ea25c40 input=bae96961b14a0922]*/ +gc_get_referrers_impl(PyObject *module, PyObject *objs) +/*[clinic end generated code: output=929d6dff26f609b9 input=9102be7ebee69ee3]*/ { - PyObject *varargs = _PyTuple_FromArray(args, nargs); - - if (!varargs) { - return NULL; - } - if (PySys_Audit("gc.get_referrers", "(O)", varargs) < 0) { - Py_DECREF(varargs); + if (PySys_Audit("gc.get_referrers", "(O)", objs) < 0) { return NULL; } PyInterpreterState *interp = _PyInterpreterState_GET(); - PyObject *result = _PyGC_GetReferrers(interp, varargs); - - Py_DECREF(varargs); - return result; + return _PyGC_GetReferrers(interp, objs); } /* Append obj to list; return true if error (out of memory), false if OK. */ @@ -274,43 +264,34 @@ append_referrents(PyObject *result, PyObject *args) /*[clinic input] gc.get_referents - *objs as args: object + *objs: tuple Return the list of objects that are directly referred to by 'objs'. [clinic start generated code]*/ static PyObject * -gc_get_referents_impl(PyObject *module, Py_ssize_t nargs, - PyObject *const *args) -/*[clinic end generated code: output=e459f3e8c0d19311 input=b3ceab0c34038cbf]*/ +gc_get_referents_impl(PyObject *module, PyObject *objs) +/*[clinic end generated code: output=6dfde40cd1588e1d input=55c078a6d0248fe0]*/ { - PyObject *varargs = _PyTuple_FromArray(args, nargs); - - if (!varargs) { - return NULL; - } - if (PySys_Audit("gc.get_referents", "(O)", varargs) < 0) { - Py_DECREF(varargs); + if (PySys_Audit("gc.get_referents", "(O)", objs) < 0) { return NULL; } PyInterpreterState *interp = _PyInterpreterState_GET(); PyObject *result = PyList_New(0); if (result == NULL) { - Py_DECREF(varargs); return NULL; } // NOTE: stop the world is a no-op in default build _PyEval_StopTheWorld(interp); - int err = append_referrents(result, varargs); + int err = append_referrents(result, objs); _PyEval_StartTheWorld(interp); if (err < 0) { Py_CLEAR(result); } - Py_DECREF(varargs); return result; } diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 77f50a2001634b..29638114dd94a9 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -722,22 +722,23 @@ m_log10(double x) /*[clinic input] math.gcd - *integers as args: object + *integers as args: array Greatest Common Divisor. [clinic start generated code]*/ static PyObject * -math_gcd_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args) -/*[clinic end generated code: output=b57687fcf431c1b8 input=94e675b7ceeaf0c9]*/ +math_gcd_impl(PyObject *module, PyObject * const *args, + Py_ssize_t args_length) +/*[clinic end generated code: output=a26c95907374ffb4 input=ded7f0ea3850c05c]*/ { // Fast-path for the common case: gcd(int, int) - if (nargs == 2 && PyLong_CheckExact(args[0]) && PyLong_CheckExact(args[1])) + if (args_length == 2 && PyLong_CheckExact(args[0]) && PyLong_CheckExact(args[1])) { return _PyLong_GCD(args[0], args[1]); } - if (nargs == 0) { + if (args_length == 0) { return PyLong_FromLong(0); } @@ -745,13 +746,13 @@ math_gcd_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args) if (res == NULL) { return NULL; } - if (nargs == 1) { + if (args_length == 1) { Py_SETREF(res, PyNumber_Absolute(res)); return res; } PyObject *one = _PyLong_GetOne(); // borrowed ref - for (Py_ssize_t i = 1; i < nargs; i++) { + for (Py_ssize_t i = 1; i < args_length; i++) { PyObject *x = _PyNumber_Index(args[i]); if (x == NULL) { Py_DECREF(res); @@ -804,32 +805,33 @@ long_lcm(PyObject *a, PyObject *b) /*[clinic input] math.lcm - *integers as args: object + *integers as args: array Least Common Multiple. [clinic start generated code]*/ static PyObject * -math_lcm_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args) -/*[clinic end generated code: output=f3eff0c25e4d7030 input=e64c33e85f4c47c6]*/ +math_lcm_impl(PyObject *module, PyObject * const *args, + Py_ssize_t args_length) +/*[clinic end generated code: output=c8a59a5c2e55c816 input=3e4f4b7cdf948a98]*/ { PyObject *res, *x; Py_ssize_t i; - if (nargs == 0) { + if (args_length == 0) { return PyLong_FromLong(1); } res = PyNumber_Index(args[0]); if (res == NULL) { return NULL; } - if (nargs == 1) { + if (args_length == 1) { Py_SETREF(res, PyNumber_Absolute(res)); return res; } PyObject *zero = _PyLong_GetZero(); // borrowed ref - for (i = 1; i < nargs; i++) { + for (i = 1; i < args_length; i++) { x = PyNumber_Index(args[i]); if (x == NULL) { Py_DECREF(res); @@ -2629,7 +2631,7 @@ math_dist_impl(PyObject *module, PyObject *p, PyObject *q) /*[clinic input] math.hypot - *coordinates as args: object + *coordinates as args: array Multidimensional Euclidean distance from the origin to a point. @@ -2646,8 +2648,9 @@ For example, the hypotenuse of a 3/4/5 right triangle is: [clinic start generated code]*/ static PyObject * -math_hypot_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args) -/*[clinic end generated code: output=dcb6d4b7a1102ee1 input=5c0061a2d11235ed]*/ +math_hypot_impl(PyObject *module, PyObject * const *args, + Py_ssize_t args_length) +/*[clinic end generated code: output=c9de404e24370068 input=1bceaf7d4fdcd9c2]*/ { Py_ssize_t i; PyObject *item; @@ -2657,13 +2660,13 @@ math_hypot_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args) double coord_on_stack[NUM_STACK_ELEMS]; double *coordinates = coord_on_stack; - if (nargs > NUM_STACK_ELEMS) { - coordinates = (double *) PyMem_Malloc(nargs * sizeof(double)); + if (args_length > NUM_STACK_ELEMS) { + coordinates = (double *) PyMem_Malloc(args_length * sizeof(double)); if (coordinates == NULL) { return PyErr_NoMemory(); } } - for (i = 0; i < nargs; i++) { + for (i = 0; i < args_length; i++) { item = args[i]; ASSIGN_DOUBLE(x, item, error_exit); x = fabs(x); @@ -2673,7 +2676,7 @@ math_hypot_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args) max = x; } } - result = vector_norm(nargs, coordinates, max, found_nan); + result = vector_norm(args_length, coordinates, max, found_nan); if (coordinates != coord_on_stack) { PyMem_Free(coordinates); } diff --git a/Objects/clinic/setobject.c.h b/Objects/clinic/setobject.c.h index d6e381a9975050..986993b4aa9bda 100644 --- a/Objects/clinic/setobject.c.h +++ b/Objects/clinic/setobject.c.h @@ -3,7 +3,6 @@ preserve [clinic start generated code]*/ #include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION() -#include "pycore_modsupport.h" // _PyArg_CheckPositional() PyDoc_STRVAR(set_pop__doc__, "pop($self, /)\n" @@ -41,22 +40,20 @@ PyDoc_STRVAR(set_update__doc__, {"update", _PyCFunction_CAST(set_update), METH_FASTCALL, set_update__doc__}, static PyObject * -set_update_impl(PySetObject *so, Py_ssize_t nargs, PyObject *const *args); +set_update_impl(PySetObject *so, PyObject * const *others, + Py_ssize_t others_length); static PyObject * set_update(PySetObject *so, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - Py_ssize_t nvararg = nargs - 0; - PyObject *const *__clinic_args = NULL; + PyObject * const *others; + Py_ssize_t others_length; - if (!_PyArg_CheckPositional("update", nargs, 0, PY_SSIZE_T_MAX)) { - goto exit; - } - __clinic_args = args + 0; - return_value = set_update_impl(so, nvararg, __clinic_args); + others = args; + others_length = nargs; + return_value = set_update_impl(so, others, others_length); -exit: return return_value; } @@ -142,22 +139,20 @@ PyDoc_STRVAR(set_union__doc__, {"union", _PyCFunction_CAST(set_union), METH_FASTCALL, set_union__doc__}, static PyObject * -set_union_impl(PySetObject *so, Py_ssize_t nargs, PyObject *const *args); +set_union_impl(PySetObject *so, PyObject * const *others, + Py_ssize_t others_length); static PyObject * set_union(PySetObject *so, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - Py_ssize_t nvararg = nargs - 0; - PyObject *const *__clinic_args = NULL; + PyObject * const *others; + Py_ssize_t others_length; - if (!_PyArg_CheckPositional("union", nargs, 0, PY_SSIZE_T_MAX)) { - goto exit; - } - __clinic_args = args + 0; - return_value = set_union_impl(so, nvararg, __clinic_args); + others = args; + others_length = nargs; + return_value = set_union_impl(so, others, others_length); -exit: return return_value; } @@ -171,23 +166,20 @@ PyDoc_STRVAR(set_intersection_multi__doc__, {"intersection", _PyCFunction_CAST(set_intersection_multi), METH_FASTCALL, set_intersection_multi__doc__}, static PyObject * -set_intersection_multi_impl(PySetObject *so, Py_ssize_t nargs, - PyObject *const *args); +set_intersection_multi_impl(PySetObject *so, PyObject * const *others, + Py_ssize_t others_length); static PyObject * set_intersection_multi(PySetObject *so, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - Py_ssize_t nvararg = nargs - 0; - PyObject *const *__clinic_args = NULL; + PyObject * const *others; + Py_ssize_t others_length; - if (!_PyArg_CheckPositional("intersection", nargs, 0, PY_SSIZE_T_MAX)) { - goto exit; - } - __clinic_args = args + 0; - return_value = set_intersection_multi_impl(so, nvararg, __clinic_args); + others = args; + others_length = nargs; + return_value = set_intersection_multi_impl(so, others, others_length); -exit: return return_value; } @@ -201,23 +193,20 @@ PyDoc_STRVAR(set_intersection_update_multi__doc__, {"intersection_update", _PyCFunction_CAST(set_intersection_update_multi), METH_FASTCALL, set_intersection_update_multi__doc__}, static PyObject * -set_intersection_update_multi_impl(PySetObject *so, Py_ssize_t nargs, - PyObject *const *args); +set_intersection_update_multi_impl(PySetObject *so, PyObject * const *others, + Py_ssize_t others_length); static PyObject * set_intersection_update_multi(PySetObject *so, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - Py_ssize_t nvararg = nargs - 0; - PyObject *const *__clinic_args = NULL; + PyObject * const *others; + Py_ssize_t others_length; - if (!_PyArg_CheckPositional("intersection_update", nargs, 0, PY_SSIZE_T_MAX)) { - goto exit; - } - __clinic_args = args + 0; - return_value = set_intersection_update_multi_impl(so, nvararg, __clinic_args); + others = args; + others_length = nargs; + return_value = set_intersection_update_multi_impl(so, others, others_length); -exit: return return_value; } @@ -255,23 +244,20 @@ PyDoc_STRVAR(set_difference_update__doc__, {"difference_update", _PyCFunction_CAST(set_difference_update), METH_FASTCALL, set_difference_update__doc__}, static PyObject * -set_difference_update_impl(PySetObject *so, Py_ssize_t nargs, - PyObject *const *args); +set_difference_update_impl(PySetObject *so, PyObject * const *others, + Py_ssize_t others_length); static PyObject * set_difference_update(PySetObject *so, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - Py_ssize_t nvararg = nargs - 0; - PyObject *const *__clinic_args = NULL; + PyObject * const *others; + Py_ssize_t others_length; - if (!_PyArg_CheckPositional("difference_update", nargs, 0, PY_SSIZE_T_MAX)) { - goto exit; - } - __clinic_args = args + 0; - return_value = set_difference_update_impl(so, nvararg, __clinic_args); + others = args; + others_length = nargs; + return_value = set_difference_update_impl(so, others, others_length); -exit: return return_value; } @@ -285,23 +271,20 @@ PyDoc_STRVAR(set_difference_multi__doc__, {"difference", _PyCFunction_CAST(set_difference_multi), METH_FASTCALL, set_difference_multi__doc__}, static PyObject * -set_difference_multi_impl(PySetObject *so, Py_ssize_t nargs, - PyObject *const *args); +set_difference_multi_impl(PySetObject *so, PyObject * const *others, + Py_ssize_t others_length); static PyObject * set_difference_multi(PySetObject *so, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - Py_ssize_t nvararg = nargs - 0; - PyObject *const *__clinic_args = NULL; + PyObject * const *others; + Py_ssize_t others_length; - if (!_PyArg_CheckPositional("difference", nargs, 0, PY_SSIZE_T_MAX)) { - goto exit; - } - __clinic_args = args + 0; - return_value = set_difference_multi_impl(so, nvararg, __clinic_args); + others = args; + others_length = nargs; + return_value = set_difference_multi_impl(so, others, others_length); -exit: return return_value; } @@ -536,4 +519,4 @@ set___sizeof__(PySetObject *so, PyObject *Py_UNUSED(ignored)) return return_value; } -/*[clinic end generated code: output=9d4b41191b2c602f input=a9049054013a1b77]*/ +/*[clinic end generated code: output=4b65e7709927f31f input=a9049054013a1b77]*/ diff --git a/Objects/clinic/typevarobject.c.h b/Objects/clinic/typevarobject.c.h index 0ba4ff48bc8804..3f439a78ded241 100644 --- a/Objects/clinic/typevarobject.c.h +++ b/Objects/clinic/typevarobject.c.h @@ -49,7 +49,7 @@ typevar_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[7]; + PyObject *argsbuf[6]; PyObject * const *fastargs; Py_ssize_t nargs = PyTuple_GET_SIZE(args); Py_ssize_t noptargs = Py_MIN(nargs, 1) + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 1; @@ -61,7 +61,7 @@ typevar_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) int contravariant = 0; int infer_variance = 0; - fastargs = _PyArg_UnpackKeywordsWithVararg(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 1, 0, 1, argsbuf); + fastargs = _PyArg_UnpackKeywordsWithVararg(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 1, 0, argsbuf); if (!fastargs) { goto exit; } @@ -70,24 +70,23 @@ typevar_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) goto exit; } name = fastargs[0]; - constraints = fastargs[1]; if (!noptargs) { goto skip_optional_kwonly; } - if (fastargs[2]) { - bound = fastargs[2]; + if (fastargs[1]) { + bound = fastargs[1]; if (!--noptargs) { goto skip_optional_kwonly; } } - if (fastargs[3]) { - default_value = fastargs[3]; + if (fastargs[2]) { + default_value = fastargs[2]; if (!--noptargs) { goto skip_optional_kwonly; } } - if (fastargs[4]) { - covariant = PyObject_IsTrue(fastargs[4]); + if (fastargs[3]) { + covariant = PyObject_IsTrue(fastargs[3]); if (covariant < 0) { goto exit; } @@ -95,8 +94,8 @@ typevar_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) goto skip_optional_kwonly; } } - if (fastargs[5]) { - contravariant = PyObject_IsTrue(fastargs[5]); + if (fastargs[4]) { + contravariant = PyObject_IsTrue(fastargs[4]); if (contravariant < 0) { goto exit; } @@ -104,15 +103,21 @@ typevar_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) goto skip_optional_kwonly; } } - infer_variance = PyObject_IsTrue(fastargs[6]); + infer_variance = PyObject_IsTrue(fastargs[5]); if (infer_variance < 0) { goto exit; } skip_optional_kwonly: + constraints = PyTuple_GetSlice(args, 1, PY_SSIZE_T_MAX); + if (!constraints) { + goto exit; + } return_value = typevar_new_impl(type, name, constraints, bound, default_value, covariant, contravariant, infer_variance); exit: + /* Cleanup for constraints */ Py_XDECREF(constraints); + return return_value; } @@ -695,4 +700,4 @@ typealias_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=73b39e550e4e336c input=a9049054013a1b77]*/ +/*[clinic end generated code: output=32b9e6ced80d3fb0 input=a9049054013a1b77]*/ diff --git a/Objects/setobject.c b/Objects/setobject.c index 66d7fc730c555c..2671792190d7a7 100644 --- a/Objects/setobject.c +++ b/Objects/setobject.c @@ -1054,19 +1054,20 @@ set_update_internal(PySetObject *so, PyObject *other) /*[clinic input] set.update so: setobject - *others as args: object + *others: array Update the set, adding elements from all others. [clinic start generated code]*/ static PyObject * -set_update_impl(PySetObject *so, Py_ssize_t nargs, PyObject *const *args) -/*[clinic end generated code: output=050e2a21f8d7d16a input=df4fe486e38cd337]*/ +set_update_impl(PySetObject *so, PyObject * const *others, + Py_ssize_t others_length) +/*[clinic end generated code: output=017c781c992d5c23 input=ed5d78885b076636]*/ { Py_ssize_t i; - for (i = 0; i < nargs; i++) { - PyObject *other = args[i]; + for (i = 0; i < others_length; i++) { + PyObject *other = others[i]; if (set_update_internal(so, other)) return NULL; } @@ -1283,14 +1284,15 @@ set_clear_impl(PySetObject *so) /*[clinic input] set.union so: setobject - *others as args: object + *others: array Return a new set with elements from the set and all others. [clinic start generated code]*/ static PyObject * -set_union_impl(PySetObject *so, Py_ssize_t nargs, PyObject *const *args) -/*[clinic end generated code: output=f68ec24d5c19d404 input=ddf088706e9577b2]*/ +set_union_impl(PySetObject *so, PyObject * const *others, + Py_ssize_t others_length) +/*[clinic end generated code: output=b1bfa3d74065f27e input=55a2e81db6347a4f]*/ { PySetObject *result; PyObject *other; @@ -1300,8 +1302,8 @@ set_union_impl(PySetObject *so, Py_ssize_t nargs, PyObject *const *args) if (result == NULL) return NULL; - for (i = 0; i < nargs; i++) { - other = args[i]; + for (i = 0; i < others_length; i++) { + other = others[i]; if ((PyObject *)so == other) continue; if (set_update_local(result, other)) { @@ -1434,25 +1436,25 @@ set_intersection(PySetObject *so, PyObject *other) /*[clinic input] set.intersection as set_intersection_multi so: setobject - *others as args: object + *others: array Return a new set with elements common to the set and all others. [clinic start generated code]*/ static PyObject * -set_intersection_multi_impl(PySetObject *so, Py_ssize_t nargs, - PyObject *const *args) -/*[clinic end generated code: output=ef0756ddb5f2dee9 input=0d9f3805ccbba6a4]*/ +set_intersection_multi_impl(PySetObject *so, PyObject * const *others, + Py_ssize_t others_length) +/*[clinic end generated code: output=db9ff9f875132b6b input=36c7b615694cadae]*/ { Py_ssize_t i; - if (nargs == 0) { + if (others_length == 0) { return set_copy(so, NULL); } PyObject *result = Py_NewRef(so); - for (i = 0; i < nargs; i++) { - PyObject *other = args[i]; + for (i = 0; i < others_length; i++) { + PyObject *other = others[i]; PyObject *newresult; Py_BEGIN_CRITICAL_SECTION2(result, other); newresult = set_intersection((PySetObject *)result, other); @@ -1482,19 +1484,19 @@ set_intersection_update(PySetObject *so, PyObject *other) /*[clinic input] set.intersection_update as set_intersection_update_multi so: setobject - *others as args: object + *others: array Update the set, keeping only elements found in it and all others. [clinic start generated code]*/ static PyObject * -set_intersection_update_multi_impl(PySetObject *so, Py_ssize_t nargs, - PyObject *const *args) -/*[clinic end generated code: output=808d7ad1935b1dfe input=223c1e086aa669a9]*/ +set_intersection_update_multi_impl(PySetObject *so, PyObject * const *others, + Py_ssize_t others_length) +/*[clinic end generated code: output=d768b5584675b48d input=782e422fc370e4fc]*/ { PyObject *tmp; - tmp = set_intersection_multi_impl(so, nargs, args); + tmp = set_intersection_multi_impl(so, others, others_length); if (tmp == NULL) return NULL; Py_BEGIN_CRITICAL_SECTION(so); @@ -1672,20 +1674,20 @@ set_difference_update_internal(PySetObject *so, PyObject *other) /*[clinic input] set.difference_update so: setobject - *others as args: object + *others: array Update the set, removing elements found in others. [clinic start generated code]*/ static PyObject * -set_difference_update_impl(PySetObject *so, Py_ssize_t nargs, - PyObject *const *args) -/*[clinic end generated code: output=55f850c27748d312 input=024e6baa6fbcbb3d]*/ +set_difference_update_impl(PySetObject *so, PyObject * const *others, + Py_ssize_t others_length) +/*[clinic end generated code: output=04a22179b322cfe6 input=93ac28ba5b233696]*/ { Py_ssize_t i; - for (i = 0; i < nargs; i++) { - PyObject *other = args[i]; + for (i = 0; i < others_length; i++) { + PyObject *other = others[i]; int rv; Py_BEGIN_CRITICAL_SECTION2(so, other); rv = set_difference_update_internal(so, other); @@ -1790,32 +1792,32 @@ set_difference(PySetObject *so, PyObject *other) /*[clinic input] set.difference as set_difference_multi so: setobject - *others as args: object + *others: array Return a new set with elements in the set that are not in the others. [clinic start generated code]*/ static PyObject * -set_difference_multi_impl(PySetObject *so, Py_ssize_t nargs, - PyObject *const *args) -/*[clinic end generated code: output=8150d008c00523f3 input=ba78ea5f099e58df]*/ +set_difference_multi_impl(PySetObject *so, PyObject * const *others, + Py_ssize_t others_length) +/*[clinic end generated code: output=b0d33fb05d5477a7 input=c1eb448d483416ad]*/ { Py_ssize_t i; PyObject *result, *other; - if (nargs == 0) { + if (others_length == 0) { return set_copy(so, NULL); } - other = args[0]; + other = others[0]; Py_BEGIN_CRITICAL_SECTION2(so, other); result = set_difference(so, other); Py_END_CRITICAL_SECTION2(); if (result == NULL) return NULL; - for (i = 1; i < nargs; i++) { - other = args[i]; + for (i = 1; i < others_length; i++) { + other = others[i]; int rv; Py_BEGIN_CRITICAL_SECTION(other); rv = set_difference_update_internal((PySetObject *)result, other); diff --git a/Python/clinic/bltinmodule.c.h b/Python/clinic/bltinmodule.c.h index f75a8d4ac0ccd4..b47279686cc2ed 100644 --- a/Python/clinic/bltinmodule.c.h +++ b/Python/clinic/bltinmodule.c.h @@ -7,6 +7,7 @@ preserve # include "pycore_runtime.h" // _Py_ID() #endif #include "pycore_modsupport.h" // _PyArg_UnpackKeywords() +#include "pycore_tuple.h" // _PyTuple_FromArray() PyDoc_STRVAR(builtin___import____doc__, "__import__($module, /, name, globals=None, locals=None, fromlist=(),\n" @@ -933,7 +934,8 @@ builtin_print(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[5]; + PyObject *argsbuf[4]; + PyObject * const *fastargs; Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; PyObject *__clinic_args = NULL; PyObject *sep = Py_None; @@ -941,41 +943,46 @@ builtin_print(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec PyObject *file = Py_None; int flush = 0; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, 0, argsbuf); - if (!args) { + fastargs = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, argsbuf); + if (!fastargs) { goto exit; } - __clinic_args = args[0]; if (!noptargs) { goto skip_optional_kwonly; } - if (args[1]) { - sep = args[1]; + if (fastargs[0]) { + sep = fastargs[0]; if (!--noptargs) { goto skip_optional_kwonly; } } - if (args[2]) { - end = args[2]; + if (fastargs[1]) { + end = fastargs[1]; if (!--noptargs) { goto skip_optional_kwonly; } } - if (args[3]) { - file = args[3]; + if (fastargs[2]) { + file = fastargs[2]; if (!--noptargs) { goto skip_optional_kwonly; } } - flush = PyObject_IsTrue(args[4]); + flush = PyObject_IsTrue(fastargs[3]); if (flush < 0) { goto exit; } skip_optional_kwonly: + __clinic_args = _PyTuple_FromArray(args, nargs); + if (__clinic_args == NULL) { + goto exit; + } return_value = builtin_print_impl(module, __clinic_args, sep, end, file, flush); exit: + /* Cleanup for args */ Py_XDECREF(__clinic_args); + return return_value; } @@ -1228,4 +1235,4 @@ builtin_issubclass(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=435d3f286a863c49 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=76b27cf4164f257e input=a9049054013a1b77]*/ diff --git a/Python/getargs.c b/Python/getargs.c index a764343ea9ee3f..d529994ad3ac0d 100644 --- a/Python/getargs.c +++ b/Python/getargs.c @@ -2308,13 +2308,11 @@ vgetargskeywordsfast(PyObject *args, PyObject *keywords, } -#undef _PyArg_UnpackKeywords - PyObject * const * -_PyArg_UnpackKeywords(PyObject *const *args, Py_ssize_t nargs, +_PyArg_UnpackKeywordsEx(PyObject *const *args, Py_ssize_t nargs, PyObject *kwargs, PyObject *kwnames, struct _PyArg_Parser *parser, - int minpos, int maxpos, int minkw, + int minpos, int maxpos, int minkw, int varpos, PyObject **buf) { PyObject *kwtuple; @@ -2360,11 +2358,11 @@ _PyArg_UnpackKeywords(PyObject *const *args, Py_ssize_t nargs, else { nkwargs = 0; } - if (nkwargs == 0 && minkw == 0 && minpos <= nargs && nargs <= maxpos) { + if (nkwargs == 0 && minkw == 0 && minpos <= nargs && (varpos || nargs <= maxpos)) { /* Fast path. */ return args; } - if (nargs + nkwargs > maxargs) { + if (!varpos && nargs + nkwargs > maxargs) { /* Adding "keyword" (when nargs == 0) prevents producing wrong error messages in some special cases (see bpo-31229). */ PyErr_Format(PyExc_TypeError, @@ -2377,7 +2375,7 @@ _PyArg_UnpackKeywords(PyObject *const *args, Py_ssize_t nargs, nargs + nkwargs); return NULL; } - if (nargs > maxpos) { + if (!varpos && nargs > maxpos) { if (maxpos == 0) { PyErr_Format(PyExc_TypeError, "%.200s%s takes no positional arguments", @@ -2402,13 +2400,16 @@ _PyArg_UnpackKeywords(PyObject *const *args, Py_ssize_t nargs, " (%zd given)", (parser->fname == NULL) ? "function" : parser->fname, (parser->fname == NULL) ? "" : "()", - minposonly < maxpos ? "at least" : "exactly", + (varpos || minposonly < maxpos) ? "at least" : "exactly", minposonly, minposonly == 1 ? "" : "s", nargs); return NULL; } + if (varpos) { + nargs = Py_MIN(maxpos, nargs); + } /* copy tuple args */ for (i = 0; i < nargs; i++) { buf[i] = args[i]; @@ -2486,157 +2487,6 @@ _PyArg_UnpackKeywords(PyObject *const *args, Py_ssize_t nargs, return buf; } -PyObject * const * -_PyArg_UnpackKeywordsWithVararg(PyObject *const *args, Py_ssize_t nargs, - PyObject *kwargs, PyObject *kwnames, - struct _PyArg_Parser *parser, - int minpos, int maxpos, int minkw, - int vararg, PyObject **buf) -{ - PyObject *kwtuple; - PyObject *keyword; - Py_ssize_t varargssize = 0; - int i, posonly, minposonly, maxargs; - int reqlimit = minkw ? maxpos + minkw : minpos; - Py_ssize_t nkwargs; - PyObject * const *kwstack = NULL; - - assert(kwargs == NULL || PyDict_Check(kwargs)); - assert(kwargs == NULL || kwnames == NULL); - - if (parser == NULL) { - PyErr_BadInternalCall(); - return NULL; - } - - if (kwnames != NULL && !PyTuple_Check(kwnames)) { - PyErr_BadInternalCall(); - return NULL; - } - - if (args == NULL && nargs == 0) { - args = buf; - } - - if (parser_init(parser) < 0) { - return NULL; - } - - kwtuple = parser->kwtuple; - posonly = parser->pos; - minposonly = Py_MIN(posonly, minpos); - maxargs = posonly + (int)PyTuple_GET_SIZE(kwtuple); - if (kwargs != NULL) { - nkwargs = PyDict_GET_SIZE(kwargs); - } - else if (kwnames != NULL) { - nkwargs = PyTuple_GET_SIZE(kwnames); - kwstack = args + nargs; - } - else { - nkwargs = 0; - } - if (nargs < minposonly) { - PyErr_Format(PyExc_TypeError, - "%.200s%s takes %s %d positional argument%s" - " (%zd given)", - (parser->fname == NULL) ? "function" : parser->fname, - (parser->fname == NULL) ? "" : "()", - minposonly < maxpos ? "at least" : "exactly", - minposonly, - minposonly == 1 ? "" : "s", - nargs); - return NULL; - } - - /* create varargs tuple */ - varargssize = nargs - maxpos; - if (varargssize < 0) { - varargssize = 0; - } - buf[vararg] = PyTuple_New(varargssize); - if (!buf[vararg]) { - return NULL; - } - - /* copy tuple args */ - for (i = 0; i < nargs; i++) { - if (i >= vararg) { - PyTuple_SET_ITEM(buf[vararg], i - vararg, Py_NewRef(args[i])); - continue; - } - else { - buf[i] = args[i]; - } - } - - /* copy keyword args using kwtuple to drive process */ - for (i = Py_MAX((int)nargs, posonly) - Py_SAFE_DOWNCAST(varargssize, Py_ssize_t, int); i < maxargs; i++) { - PyObject *current_arg; - if (nkwargs) { - keyword = PyTuple_GET_ITEM(kwtuple, i - posonly); - if (kwargs != NULL) { - if (PyDict_GetItemRef(kwargs, keyword, ¤t_arg) < 0) { - goto exit; - } - } - else { - current_arg = find_keyword(kwnames, kwstack, keyword); - } - } - else { - current_arg = NULL; - } - - /* If an arguments is passed in as a keyword argument, - * it should be placed before `buf[vararg]`. - * - * For example: - * def f(a, /, b, *args): - * pass - * f(1, b=2) - * - * This `buf` array should be: [1, 2, NULL]. - * In this case, nargs < vararg. - * - * Otherwise, we leave a place at `buf[vararg]` for vararg tuple - * so the index is `i + 1`. */ - if (i < vararg) { - buf[i] = current_arg; - } - else { - buf[i + 1] = current_arg; - } - - if (current_arg) { - Py_DECREF(current_arg); - --nkwargs; - } - else if (i < minpos || (maxpos <= i && i < reqlimit)) { - /* Less arguments than required */ - keyword = PyTuple_GET_ITEM(kwtuple, i - posonly); - PyErr_Format(PyExc_TypeError, "%.200s%s missing required " - "argument '%U' (pos %d)", - (parser->fname == NULL) ? "function" : parser->fname, - (parser->fname == NULL) ? "" : "()", - keyword, i+1); - goto exit; - } - } - - if (nkwargs > 0) { - error_unexpected_keyword_arg(kwargs, kwnames, kwtuple, parser->fname); - goto exit; - } - - return buf; - -exit: - Py_XDECREF(buf[vararg]); - return NULL; -} - - static const char * skipitem(const char **p_format, va_list *p_va, int flags) { diff --git a/Tools/c-analyzer/c_parser/parser/__init__.py b/Tools/c-analyzer/c_parser/parser/__init__.py index 4227e938d7f8da..ff4f303c4a2bec 100644 --- a/Tools/c-analyzer/c_parser/parser/__init__.py +++ b/Tools/c-analyzer/c_parser/parser/__init__.py @@ -164,7 +164,7 @@ def _parse(srclines, anon_name, **srckwargs): # We use defaults that cover most files. Files with bigger declarations # are covered elsewhere (MAX_SIZES in cpython/_parser.py). -def _iter_source(lines, *, maxtext=10_000, maxlines=200, showtext=False): +def _iter_source(lines, *, maxtext=11_000, maxlines=200, showtext=False): maxtext = maxtext if maxtext and maxtext > 0 else None maxlines = maxlines if maxlines and maxlines > 0 else None filestack = [] diff --git a/Tools/clinic/libclinic/clanguage.py b/Tools/clinic/libclinic/clanguage.py index 32aba81ab8a850..32d2c045b06bca 100644 --- a/Tools/clinic/libclinic/clanguage.py +++ b/Tools/clinic/libclinic/clanguage.py @@ -15,7 +15,7 @@ Module, Class, Function, Parameter, permute_optional_groups, GETTER, SETTER, METHOD_INIT) -from libclinic.converters import defining_class_converter, self_converter +from libclinic.converters import self_converter from libclinic.parse_args import ParseArgsCodeGen if TYPE_CHECKING: from libclinic.app import Clinic @@ -396,12 +396,6 @@ def render_function( first_optional = len(selfless) positional = selfless and selfless[-1].is_positional_only() has_option_groups = False - requires_defining_class = (len(selfless) - and isinstance(selfless[0].converter, - defining_class_converter)) - pass_vararg_directly = (all(p.is_positional_only() or p.is_vararg() - for p in selfless) - and not requires_defining_class) # offset i by -1 because first_optional needs to ignore self for i, p in enumerate(parameters, -1): @@ -410,9 +404,6 @@ def render_function( if (i != -1) and (p.default is not unspecified): first_optional = min(first_optional, i) - if p.is_vararg() and not pass_vararg_directly: - data.cleanup.append(f"Py_XDECREF({c.parser_name});") - # insert group variable group = p.group if last_group != group: @@ -424,11 +415,6 @@ def render_function( data.impl_parameters.append("int " + group_name) has_option_groups = True - if p.is_vararg() and pass_vararg_directly: - data.impl_arguments.append('nvararg') - data.impl_parameters.append('Py_ssize_t nargs') - p.converter.type = 'PyObject *const *' - c.render(p, data) if has_option_groups and (not positional): diff --git a/Tools/clinic/libclinic/converter.py b/Tools/clinic/libclinic/converter.py index 86853bb4fba253..2c93dda3541030 100644 --- a/Tools/clinic/libclinic/converter.py +++ b/Tools/clinic/libclinic/converter.py @@ -312,7 +312,7 @@ def render(self, parameter: Parameter, data: CRenderData) -> None: def length_name(self) -> str: """Computes the name of the associated "length" variable.""" assert self.length is not None - return self.parser_name + "_length" + return self.name + "_length" # Why is this one broken out separately? # For "positional-only" function parsing, diff --git a/Tools/clinic/libclinic/converters.py b/Tools/clinic/libclinic/converters.py index bd5c2a2b73b94a..2d103c941cbf23 100644 --- a/Tools/clinic/libclinic/converters.py +++ b/Tools/clinic/libclinic/converters.py @@ -1228,3 +1228,30 @@ def set_template_dict(self, template_dict: TemplateDict) -> None: type_object = cls.type_object type_ptr = f'PyTypeObject *base_tp = {type_object};' template_dict['base_type_ptr'] = type_ptr + + +# Converters for var-positional parameter. + +class varpos_tuple_converter(CConverter): + type = 'PyObject *' + format_unit = '' + c_default = 'NULL' + + def cleanup(self) -> str: + return f"""Py_XDECREF({self.parser_name});\n""" + + def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: + raise AssertionError('should never be called') + +class varpos_array_converter(CConverter): + type = 'PyObject * const *' + format_unit = '' + length = True + c_ignored_default = '' + + def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: + raise AssertionError('should never be called') + +# XXX: temporary +class varpos_object_converter(varpos_tuple_converter): + pass diff --git a/Tools/clinic/libclinic/dsl_parser.py b/Tools/clinic/libclinic/dsl_parser.py index 5ca3bd5cb6c3f1..4b4a8b9969d142 100644 --- a/Tools/clinic/libclinic/dsl_parser.py +++ b/Tools/clinic/libclinic/dsl_parser.py @@ -925,16 +925,17 @@ def parse_parameter(self, line: str) -> None: parameter_name = parameter.arg name, legacy, kwargs = self.parse_converter(parameter.annotation) + if is_vararg: + name = 'varpos_' + name value: object if not default: - if self.parameter_state is ParamState.OPTIONAL: - fail(f"Can't have a parameter without a default ({parameter_name!r}) " - "after a parameter with a default!") if is_vararg: value = NULL - kwargs.setdefault('c_default', "NULL") else: + if self.parameter_state is ParamState.OPTIONAL: + fail(f"Can't have a parameter without a default ({parameter_name!r}) " + "after a parameter with a default!") value = unspecified if 'py_default' in kwargs: fail("You can't specify py_default without specifying a default value!") diff --git a/Tools/clinic/libclinic/parse_args.py b/Tools/clinic/libclinic/parse_args.py index 559d4fbdd09c57..2ce4e7512148d2 100644 --- a/Tools/clinic/libclinic/parse_args.py +++ b/Tools/clinic/libclinic/parse_args.py @@ -217,8 +217,7 @@ class ParseArgsCodeGen: min_pos: int = 0 max_pos: int = 0 min_kw_only: int = 0 - pseudo_args: int = 0 - vararg: int | str = NO_VARARG + varpos: Parameter | None = None docstring_prototype: str docstring_definition: str @@ -246,6 +245,13 @@ def __init__(self, func: Function, codegen: CodeGen) -> None: if self.parameters and isinstance(self.parameters[0].converter, defining_class_converter): self.requires_defining_class = True del self.parameters[0] + + for i, p in enumerate(self.parameters): + if p.is_vararg(): + self.varpos = p + del self.parameters[i] + break + self.converters = [p.converter for p in self.parameters] if self.func.critical_section: @@ -257,18 +263,13 @@ def __init__(self, func: Function, codegen: CodeGen) -> None: self.min_pos = 0 self.max_pos = 0 self.min_kw_only = 0 - self.pseudo_args = 0 for i, p in enumerate(self.parameters, 1): if p.is_keyword_only(): assert not p.is_positional_only() if not p.is_optional(): - self.min_kw_only = i - self.max_pos - int(self.vararg != NO_VARARG) - elif p.is_vararg(): - self.pseudo_args += 1 - self.vararg = i - 1 + self.min_kw_only = i - self.max_pos else: - if self.vararg == NO_VARARG: - self.max_pos = i + self.max_pos = i if p.is_positional_only(): self.pos_only = i if not p.is_optional(): @@ -285,6 +286,7 @@ def use_meth_o(self) -> bool: return (len(self.parameters) == 1 and self.parameters[0].is_positional_only() and not self.converters[0].is_optional() + and not self.varpos and not self.requires_defining_class and not self.is_new_or_init()) @@ -315,7 +317,8 @@ def select_prototypes(self) -> None: def init_limited_capi(self) -> None: self.limited_capi = self.codegen.limited_capi - if self.limited_capi and (self.pseudo_args or + if self.limited_capi and ( + (self.varpos and self.pos_only < len(self.parameters)) or (any(p.is_optional() for p in self.parameters) and any(p.is_keyword_only() and not p.is_optional() for p in self.parameters)) or any(c.broken_limited_capi for c in self.converters)): @@ -447,6 +450,74 @@ def parse_option_groups(self) -> None: parser_code = ' {option_group_parsing}' self.parser_body(parser_code) + def _parse_vararg(self) -> str: + assert self.varpos is not None + paramname = self.varpos.converter.parser_name + if self.varpos.converter.length: + if not self.fastcall: + self.codegen.add_include('pycore_tuple.h', + '_PyTuple_ITEMS()') + start = 'args' if self.fastcall else '_PyTuple_ITEMS(args)' + size = 'nargs' if self.fastcall else 'PyTuple_GET_SIZE(args)' + if self.max_pos: + if min(self.pos_only, self.min_pos) < self.max_pos: + start = f'{size} > {self.max_pos} ? {start} + {self.max_pos} : {start}' + size = f'Py_MAX(0, {size} - {self.max_pos})' + else: + start = f'{start} + {self.max_pos}' + size = f'{size} - {self.max_pos}' + return f""" + {paramname} = {start}; + {self.varpos.converter.length_name} = {size}; + """ + + if self.fastcall: + if self.limited_capi: + if min(self.pos_only, self.min_pos) < self.max_pos: + size = f'Py_MAX(nargs - {self.max_pos}, 0)' + else: + size = f'nargs - {self.max_pos}' if self.max_pos else 'nargs' + return f""" + {paramname} = PyTuple_New({size}); + if (!{paramname}) {{{{ + goto exit; + }}}} + for (Py_ssize_t i = {self.max_pos}; i < nargs; ++i) {{{{ + PyTuple_SET_ITEM({paramname}, i - {self.max_pos}, Py_NewRef(args[i])); + }}}} + """ + else: + self.codegen.add_include('pycore_tuple.h', + '_PyTuple_FromArray()') + if min(self.pos_only, self.min_pos) < self.max_pos: + return f""" + {paramname} = nargs > {self.max_pos} + ? _PyTuple_FromArray(args + {self.max_pos}, nargs - {self.max_pos}) + : PyTuple_New(0); + if ({paramname} == NULL) {{{{ + goto exit; + }}}} + """ + else: + start = f'args + {self.max_pos}' if self.max_pos else 'args' + size = f'nargs - {self.max_pos}' if self.max_pos else 'nargs' + return f""" + {paramname} = _PyTuple_FromArray({start}, {size}); + if ({paramname} == NULL) {{{{ + goto exit; + }}}} + """ + else: + if self.max_pos: + return f""" + {paramname} = PyTuple_GetSlice(args, {self.max_pos}, PY_SSIZE_T_MAX); + if (!{paramname}) {{{{ + goto exit; + }}}} + """ + else: + return f"{paramname} = Py_NewRef(args);\n" + def parse_pos_only(self) -> None: if self.fastcall: # positional-only, but no option groups @@ -469,14 +540,9 @@ def parse_pos_only(self) -> None: nargs = 'PyTuple_GET_SIZE(args)' argname_fmt = 'PyTuple_GET_ITEM(args, %d)' - if self.vararg != NO_VARARG: - self.declarations = f"Py_ssize_t nvararg = {nargs} - {self.max_pos};" - else: - self.declarations = "" - - max_args = NO_VARARG if (self.vararg != NO_VARARG) else self.max_pos + parser_code = [] + max_args = NO_VARARG if self.varpos else self.max_pos if self.limited_capi: - parser_code = [] if nargs != 'nargs': nargs_def = f'Py_ssize_t nargs = {nargs};' parser_code.append(libclinic.normalize_snippet(nargs_def, indent=4)) @@ -509,33 +575,27 @@ def parse_pos_only(self) -> None: }}}} """, indent=4)) - else: + elif self.min_pos or max_args != NO_VARARG: self.codegen.add_include('pycore_modsupport.h', '_PyArg_CheckPositional()') - parser_code = [libclinic.normalize_snippet(f""" + parser_code.append(libclinic.normalize_snippet(f""" if (!_PyArg_CheckPositional("{{name}}", {nargs}, {self.min_pos}, {max_args})) {{{{ goto exit; }}}} - """, indent=4)] + """, indent=4)) has_optional = False use_parser_code = True for i, p in enumerate(self.parameters): - if p.is_vararg(): - var = p.converter.parser_name - if self.fastcall: - code = f"{var} = args + {self.vararg};" - else: - code = f"{var} = _PyTuple_CAST(args)->ob_item;" - formatted_code = libclinic.normalize_snippet(code, indent=4) - parser_code.append(formatted_code) - continue - displayname = p.get_displayname(i+1) argname = argname_fmt % i parsearg: str | None parsearg = p.converter.parse_arg(argname, displayname, limited_capi=self.limited_capi) if parsearg is None: + if self.varpos: + raise ValueError( + f"Using converter {p.converter} is not supported " + f"in function with var-positional parameter") use_parser_code = False parser_code = [] break @@ -551,6 +611,8 @@ def parse_pos_only(self) -> None: if use_parser_code: if has_optional: parser_code.append("skip_optional:") + if self.varpos: + parser_code.append(libclinic.normalize_snippet(self._parse_vararg(), indent=4)) else: for parameter in self.parameters: parameter.converter.use_converter() @@ -575,7 +637,7 @@ def parse_pos_only(self) -> None: goto exit; }} """, indent=4)] - self.parser_body(*parser_code, declarations=self.declarations) + self.parser_body(*parser_code) def parse_general(self, clang: CLanguage) -> None: parsearg: str | None @@ -589,7 +651,7 @@ def parse_general(self, clang: CLanguage) -> None: has_optional_kw = ( max(self.pos_only, self.min_pos) + self.min_kw_only - < len(self.converters) - int(self.vararg != NO_VARARG) + < len(self.converters) ) use_parser_code = True @@ -598,57 +660,53 @@ def parse_general(self, clang: CLanguage) -> None: use_parser_code = False self.fastcall = False else: - if self.vararg == NO_VARARG: + if not self.varpos: self.codegen.add_include('pycore_modsupport.h', '_PyArg_UnpackKeywords()') - args_declaration = "_PyArg_UnpackKeywords", "%s, %s, %s" % ( - self.min_pos, - self.max_pos, - self.min_kw_only - ) + unpack_func = '_PyArg_UnpackKeywords' nargs = "nargs" else: self.codegen.add_include('pycore_modsupport.h', '_PyArg_UnpackKeywordsWithVararg()') - args_declaration = "_PyArg_UnpackKeywordsWithVararg", "%s, %s, %s, %s" % ( - self.min_pos, - self.max_pos, - self.min_kw_only, - self.vararg - ) + unpack_func = '_PyArg_UnpackKeywordsWithVararg' nargs = f"Py_MIN(nargs, {self.max_pos})" if self.max_pos else "0" if self.fastcall: self.flags = "METH_FASTCALL|METH_KEYWORDS" self.parser_prototype = PARSER_PROTOTYPE_FASTCALL_KEYWORDS - argname_fmt = 'args[%d]' self.declarations = declare_parser(self.func, codegen=self.codegen) - self.declarations += "\nPyObject *argsbuf[%s];" % len(self.converters) + self.declarations += "\nPyObject *argsbuf[%s];" % (len(self.converters) or 1) + if self.varpos: + self.declarations += "\nPyObject * const *fastargs;" + argsname = 'fastargs' + argname_fmt = 'fastargs[%d]' + else: + argsname = 'args' + argname_fmt = 'args[%d]' if has_optional_kw: self.declarations += "\nPy_ssize_t noptargs = %s + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - %d;" % (nargs, self.min_pos + self.min_kw_only) - parser_code = [libclinic.normalize_snippet(""" - args = %s(args, nargs, NULL, kwnames, &_parser, %s, argsbuf); - if (!args) {{ - goto exit; - }} - """ % args_declaration, indent=4)] + unpack_args = 'args, nargs, NULL, kwnames' else: # positional-or-keyword arguments self.flags = "METH_VARARGS|METH_KEYWORDS" self.parser_prototype = PARSER_PROTOTYPE_KEYWORD + argsname = 'fastargs' argname_fmt = 'fastargs[%d]' self.declarations = declare_parser(self.func, codegen=self.codegen) - self.declarations += "\nPyObject *argsbuf[%s];" % len(self.converters) + self.declarations += "\nPyObject *argsbuf[%s];" % (len(self.converters) or 1) self.declarations += "\nPyObject * const *fastargs;" self.declarations += "\nPy_ssize_t nargs = PyTuple_GET_SIZE(args);" if has_optional_kw: self.declarations += "\nPy_ssize_t noptargs = %s + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - %d;" % (nargs, self.min_pos + self.min_kw_only) - parser_code = [libclinic.normalize_snippet(""" - fastargs = %s(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, %s, argsbuf); - if (!fastargs) {{ - goto exit; - }} - """ % args_declaration, indent=4)] + unpack_args = '_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL' + unpack_args += (f', &_parser, {self.min_pos}, {self.max_pos}, ' + f'{self.min_kw_only}, argsbuf') + parser_code = [libclinic.normalize_snippet(f""" + {argsname} = {unpack_func}({unpack_args}); + if (!{argsname}) {{{{ + goto exit; + }}}} + """, indent=4)] if self.requires_defining_class: self.flags = 'METH_METHOD|' + self.flags @@ -697,8 +755,6 @@ def parse_general(self, clang: CLanguage) -> None: else: label = 'skip_optional_kwonly' first_opt = self.max_pos + self.min_kw_only - if self.vararg != NO_VARARG: - first_opt += 1 if i == first_opt: add_label = label parser_code.append(libclinic.normalize_snippet(""" @@ -724,6 +780,8 @@ def parse_general(self, clang: CLanguage) -> None: if use_parser_code: if add_label: parser_code.append("%s:" % add_label) + if self.varpos: + parser_code.append(libclinic.normalize_snippet(self._parse_vararg(), indent=4)) else: for parameter in self.parameters: parameter.converter.use_converter() @@ -914,14 +972,14 @@ def parse_args(self, clang: CLanguage) -> dict[str, str]: # previous call to parser_body. this is used for an awful hack. self.parser_body_fields: tuple[str, ...] = () - if not self.parameters: + if not self.parameters and not self.varpos: self.parse_no_args() elif self.use_meth_o(): self.parse_one_arg() elif self.has_option_groups(): self.parse_option_groups() elif (not self.requires_defining_class - and self.pos_only == len(self.parameters) - self.pseudo_args): + and self.pos_only == len(self.parameters)): self.parse_pos_only() else: self.parse_general(clang) From bbe9b21d06c192a616bc1720ec8f7d4ccc16cab8 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Thu, 7 Nov 2024 13:40:56 -0800 Subject: [PATCH 099/219] GH-123877: default to `wasm32-wasip1` instead of `wasm32-wasi` to be more specific (GH-126552) Eventually wasm32-wasi will represent WASI 1.0, and so it's currently deprecated so it can be used for that eventual purpose. wasm32-wasip1 is also more specific to what version of WASI is currently supported. --------- Co-authored-by: Erlend E. Aasland --- .github/workflows/reusable-wasi.yml | 4 ++-- .../Build/2024-11-07-11-09-31.gh-issue-123877.CVdd0b.rst | 3 +++ Tools/wasm/wasi.py | 2 +- configure | 6 +++--- configure.ac | 6 +++--- 5 files changed, 12 insertions(+), 9 deletions(-) create mode 100644 Misc/NEWS.d/next/Build/2024-11-07-11-09-31.gh-issue-123877.CVdd0b.rst diff --git a/.github/workflows/reusable-wasi.yml b/.github/workflows/reusable-wasi.yml index 85af793c342c51..3f96c888e2dd30 100644 --- a/.github/workflows/reusable-wasi.yml +++ b/.github/workflows/reusable-wasi.yml @@ -17,7 +17,7 @@ jobs: WASI_SDK_VERSION: 24 WASI_SDK_PATH: /opt/wasi-sdk CROSS_BUILD_PYTHON: cross-build/build - CROSS_BUILD_WASI: cross-build/wasm32-wasi + CROSS_BUILD_WASI: cross-build/wasm32-wasip1 steps: - uses: actions/checkout@v4 # No problem resolver registered as one doesn't currently exist for Clang. @@ -31,7 +31,7 @@ jobs: with: path: ${{ env.WASI_SDK_PATH }} key: ${{ runner.os }}-wasi-sdk-${{ env.WASI_SDK_VERSION }} - - name: "Install WASI SDK" + - name: "Install WASI SDK" # Hard-coded to x64. if: steps.cache-wasi-sdk.outputs.cache-hit != 'true' run: | mkdir ${{ env.WASI_SDK_PATH }} && \ diff --git a/Misc/NEWS.d/next/Build/2024-11-07-11-09-31.gh-issue-123877.CVdd0b.rst b/Misc/NEWS.d/next/Build/2024-11-07-11-09-31.gh-issue-123877.CVdd0b.rst new file mode 100644 index 00000000000000..080d2f2ab12af9 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2024-11-07-11-09-31.gh-issue-123877.CVdd0b.rst @@ -0,0 +1,3 @@ +Use ``wasm32-wasip1`` as the target triple for WASI instead of +``wasm32-wasi``. The latter will eventually be reclaimed for WASI 1.0 while +CPython currently only supports WASI preview1. diff --git a/Tools/wasm/wasi.py b/Tools/wasm/wasi.py index 050e3723feb247..ac36d55587a38f 100644 --- a/Tools/wasm/wasi.py +++ b/Tools/wasm/wasi.py @@ -346,7 +346,7 @@ def main(): "(default designed for wasmtime 14 or newer: " f"`{default_host_runner}`)") for subcommand in build, configure_host, make_host: - subcommand.add_argument("--host-triple", action="store", default="wasm32-wasi", + subcommand.add_argument("--host-triple", action="store", default="wasm32-wasip1", help="The target triple for the WASI host build") context = parser.parse_args() diff --git a/configure b/configure index 1d5c0941247c30..71ffe9ca1c841e 100755 --- a/configure +++ b/configure @@ -4062,7 +4062,7 @@ then *-*-emscripten) ac_sys_system=Emscripten ;; - *-*-wasi) + *-*-wasi*) ac_sys_system=WASI ;; *) @@ -7086,7 +7086,7 @@ case $host/$ac_cv_cc_name in #( PY_SUPPORT_TIER=2 ;; #( powerpc64le-*-linux-gnu/gcc) : PY_SUPPORT_TIER=2 ;; #( - wasm32-unknown-wasi/clang) : + wasm32-unknown-wasip1/clang) : PY_SUPPORT_TIER=2 ;; #( x86_64-*-linux-gnu/clang) : PY_SUPPORT_TIER=2 ;; #( @@ -7792,7 +7792,7 @@ then : fi ;; #( WASI/*) : - HOSTRUNNER='wasmtime run --wasm max-wasm-stack=16777216 --wasi preview2 --env PYTHONPATH=/$(shell realpath --relative-to $(abs_srcdir) $(abs_builddir))/$(shell cat pybuilddir.txt):/Lib --dir $(srcdir)::/' ;; #( + HOSTRUNNER='wasmtime run --wasm max-wasm-stack=16777216 --wasi preview2=n --env PYTHONPATH=/$(shell realpath --relative-to $(abs_srcdir) $(abs_builddir))/$(shell cat pybuilddir.txt):/Lib --dir $(srcdir)::/' ;; #( *) : HOSTRUNNER='' ;; diff --git a/configure.ac b/configure.ac index ce5a5eb9c2891f..36199b36d27ba2 100644 --- a/configure.ac +++ b/configure.ac @@ -336,7 +336,7 @@ then *-*-emscripten) ac_sys_system=Emscripten ;; - *-*-wasi) + *-*-wasi*) ac_sys_system=WASI ;; *) @@ -1201,7 +1201,7 @@ AS_CASE([$host/$ac_cv_cc_name], [aarch64-*-linux-gnu/gcc], [PY_SUPPORT_TIER=2], dnl Linux ARM64, glibc, gcc+clang [aarch64-*-linux-gnu/clang], [PY_SUPPORT_TIER=2], [powerpc64le-*-linux-gnu/gcc], [PY_SUPPORT_TIER=2], dnl Linux on PPC64 little endian, glibc, gcc - [wasm32-unknown-wasi/clang], [PY_SUPPORT_TIER=2], dnl WebAssembly System Interface, clang + [wasm32-unknown-wasip1/clang], [PY_SUPPORT_TIER=2], dnl WebAssembly System Interface preview1, clang [x86_64-*-linux-gnu/clang], [PY_SUPPORT_TIER=2], dnl Linux on AMD64, any vendor, glibc, clang [aarch64-pc-windows-msvc/msvc], [PY_SUPPORT_TIER=3], dnl Windows ARM64, MSVC @@ -1647,7 +1647,7 @@ then dnl TODO: support other WASI runtimes dnl wasmtime starts the process with "/" as CWD. For OOT builds add the dnl directory containing _sysconfigdata to PYTHONPATH. - [WASI/*], [HOSTRUNNER='wasmtime run --wasm max-wasm-stack=16777216 --wasi preview2 --env PYTHONPATH=/$(shell realpath --relative-to $(abs_srcdir) $(abs_builddir))/$(shell cat pybuilddir.txt):/Lib --dir $(srcdir)::/'], + [WASI/*], [HOSTRUNNER='wasmtime run --wasm max-wasm-stack=16777216 --wasi preview2=n --env PYTHONPATH=/$(shell realpath --relative-to $(abs_srcdir) $(abs_builddir))/$(shell cat pybuilddir.txt):/Lib --dir $(srcdir)::/'], [HOSTRUNNER=''] ) fi From c222441fa7f89d448e476c252ba09be588568392 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Fri, 8 Nov 2024 00:03:11 +0100 Subject: [PATCH 100/219] gh-120017: use 'do-while(0)' in some `{codegen,compile}.c` multi-line macros (#120018) --- Python/codegen.c | 126 +++++++++++++++++++++++++---------------------- Python/compile.c | 8 +-- 2 files changed, 72 insertions(+), 62 deletions(-) diff --git a/Python/codegen.c b/Python/codegen.c index d6ba85887e3860..624d4f7ce14f21 100644 --- a/Python/codegen.c +++ b/Python/codegen.c @@ -51,16 +51,19 @@ #define ERROR -1 #define RETURN_IF_ERROR(X) \ - if ((X) == -1) { \ - return ERROR; \ - } - -#define RETURN_IF_ERROR_IN_SCOPE(C, CALL) { \ - if ((CALL) < 0) { \ - _PyCompile_ExitScope((C)); \ - return ERROR; \ - } \ -} + do { \ + if ((X) == -1) { \ + return ERROR; \ + } \ + } while (0) + +#define RETURN_IF_ERROR_IN_SCOPE(C, CALL) \ + do { \ + if ((CALL) < 0) { \ + _PyCompile_ExitScope((C)); \ + return ERROR; \ + } \ + } while (0) struct _PyCompiler; typedef struct _PyCompiler compiler; @@ -261,7 +264,7 @@ codegen_addop_i(instr_sequence *seq, int opcode, Py_ssize_t oparg, location loc) RETURN_IF_ERROR(codegen_addop_i(INSTR_SEQUENCE(C), (OP), (O), (LOC))) #define ADDOP_I_IN_SCOPE(C, LOC, OP, O) \ - RETURN_IF_ERROR_IN_SCOPE(C, codegen_addop_i(INSTR_SEQUENCE(C), (OP), (O), (LOC))); + RETURN_IF_ERROR_IN_SCOPE(C, codegen_addop_i(INSTR_SEQUENCE(C), (OP), (O), (LOC))) static int codegen_addop_noarg(instr_sequence *seq, int opcode, location loc) @@ -303,17 +306,18 @@ codegen_addop_load_const(compiler *c, location loc, PyObject *o) RETURN_IF_ERROR_IN_SCOPE((C), codegen_addop_load_const((C), (LOC), (O))) /* Same as ADDOP_LOAD_CONST, but steals a reference. */ -#define ADDOP_LOAD_CONST_NEW(C, LOC, O) { \ - PyObject *__new_const = (O); \ - if (__new_const == NULL) { \ - return ERROR; \ - } \ - if (codegen_addop_load_const((C), (LOC), __new_const) < 0) { \ - Py_DECREF(__new_const); \ - return ERROR; \ - } \ - Py_DECREF(__new_const); \ -} +#define ADDOP_LOAD_CONST_NEW(C, LOC, O) \ + do { \ + PyObject *__new_const = (O); \ + if (__new_const == NULL) { \ + return ERROR; \ + } \ + if (codegen_addop_load_const((C), (LOC), __new_const) < 0) { \ + Py_DECREF(__new_const); \ + return ERROR; \ + } \ + Py_DECREF(__new_const); \ + } while (0) static int codegen_addop_o(compiler *c, location loc, @@ -325,19 +329,23 @@ codegen_addop_o(compiler *c, location loc, return SUCCESS; } -#define ADDOP_N(C, LOC, OP, O, TYPE) { \ - assert(!OPCODE_HAS_CONST(OP)); /* use ADDOP_LOAD_CONST_NEW */ \ - int ret = codegen_addop_o((C), (LOC), (OP), METADATA(C)->u_ ## TYPE, (O)); \ - Py_DECREF((O)); \ - RETURN_IF_ERROR(ret); \ -} - -#define ADDOP_N_IN_SCOPE(C, LOC, OP, O, TYPE) { \ - assert(!OPCODE_HAS_CONST(OP)); /* use ADDOP_LOAD_CONST_NEW */ \ - int ret = codegen_addop_o((C), (LOC), (OP), METADATA(C)->u_ ## TYPE, (O)); \ - Py_DECREF((O)); \ - RETURN_IF_ERROR_IN_SCOPE((C), ret); \ -} +#define ADDOP_N(C, LOC, OP, O, TYPE) \ + do { \ + assert(!OPCODE_HAS_CONST(OP)); /* use ADDOP_LOAD_CONST_NEW */ \ + int ret = codegen_addop_o((C), (LOC), (OP), \ + METADATA(C)->u_ ## TYPE, (O)); \ + Py_DECREF((O)); \ + RETURN_IF_ERROR(ret); \ + } while (0) + +#define ADDOP_N_IN_SCOPE(C, LOC, OP, O, TYPE) \ + do { \ + assert(!OPCODE_HAS_CONST(OP)); /* use ADDOP_LOAD_CONST_NEW */ \ + int ret = codegen_addop_o((C), (LOC), (OP), \ + METADATA(C)->u_ ## TYPE, (O)); \ + Py_DECREF((O)); \ + RETURN_IF_ERROR_IN_SCOPE((C), ret); \ + } while (0) #define LOAD_METHOD -1 #define LOAD_SUPER_METHOD -2 @@ -426,31 +434,31 @@ codegen_addop_j(instr_sequence *seq, location loc, */ #define VISIT(C, TYPE, V) \ - RETURN_IF_ERROR(codegen_visit_ ## TYPE((C), (V))); + RETURN_IF_ERROR(codegen_visit_ ## TYPE((C), (V))) #define VISIT_IN_SCOPE(C, TYPE, V) \ RETURN_IF_ERROR_IN_SCOPE((C), codegen_visit_ ## TYPE((C), (V))) -#define VISIT_SEQ(C, TYPE, SEQ) { \ - int _i; \ - asdl_ ## TYPE ## _seq *seq = (SEQ); /* avoid variable capture */ \ - for (_i = 0; _i < asdl_seq_LEN(seq); _i++) { \ - TYPE ## _ty elt = (TYPE ## _ty)asdl_seq_GET(seq, _i); \ - RETURN_IF_ERROR(codegen_visit_ ## TYPE((C), elt)); \ - } \ -} - -#define VISIT_SEQ_IN_SCOPE(C, TYPE, SEQ) { \ - int _i; \ - asdl_ ## TYPE ## _seq *seq = (SEQ); /* avoid variable capture */ \ - for (_i = 0; _i < asdl_seq_LEN(seq); _i++) { \ - TYPE ## _ty elt = (TYPE ## _ty)asdl_seq_GET(seq, _i); \ - if (codegen_visit_ ## TYPE((C), elt) < 0) { \ - _PyCompile_ExitScope(C); \ - return ERROR; \ - } \ - } \ -} +#define VISIT_SEQ(C, TYPE, SEQ) \ + do { \ + asdl_ ## TYPE ## _seq *seq = (SEQ); /* avoid variable capture */ \ + for (int _i = 0; _i < asdl_seq_LEN(seq); _i++) { \ + TYPE ## _ty elt = (TYPE ## _ty)asdl_seq_GET(seq, _i); \ + RETURN_IF_ERROR(codegen_visit_ ## TYPE((C), elt)); \ + } \ + } while (0) + +#define VISIT_SEQ_IN_SCOPE(C, TYPE, SEQ) \ + do { \ + asdl_ ## TYPE ## _seq *seq = (SEQ); /* avoid variable capture */ \ + for (int _i = 0; _i < asdl_seq_LEN(seq); _i++) { \ + TYPE ## _ty elt = (TYPE ## _ty)asdl_seq_GET(seq, _i); \ + if (codegen_visit_ ## TYPE((C), elt) < 0) { \ + _PyCompile_ExitScope(C); \ + return ERROR; \ + } \ + } \ + } while (0) static int codegen_call_exit_with_nones(compiler *c, location loc) @@ -2866,7 +2874,7 @@ codegen_visit_stmt(compiler *c, stmt_ty s) case Return_kind: return codegen_return(c, s); case Delete_kind: - VISIT_SEQ(c, expr, s->v.Delete.targets) + VISIT_SEQ(c, expr, s->v.Delete.targets); break; case Assign_kind: { @@ -4759,7 +4767,7 @@ codegen_async_with(compiler *c, stmt_ty s, int pos) pos++; if (pos == asdl_seq_LEN(s->v.AsyncWith.items)) { /* BLOCK code */ - VISIT_SEQ(c, stmt, s->v.AsyncWith.body) + VISIT_SEQ(c, stmt, s->v.AsyncWith.body); } else { RETURN_IF_ERROR(codegen_async_with(c, s, pos)); @@ -4858,7 +4866,7 @@ codegen_with(compiler *c, stmt_ty s, int pos) pos++; if (pos == asdl_seq_LEN(s->v.With.items)) { /* BLOCK code */ - VISIT_SEQ(c, stmt, s->v.With.body) + VISIT_SEQ(c, stmt, s->v.With.body); } else { RETURN_IF_ERROR(codegen_with(c, s, pos)); diff --git a/Python/compile.c b/Python/compile.c index ecca9b0b06ecf7..cbfba7f493e07d 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -31,9 +31,11 @@ #define ERROR -1 #define RETURN_IF_ERROR(X) \ - if ((X) == -1) { \ - return ERROR; \ - } + do { \ + if ((X) == -1) { \ + return ERROR; \ + } \ + } while (0) typedef _Py_SourceLocation location; typedef _PyJumpTargetLabel jump_target_label; From 06a8b0bb5ee0d01ed93315137c41c104a11e4640 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 8 Nov 2024 07:41:54 +0200 Subject: [PATCH 101/219] gh-122943: Remove the object converter for var-positional parameter (GH-126560) --- Lib/test/clinic.test.c | 24 ++++----- Lib/test/test_clinic.py | 24 ++++----- Modules/_testclinic.c | 76 ++++++++++++++-------------- Objects/typevarobject.c | 4 +- Python/bltinmodule.c | 13 ++--- Python/clinic/bltinmodule.c.h | 22 ++++---- Tools/clinic/libclinic/converters.py | 4 -- 7 files changed, 80 insertions(+), 87 deletions(-) diff --git a/Lib/test/clinic.test.c b/Lib/test/clinic.test.c index 80208862f2a09a..e653052e1b772c 100644 --- a/Lib/test/clinic.test.c +++ b/Lib/test/clinic.test.c @@ -4135,7 +4135,7 @@ test_vararg_and_posonly a: object / - *args: object + *args: tuple [clinic start generated code]*/ @@ -4176,7 +4176,7 @@ test_vararg_and_posonly(PyObject *module, PyObject *const *args, Py_ssize_t narg static PyObject * test_vararg_and_posonly_impl(PyObject *module, PyObject *a, PyObject *args) -/*[clinic end generated code: output=0c11c475e240869e input=9cfa748bbff09877]*/ +/*[clinic end generated code: output=0c11c475e240869e input=2c49a482f68545c0]*/ PyDoc_STRVAR(test_vararg_and_posonly__doc__, "test_vararg_and_posonly($module, a, /, *args)\n" @@ -4221,7 +4221,7 @@ test_vararg a: object - *args: object + *args: tuple [clinic start generated code]*/ @@ -4292,14 +4292,14 @@ test_vararg(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject static PyObject * test_vararg_impl(PyObject *module, PyObject *a, PyObject *args) -/*[clinic end generated code: output=e7d7da6a7e008125 input=81d33815ad1bae6e]*/ +/*[clinic end generated code: output=e7d7da6a7e008125 input=7448995636d9186a]*/ /*[clinic input] test_vararg_with_default a: object - *args: object + *args: tuple b: bool = False [clinic start generated code]*/ @@ -4383,13 +4383,13 @@ test_vararg_with_default(PyObject *module, PyObject *const *args, Py_ssize_t nar static PyObject * test_vararg_with_default_impl(PyObject *module, PyObject *a, PyObject *args, int b) -/*[clinic end generated code: output=46781f9920ecedcf input=6e110b54acd9b22d]*/ +/*[clinic end generated code: output=46781f9920ecedcf input=3a0f9f557ce1f712]*/ /*[clinic input] test_vararg_with_only_defaults - *args: object + *args: tuple b: bool = False c: object = ' ' @@ -4477,7 +4477,7 @@ test_vararg_with_only_defaults(PyObject *module, PyObject *const *args, Py_ssize static PyObject * test_vararg_with_only_defaults_impl(PyObject *module, PyObject *args, int b, PyObject *c) -/*[clinic end generated code: output=d03daf5067039c03 input=fa56a709a035666e]*/ +/*[clinic end generated code: output=d03daf5067039c03 input=6983e66817f82924]*/ /*[clinic input] test_paramname_module @@ -4978,7 +4978,7 @@ Test_an_metho_arg_named_arg_impl(TestObj *self, int arg) /*[clinic input] Test.__init__ - *args: object + *args: tuple Varargs init method. For example, nargs is translated to PyTuple_GET_SIZE. [clinic start generated code]*/ @@ -5016,7 +5016,7 @@ Test___init__(PyObject *self, PyObject *args, PyObject *kwargs) static int Test___init___impl(TestObj *self, PyObject *args) -/*[clinic end generated code: output=f172425cec373cd6 input=2a8bd0033c9ac772]*/ +/*[clinic end generated code: output=f172425cec373cd6 input=4b8388c4e6baab6f]*/ PyDoc_STRVAR(Test___init____doc__, "Test(*args)\n" @@ -5054,7 +5054,7 @@ Test___init__(PyObject *self, PyObject *args, PyObject *kwargs) /*[clinic input] @classmethod Test.__new__ - *args: object + *args: tuple Varargs new method. For example, nargs is translated to PyTuple_GET_SIZE. [clinic start generated code]*/ @@ -5091,7 +5091,7 @@ Test(PyTypeObject *type, PyObject *args, PyObject *kwargs) static PyObject * Test_impl(PyTypeObject *type, PyObject *args) -/*[clinic end generated code: output=ee1e8892a67abd4a input=70ad829df3dd9b84]*/ +/*[clinic end generated code: output=ee1e8892a67abd4a input=a8259521129cad20]*/ PyDoc_STRVAR(Test__doc__, "Test(*args)\n" diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index a4317a3ad468d8..3936acd81d4d5a 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -338,7 +338,7 @@ def test_star_after_vararg(self): my_test_func pos_arg: object - *args: object + *args: tuple * kw_arg: object [clinic start generated code]*/ @@ -353,7 +353,7 @@ def test_vararg_after_star(self): pos_arg: object * - *args: object + *args: tuple kw_arg: object [clinic start generated code]*/ """ @@ -1816,7 +1816,7 @@ def test_parameters_required_after_depr_star3(self): foo.bar a: int * [from 3.14] - *args: object + *args: tuple b: int Docstring. """ @@ -1844,7 +1844,7 @@ def test_depr_star_must_come_before_vararg(self): module foo foo.bar a: int - *args: object + *args: tuple * [from 3.14] b: int Docstring. @@ -1980,7 +1980,7 @@ def test_slash_after_vararg(self): foo.bar x: int y: int - *args: object + *args: tuple z: int / """ @@ -2031,7 +2031,7 @@ def test_vararg_must_come_after_depr_slash(self): module foo foo.bar a: int - *args: object + *args: tuple / [from 3.14] b: int Docstring. @@ -2070,8 +2070,8 @@ def test_parameters_no_more_than_one_vararg(self): block = """ module foo foo.bar - *vararg1: object - *vararg2: object + *vararg1: tuple + *vararg2: tuple """ self.expect_failure(block, err, lineno=3) @@ -2156,11 +2156,11 @@ def test_indent_stack_no_tabs(self): block = """ module foo foo.bar - *vararg1: object - \t*vararg2: object + *vararg1: tuple + \t*vararg2: tuple """ err = ("Tab characters are illegal in the Clinic DSL: " - r"'\t*vararg2: object'") + r"'\t*vararg2: tuple'") self.expect_failure(block, err) def test_indent_stack_illegal_outdent(self): @@ -2505,7 +2505,7 @@ def test_vararg_cannot_take_default_value(self): err = "Vararg can't take a default value!" block = """ fn - *args: object = None + *args: tuple = None """ self.expect_failure(block, err, lineno=1) diff --git a/Modules/_testclinic.c b/Modules/_testclinic.c index 320c8ddf214dcd..a5c4a4c40b3949 100644 --- a/Modules/_testclinic.c +++ b/Modules/_testclinic.c @@ -980,13 +980,13 @@ keyword_only_parameter_impl(PyObject *module, PyObject *a) /*[clinic input] varpos - *args: object + *args: tuple [clinic start generated code]*/ static PyObject * varpos_impl(PyObject *module, PyObject *args) -/*[clinic end generated code: output=7b0b9545872bdca4 input=f87cd674145d394c]*/ +/*[clinic end generated code: output=7b0b9545872bdca4 input=ae7ccecd997aaff4]*/ { return Py_NewRef(args); } @@ -998,14 +998,14 @@ posonly_varpos a: object b: object / - *args: object + *args: tuple [clinic start generated code]*/ static PyObject * posonly_varpos_impl(PyObject *module, PyObject *a, PyObject *b, PyObject *args) -/*[clinic end generated code: output=5dae5eb2a0d623cd input=c9fd7895cfbaabba]*/ +/*[clinic end generated code: output=5dae5eb2a0d623cd input=6dd74417b62cbe67]*/ { return pack_arguments_newref(3, a, b, args); } @@ -1017,14 +1017,14 @@ posonly_req_opt_varpos a: object b: object = False / - *args: object + *args: tuple [clinic start generated code]*/ static PyObject * posonly_req_opt_varpos_impl(PyObject *module, PyObject *a, PyObject *b, PyObject *args) -/*[clinic end generated code: output=67f82f90838e166a input=a49bd64740171e1c]*/ +/*[clinic end generated code: output=67f82f90838e166a input=e08ed48926a5b760]*/ { return pack_arguments_newref(3, a, b, args); } @@ -1036,14 +1036,14 @@ posonly_poskw_varpos a: object / b: object - *args: object + *args: tuple [clinic start generated code]*/ static PyObject * posonly_poskw_varpos_impl(PyObject *module, PyObject *a, PyObject *b, PyObject *args) -/*[clinic end generated code: output=bffdb7649941c939 input=b3d7a734e0625f68]*/ +/*[clinic end generated code: output=bffdb7649941c939 input=e5a2c4cab6745ca1]*/ { return pack_arguments_newref(3, a, b, args); } @@ -1053,13 +1053,13 @@ posonly_poskw_varpos_impl(PyObject *module, PyObject *a, PyObject *b, poskw_varpos a: object - *args: object + *args: tuple [clinic start generated code]*/ static PyObject * poskw_varpos_impl(PyObject *module, PyObject *a, PyObject *args) -/*[clinic end generated code: output=2413ddfb5ef22794 input=a1dff12d00422484]*/ +/*[clinic end generated code: output=2413ddfb5ef22794 input=e78114436a07aefe]*/ { return pack_arguments_newref(2, a, args); } @@ -1069,7 +1069,7 @@ poskw_varpos_impl(PyObject *module, PyObject *a, PyObject *args) poskw_varpos_kwonly_opt a: object - *args: object + *args: tuple b: bool = False [clinic start generated code]*/ @@ -1077,7 +1077,7 @@ poskw_varpos_kwonly_opt static PyObject * poskw_varpos_kwonly_opt_impl(PyObject *module, PyObject *a, PyObject *args, int b) -/*[clinic end generated code: output=f36d35ba6133463b input=1721d43dc5f6d856]*/ +/*[clinic end generated code: output=f36d35ba6133463b input=ebecfb189f4a3380]*/ { PyObject *obj_b = b ? Py_True : Py_False; return pack_arguments_newref(3, a, args, obj_b); @@ -1088,7 +1088,7 @@ poskw_varpos_kwonly_opt_impl(PyObject *module, PyObject *a, PyObject *args, poskw_varpos_kwonly_opt2 a: object - *args: object + *args: tuple b: object = False c: object = False @@ -1097,7 +1097,7 @@ poskw_varpos_kwonly_opt2 static PyObject * poskw_varpos_kwonly_opt2_impl(PyObject *module, PyObject *a, PyObject *args, PyObject *b, PyObject *c) -/*[clinic end generated code: output=846cef62c6c40463 input=bb4b8d1577a8a408]*/ +/*[clinic end generated code: output=846cef62c6c40463 input=1aff29829431f711]*/ { return pack_arguments_newref(4, a, args, b, c); } @@ -1106,14 +1106,14 @@ poskw_varpos_kwonly_opt2_impl(PyObject *module, PyObject *a, PyObject *args, /*[clinic input] varpos_kwonly_opt - *args: object + *args: tuple b: object = False [clinic start generated code]*/ static PyObject * varpos_kwonly_opt_impl(PyObject *module, PyObject *args, PyObject *b) -/*[clinic end generated code: output=3b7bf98b091f5731 input=380fb00deec847e8]*/ +/*[clinic end generated code: output=3b7bf98b091f5731 input=1bec50dc49fca2eb]*/ { return pack_arguments_newref(2, args, b); } @@ -1122,7 +1122,7 @@ varpos_kwonly_opt_impl(PyObject *module, PyObject *args, PyObject *b) /*[clinic input] varpos_kwonly_req_opt - *args: object + *args: tuple a: object b: object = False c: object = False @@ -1132,7 +1132,7 @@ varpos_kwonly_req_opt static PyObject * varpos_kwonly_req_opt_impl(PyObject *module, PyObject *args, PyObject *a, PyObject *b, PyObject *c) -/*[clinic end generated code: output=165274e1fd037ae9 input=530794afd0690c22]*/ +/*[clinic end generated code: output=165274e1fd037ae9 input=355271f6119bb6c8]*/ { return pack_arguments_newref(4, args, a, b, c); } @@ -1219,7 +1219,7 @@ gh_32092_oob pos1: object pos2: object - *varargs: object + *varargs: tuple kw1: object = None kw2: object = None @@ -1230,7 +1230,7 @@ Proof-of-concept of GH-32092 OOB bug. static PyObject * gh_32092_oob_impl(PyObject *module, PyObject *pos1, PyObject *pos2, PyObject *varargs, PyObject *kw1, PyObject *kw2) -/*[clinic end generated code: output=ee259c130054653f input=46d15c881608f8ff]*/ +/*[clinic end generated code: output=ee259c130054653f input=63aeeca881979b91]*/ { Py_RETURN_NONE; } @@ -1240,7 +1240,7 @@ gh_32092_oob_impl(PyObject *module, PyObject *pos1, PyObject *pos2, gh_32092_kw_pass pos: object - *args: object + *args: tuple kw: object = None Proof-of-concept of GH-32092 keyword args passing bug. @@ -1250,7 +1250,7 @@ Proof-of-concept of GH-32092 keyword args passing bug. static PyObject * gh_32092_kw_pass_impl(PyObject *module, PyObject *pos, PyObject *args, PyObject *kw) -/*[clinic end generated code: output=4a2bbe4f7c8604e9 input=5c0bd5b9079a0cce]*/ +/*[clinic end generated code: output=4a2bbe4f7c8604e9 input=258987971f3ee97a]*/ { Py_RETURN_NONE; } @@ -1259,7 +1259,7 @@ gh_32092_kw_pass_impl(PyObject *module, PyObject *pos, PyObject *args, /*[clinic input] gh_99233_refcount - *args: object + *args: tuple Proof-of-concept of GH-99233 refcount error bug. @@ -1267,7 +1267,7 @@ Proof-of-concept of GH-99233 refcount error bug. static PyObject * gh_99233_refcount_impl(PyObject *module, PyObject *args) -/*[clinic end generated code: output=585855abfbca9a7f input=eecfdc2092d90dc3]*/ +/*[clinic end generated code: output=585855abfbca9a7f input=f5204359fd852613]*/ { Py_RETURN_NONE; } @@ -1295,7 +1295,7 @@ gh_99240_double_free_impl(PyObject *module, char *a, char *b) null_or_tuple_for_varargs name: object - *constraints: object + *constraints: tuple covariant: bool = False See https://github.com/python/cpython/issues/110864 @@ -1304,7 +1304,7 @@ See https://github.com/python/cpython/issues/110864 static PyObject * null_or_tuple_for_varargs_impl(PyObject *module, PyObject *name, PyObject *constraints, int covariant) -/*[clinic end generated code: output=a785b35421358983 input=c9bce186637956b3]*/ +/*[clinic end generated code: output=a785b35421358983 input=4df930e019f32878]*/ { assert(name != NULL); assert(constraints != NULL); @@ -1412,13 +1412,13 @@ _testclinic_TestClass_get_defining_class_arg_impl(PyObject *self, /*[clinic input] _testclinic.TestClass.defclass_varpos cls: defining_class - *args: object + *args: tuple [clinic start generated code]*/ static PyObject * _testclinic_TestClass_defclass_varpos_impl(PyObject *self, PyTypeObject *cls, PyObject *args) -/*[clinic end generated code: output=fad33f2d3a8d778d input=47071dcda393a7e1]*/ +/*[clinic end generated code: output=fad33f2d3a8d778d input=332043286e393d38]*/ { return PyTuple_Pack(2, cls, args); } @@ -1429,7 +1429,7 @@ _testclinic.TestClass.defclass_posonly_varpos a: object b: object / - *args: object + *args: tuple [clinic start generated code]*/ static PyObject * @@ -1437,7 +1437,7 @@ _testclinic_TestClass_defclass_posonly_varpos_impl(PyObject *self, PyTypeObject *cls, PyObject *a, PyObject *b, PyObject *args) -/*[clinic end generated code: output=1740fcf48d230b07 input=40f2e56286d4a7ef]*/ +/*[clinic end generated code: output=1740fcf48d230b07 input=191da4557203c413]*/ { return pack_arguments_newref(4, cls, a, b, args); } @@ -1447,13 +1447,13 @@ _testclinic_TestClass_defclass_posonly_varpos_impl(PyObject *self, @classmethod _testclinic.TestClass.__new__ as varpos_no_fastcall - *args: object + *args: tuple [clinic start generated code]*/ static PyObject * varpos_no_fastcall_impl(PyTypeObject *type, PyObject *args) -/*[clinic end generated code: output=04e94f2898bb2dde input=b0447ebab3e81001]*/ +/*[clinic end generated code: output=04e94f2898bb2dde input=c5d3d30a6589f97f]*/ { return Py_NewRef(args); } @@ -1466,14 +1466,14 @@ _testclinic.TestClass.__new__ as posonly_varpos_no_fastcall a: object b: object / - *args: object + *args: tuple [clinic start generated code]*/ static PyObject * posonly_varpos_no_fastcall_impl(PyTypeObject *type, PyObject *a, PyObject *b, PyObject *args) -/*[clinic end generated code: output=b0a0425719f69f5a input=d2ec37a06b3c2389]*/ +/*[clinic end generated code: output=b0a0425719f69f5a input=10f29f2c2c6bfdc4]*/ { return pack_arguments_newref(3, a, b, args); } @@ -1486,14 +1486,14 @@ _testclinic.TestClass.__new__ as posonly_req_opt_varpos_no_fastcall a: object b: object = False / - *args: object + *args: tuple [clinic start generated code]*/ static PyObject * posonly_req_opt_varpos_no_fastcall_impl(PyTypeObject *type, PyObject *a, PyObject *b, PyObject *args) -/*[clinic end generated code: output=3c44915b1a554e2d input=e9e74686a5e6a06d]*/ +/*[clinic end generated code: output=3c44915b1a554e2d input=d319302a8748147c]*/ { return pack_arguments_newref(3, a, b, args); } @@ -1506,14 +1506,14 @@ _testclinic.TestClass.__new__ as posonly_poskw_varpos_no_fastcall a: object / b: object - *args: object + *args: tuple [clinic start generated code]*/ static PyObject * posonly_poskw_varpos_no_fastcall_impl(PyTypeObject *type, PyObject *a, PyObject *b, PyObject *args) -/*[clinic end generated code: output=6ad74bed4bdc7f96 input=fa931c38184213aa]*/ +/*[clinic end generated code: output=6ad74bed4bdc7f96 input=1f8c113e749414a3]*/ { return pack_arguments_newref(3, a, b, args); } diff --git a/Objects/typevarobject.c b/Objects/typevarobject.c index 91cc37c9a72636..bacb858978c5d7 100644 --- a/Objects/typevarobject.c +++ b/Objects/typevarobject.c @@ -651,7 +651,7 @@ typevar_alloc(PyObject *name, PyObject *bound, PyObject *evaluate_bound, typevar.__new__ as typevar_new name: object(subclass_of="&PyUnicode_Type") - *constraints: object + *constraints: tuple bound: object = None default as default_value: object(c_default="&_Py_NoDefaultStruct") = typing.NoDefault covariant: bool = False @@ -665,7 +665,7 @@ static PyObject * typevar_new_impl(PyTypeObject *type, PyObject *name, PyObject *constraints, PyObject *bound, PyObject *default_value, int covariant, int contravariant, int infer_variance) -/*[clinic end generated code: output=d2b248ff074eaab6 input=836f97f631d7293a]*/ +/*[clinic end generated code: output=d2b248ff074eaab6 input=1b5b62e40c92c167]*/ { if (covariant && contravariant) { PyErr_SetString(PyExc_ValueError, diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 85a28de2bb9d13..a3f41190261a05 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -2127,7 +2127,7 @@ builtin_pow_impl(PyObject *module, PyObject *base, PyObject *exp, /*[clinic input] print as builtin_print - *args: object + *args: array sep: object(c_default="Py_None") = ' ' string inserted between values, default a space. end: object(c_default="Py_None") = '\n' @@ -2142,9 +2142,10 @@ Prints the values to a stream, or to sys.stdout by default. [clinic start generated code]*/ static PyObject * -builtin_print_impl(PyObject *module, PyObject *args, PyObject *sep, - PyObject *end, PyObject *file, int flush) -/*[clinic end generated code: output=3cfc0940f5bc237b input=c143c575d24fe665]*/ +builtin_print_impl(PyObject *module, PyObject * const *args, + Py_ssize_t args_length, PyObject *sep, PyObject *end, + PyObject *file, int flush) +/*[clinic end generated code: output=3cb7e5b66f1a8547 input=66ea4de1605a2437]*/ { int i, err; @@ -2181,7 +2182,7 @@ builtin_print_impl(PyObject *module, PyObject *args, PyObject *sep, return NULL; } - for (i = 0; i < PyTuple_GET_SIZE(args); i++) { + for (i = 0; i < args_length; i++) { if (i > 0) { if (sep == NULL) { err = PyFile_WriteString(" ", file); @@ -2193,7 +2194,7 @@ builtin_print_impl(PyObject *module, PyObject *args, PyObject *sep, return NULL; } } - err = PyFile_WriteObject(PyTuple_GET_ITEM(args, i), file, Py_PRINT_RAW); + err = PyFile_WriteObject(args[i], file, Py_PRINT_RAW); if (err) { return NULL; } diff --git a/Python/clinic/bltinmodule.c.h b/Python/clinic/bltinmodule.c.h index b47279686cc2ed..39a23f09b7f390 100644 --- a/Python/clinic/bltinmodule.c.h +++ b/Python/clinic/bltinmodule.c.h @@ -7,7 +7,6 @@ preserve # include "pycore_runtime.h" // _Py_ID() #endif #include "pycore_modsupport.h" // _PyArg_UnpackKeywords() -#include "pycore_tuple.h" // _PyTuple_FromArray() PyDoc_STRVAR(builtin___import____doc__, "__import__($module, /, name, globals=None, locals=None, fromlist=(),\n" @@ -902,8 +901,9 @@ PyDoc_STRVAR(builtin_print__doc__, {"print", _PyCFunction_CAST(builtin_print), METH_FASTCALL|METH_KEYWORDS, builtin_print__doc__}, static PyObject * -builtin_print_impl(PyObject *module, PyObject *args, PyObject *sep, - PyObject *end, PyObject *file, int flush); +builtin_print_impl(PyObject *module, PyObject * const *args, + Py_ssize_t args_length, PyObject *sep, PyObject *end, + PyObject *file, int flush); static PyObject * builtin_print(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -937,7 +937,8 @@ builtin_print(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec PyObject *argsbuf[4]; PyObject * const *fastargs; Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; - PyObject *__clinic_args = NULL; + PyObject * const *__clinic_args; + Py_ssize_t args_length; PyObject *sep = Py_None; PyObject *end = Py_None; PyObject *file = Py_None; @@ -973,16 +974,11 @@ builtin_print(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec goto exit; } skip_optional_kwonly: - __clinic_args = _PyTuple_FromArray(args, nargs); - if (__clinic_args == NULL) { - goto exit; - } - return_value = builtin_print_impl(module, __clinic_args, sep, end, file, flush); + __clinic_args = args; + args_length = nargs; + return_value = builtin_print_impl(module, __clinic_args, args_length, sep, end, file, flush); exit: - /* Cleanup for args */ - Py_XDECREF(__clinic_args); - return return_value; } @@ -1235,4 +1231,4 @@ builtin_issubclass(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=76b27cf4164f257e input=a9049054013a1b77]*/ +/*[clinic end generated code: output=5c510ec462507656 input=a9049054013a1b77]*/ diff --git a/Tools/clinic/libclinic/converters.py b/Tools/clinic/libclinic/converters.py index 2d103c941cbf23..860ff8135fb258 100644 --- a/Tools/clinic/libclinic/converters.py +++ b/Tools/clinic/libclinic/converters.py @@ -1251,7 +1251,3 @@ class varpos_array_converter(CConverter): def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: raise AssertionError('should never be called') - -# XXX: temporary -class varpos_object_converter(varpos_tuple_converter): - pass From 9ecd8f7f40e6724a1c1d46c2665147aaabceb2d2 Mon Sep 17 00:00:00 2001 From: Valery Fedorenko Date: Fri, 8 Nov 2024 13:19:15 +0300 Subject: [PATCH 102/219] gh-126171: fix possible null dereference in _imp_find_frozen_impl (#126566) --- Python/import.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/import.c b/Python/import.c index d8ad37b2422795..29bd8bf68ff5e1 100644 --- a/Python/import.c +++ b/Python/import.c @@ -4424,7 +4424,7 @@ _imp_find_frozen_impl(PyObject *module, PyObject *name, int withdata) if (info.origname != NULL && info.origname[0] != '\0') { origname = PyUnicode_FromString(info.origname); if (origname == NULL) { - Py_DECREF(data); + Py_XDECREF(data); return NULL; } } From 403410fa1be036214efa7955127911e5592910db Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Fri, 8 Nov 2024 12:57:17 +0100 Subject: [PATCH 103/219] gh-89640: Restore configure error message on failure to detect float word order (#126569) Before #126387, if we didn't detect float word order we'd raise the following configure error: Unknown float word ordering. You need to manually preset ax_cv_c_float_words_bigendian=no (or yes) according to your system. This puts it back (except for ARM or WASM, which as hardcoded). --- configure | 2 +- configure.ac | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/configure b/configure index 71ffe9ca1c841e..7a9d9627e50dfc 100755 --- a/configure +++ b/configure @@ -24219,7 +24219,7 @@ printf "%s\n" "#define DOUBLE_IS_ARM_MIXED_ENDIAN_IEEE754 1" >>confdefs.h printf "%s\n" "#define DOUBLE_IS_LITTLE_ENDIAN_IEEE754 1" >>confdefs.h ;; #( *) : - ;; + as_fn_error $? "Unknown float word ordering. You need to manually preset ax_cv_c_float_words_bigendian=no (or yes) according to your system." "$LINENO" 5 ;; esac ;; esac diff --git a/configure.ac b/configure.ac index 36199b36d27ba2..bc3d2d0e63b77a 100644 --- a/configure.ac +++ b/configure.ac @@ -5920,7 +5920,12 @@ AX_C_FLOAT_WORDS_BIGENDIAN( stored in ARM mixed-endian order (byte order 45670123)])], [wasm*], [AC_DEFINE([DOUBLE_IS_LITTLE_ENDIAN_IEEE754], [1], [Define if C doubles are 64-bit IEEE 754 binary format, - stored with the least significant byte first])])]) + stored with the least significant byte first])], + [AC_MSG_ERROR([m4_normalize([ + Unknown float word ordering. You need to manually + preset ax_cv_c_float_words_bigendian=no (or yes) + according to your system. + ])])])]) # The short float repr introduced in Python 3.1 requires the # correctly-rounded string <-> double conversion functions from From ee0746af7d7cfc6cc25441726034e4fea4bcf7e5 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 8 Nov 2024 14:12:15 +0200 Subject: [PATCH 104/219] gh-122943: Move code generation for var-positional parameter to converters (GH-126575) --- Tools/clinic/libclinic/converters.py | 87 +++++++++++++++++++++++++--- Tools/clinic/libclinic/parse_args.py | 77 ++++-------------------- 2 files changed, 91 insertions(+), 73 deletions(-) diff --git a/Tools/clinic/libclinic/converters.py b/Tools/clinic/libclinic/converters.py index 860ff8135fb258..a65f73ba02fb5d 100644 --- a/Tools/clinic/libclinic/converters.py +++ b/Tools/clinic/libclinic/converters.py @@ -1232,7 +1232,18 @@ def set_template_dict(self, template_dict: TemplateDict) -> None: # Converters for var-positional parameter. -class varpos_tuple_converter(CConverter): +class VarPosCConverter(CConverter): + format_unit = '' + + def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: + raise AssertionError('should never be called') + + def parse_vararg(self, *, pos_only: int, min_pos: int, max_pos: int, + fastcall: bool, limited_capi: bool) -> str: + raise NotImplementedError + + +class varpos_tuple_converter(VarPosCConverter): type = 'PyObject *' format_unit = '' c_default = 'NULL' @@ -1240,14 +1251,76 @@ class varpos_tuple_converter(CConverter): def cleanup(self) -> str: return f"""Py_XDECREF({self.parser_name});\n""" - def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: - raise AssertionError('should never be called') + def parse_vararg(self, *, pos_only: int, min_pos: int, max_pos: int, + fastcall: bool, limited_capi: bool) -> str: + paramname = self.parser_name + if fastcall: + if limited_capi: + if min(pos_only, min_pos) < max_pos: + size = f'Py_MAX(nargs - {max_pos}, 0)' + else: + size = f'nargs - {max_pos}' if max_pos else 'nargs' + return f""" + {paramname} = PyTuple_New({size}); + if (!{paramname}) {{{{ + goto exit; + }}}} + for (Py_ssize_t i = {max_pos}; i < nargs; ++i) {{{{ + PyTuple_SET_ITEM({paramname}, i - {max_pos}, Py_NewRef(args[i])); + }}}} + """ + else: + self.add_include('pycore_tuple.h', '_PyTuple_FromArray()') + start = f'args + {max_pos}' if max_pos else 'args' + size = f'nargs - {max_pos}' if max_pos else 'nargs' + if min(pos_only, min_pos) < max_pos: + return f""" + {paramname} = nargs > {max_pos} + ? _PyTuple_FromArray({start}, {size}) + : PyTuple_New(0); + if ({paramname} == NULL) {{{{ + goto exit; + }}}} + """ + else: + return f""" + {paramname} = _PyTuple_FromArray({start}, {size}); + if ({paramname} == NULL) {{{{ + goto exit; + }}}} + """ + else: + if max_pos: + return f""" + {paramname} = PyTuple_GetSlice(args, {max_pos}, PY_SSIZE_T_MAX); + if (!{paramname}) {{{{ + goto exit; + }}}} + """ + else: + return f"{paramname} = Py_NewRef(args);\n" -class varpos_array_converter(CConverter): + +class varpos_array_converter(VarPosCConverter): type = 'PyObject * const *' - format_unit = '' length = True c_ignored_default = '' - def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: - raise AssertionError('should never be called') + def parse_vararg(self, *, pos_only: int, min_pos: int, max_pos: int, + fastcall: bool, limited_capi: bool) -> str: + paramname = self.parser_name + if not fastcall: + self.add_include('pycore_tuple.h', '_PyTuple_ITEMS()') + start = 'args' if fastcall else '_PyTuple_ITEMS(args)' + size = 'nargs' if fastcall else 'PyTuple_GET_SIZE(args)' + if max_pos: + if min(pos_only, min_pos) < max_pos: + start = f'{size} > {max_pos} ? {start} + {max_pos} : {start}' + size = f'Py_MAX(0, {size} - {max_pos})' + else: + start = f'{start} + {max_pos}' + size = f'{size} - {max_pos}' + return f""" + {paramname} = {start}; + {self.length_name} = {size}; + """ diff --git a/Tools/clinic/libclinic/parse_args.py b/Tools/clinic/libclinic/parse_args.py index 2ce4e7512148d2..08cf697b67e5ae 100644 --- a/Tools/clinic/libclinic/parse_args.py +++ b/Tools/clinic/libclinic/parse_args.py @@ -452,71 +452,13 @@ def parse_option_groups(self) -> None: def _parse_vararg(self) -> str: assert self.varpos is not None - paramname = self.varpos.converter.parser_name - if self.varpos.converter.length: - if not self.fastcall: - self.codegen.add_include('pycore_tuple.h', - '_PyTuple_ITEMS()') - start = 'args' if self.fastcall else '_PyTuple_ITEMS(args)' - size = 'nargs' if self.fastcall else 'PyTuple_GET_SIZE(args)' - if self.max_pos: - if min(self.pos_only, self.min_pos) < self.max_pos: - start = f'{size} > {self.max_pos} ? {start} + {self.max_pos} : {start}' - size = f'Py_MAX(0, {size} - {self.max_pos})' - else: - start = f'{start} + {self.max_pos}' - size = f'{size} - {self.max_pos}' - return f""" - {paramname} = {start}; - {self.varpos.converter.length_name} = {size}; - """ - - if self.fastcall: - if self.limited_capi: - if min(self.pos_only, self.min_pos) < self.max_pos: - size = f'Py_MAX(nargs - {self.max_pos}, 0)' - else: - size = f'nargs - {self.max_pos}' if self.max_pos else 'nargs' - return f""" - {paramname} = PyTuple_New({size}); - if (!{paramname}) {{{{ - goto exit; - }}}} - for (Py_ssize_t i = {self.max_pos}; i < nargs; ++i) {{{{ - PyTuple_SET_ITEM({paramname}, i - {self.max_pos}, Py_NewRef(args[i])); - }}}} - """ - else: - self.codegen.add_include('pycore_tuple.h', - '_PyTuple_FromArray()') - if min(self.pos_only, self.min_pos) < self.max_pos: - return f""" - {paramname} = nargs > {self.max_pos} - ? _PyTuple_FromArray(args + {self.max_pos}, nargs - {self.max_pos}) - : PyTuple_New(0); - if ({paramname} == NULL) {{{{ - goto exit; - }}}} - """ - else: - start = f'args + {self.max_pos}' if self.max_pos else 'args' - size = f'nargs - {self.max_pos}' if self.max_pos else 'nargs' - return f""" - {paramname} = _PyTuple_FromArray({start}, {size}); - if ({paramname} == NULL) {{{{ - goto exit; - }}}} - """ - else: - if self.max_pos: - return f""" - {paramname} = PyTuple_GetSlice(args, {self.max_pos}, PY_SSIZE_T_MAX); - if (!{paramname}) {{{{ - goto exit; - }}}} - """ - else: - return f"{paramname} = Py_NewRef(args);\n" + c = self.varpos.converter + assert isinstance(c, libclinic.converters.VarPosCConverter) + return c.parse_vararg(pos_only=self.pos_only, + min_pos=self.min_pos, + max_pos=self.max_pos, + fastcall=self.fastcall, + limited_capi=self.limited_capi) def parse_pos_only(self) -> None: if self.fastcall: @@ -839,7 +781,10 @@ def parse_general(self, clang: CLanguage) -> None: def copy_includes(self) -> None: # Copy includes from parameters to Clinic after parse_arg() # has been called above. - for converter in self.converters: + converters = self.converters + if self.varpos: + converters = converters + [self.varpos.converter] + for converter in converters: for include in converter.get_includes(): self.codegen.add_include( include.filename, From 061e50f196373d920c3eaa3718b9d0553914e006 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 8 Nov 2024 14:23:50 +0200 Subject: [PATCH 105/219] gh-122943: Add the varpos parameter in _PyArg_UnpackKeywords (GH-126564) Remove _PyArg_UnpackKeywordsWithVararg. Add comments for integer arguments of _PyArg_UnpackKeywords. --- Include/internal/pycore_modsupport.h | 15 +- Lib/test/clinic.test.c | 155 +++++---- Lib/test/test_call.py | 2 +- Lib/test/test_clinic.py | 2 +- Modules/_ctypes/clinic/_ctypes.c.h | 35 +- Modules/_ctypes/clinic/cfield.c.h | 5 +- Modules/_io/clinic/_iomodule.c.h | 8 +- Modules/_io/clinic/bufferedio.c.h | 23 +- Modules/_io/clinic/bytesio.c.h | 5 +- Modules/_io/clinic/fileio.c.h | 17 +- Modules/_io/clinic/iobase.c.h | 8 +- Modules/_io/clinic/stringio.c.h | 5 +- Modules/_io/clinic/textio.c.h | 23 +- Modules/_io/clinic/winconsoleio.c.h | 14 +- Modules/_multiprocessing/clinic/semaphore.c.h | 11 +- Modules/_sqlite/clinic/connection.c.h | 47 ++- Modules/_sqlite/clinic/cursor.c.h | 5 +- Modules/_sqlite/clinic/module.c.h | 5 +- Modules/_sre/clinic/sre.c.h | 41 ++- Modules/_ssl/clinic/cert.c.h | 5 +- Modules/_testcapi/clinic/exceptions.c.h | 5 +- Modules/cjkcodecs/clinic/multibytecodec.c.h | 20 +- Modules/clinic/_asynciomodule.c.h | 59 ++-- Modules/clinic/_bisectmodule.c.h | 14 +- Modules/clinic/_bz2module.c.h | 5 +- Modules/clinic/_codecsmodule.c.h | 8 +- Modules/clinic/_collectionsmodule.c.h | 5 +- Modules/clinic/_csv.c.h | 11 +- Modules/clinic/_curses_panel.c.h | 11 +- Modules/clinic/_cursesmodule.c.h | 5 +- Modules/clinic/_datetimemodule.c.h | 17 +- Modules/clinic/_elementtree.c.h | 38 ++- Modules/clinic/_functoolsmodule.c.h | 5 +- Modules/clinic/_hashopenssl.c.h | 62 ++-- Modules/clinic/_lsprof.c.h | 8 +- Modules/clinic/_lzmamodule.c.h | 8 +- Modules/clinic/_opcode.c.h | 32 +- Modules/clinic/_pickle.c.h | 29 +- Modules/clinic/_queuemodule.c.h | 11 +- Modules/clinic/_ssl.c.h | 35 +- Modules/clinic/_struct.c.h | 11 +- Modules/clinic/_testclinic.c.h | 107 +++--- Modules/clinic/_testclinic_depr.c.h | 74 +++-- Modules/clinic/_testinternalcapi.c.h | 17 +- Modules/clinic/_testmultiphase.c.h | 5 +- Modules/clinic/_winapi.c.h | 11 +- Modules/clinic/_zoneinfo.c.h | 26 +- Modules/clinic/arraymodule.c.h | 14 +- Modules/clinic/binascii.c.h | 23 +- Modules/clinic/blake2module.c.h | 8 +- Modules/clinic/cmathmodule.c.h | 5 +- Modules/clinic/gcmodule.c.h | 8 +- Modules/clinic/itertoolsmodule.c.h | 26 +- Modules/clinic/mathmodule.c.h | 11 +- Modules/clinic/md5module.c.h | 5 +- Modules/clinic/overlapped.c.h | 5 +- Modules/clinic/posixmodule.c.h | 305 ++++++++++++------ Modules/clinic/pyexpat.c.h | 17 +- Modules/clinic/selectmodule.c.h | 17 +- Modules/clinic/sha1module.c.h | 5 +- Modules/clinic/sha2module.c.h | 14 +- Modules/clinic/sha3module.c.h | 5 +- Modules/clinic/signalmodule.c.h | 5 +- Modules/clinic/socketmodule.c.h | 5 +- Modules/clinic/syslogmodule.c.h | 5 +- Modules/clinic/zlibmodule.c.h | 35 +- Objects/clinic/bytearrayobject.c.h | 23 +- Objects/clinic/bytesobject.c.h | 23 +- Objects/clinic/codeobject.c.h | 8 +- Objects/clinic/complexobject.c.h | 5 +- Objects/clinic/descrobject.c.h | 8 +- Objects/clinic/enumobject.c.h | 5 +- Objects/clinic/funcobject.c.h | 5 +- Objects/clinic/listobject.c.h | 5 +- Objects/clinic/longobject.c.h | 11 +- Objects/clinic/memoryobject.c.h | 17 +- Objects/clinic/moduleobject.c.h | 5 +- Objects/clinic/odictobject.c.h | 17 +- Objects/clinic/structseq.c.h | 5 +- Objects/clinic/typevarobject.c.h | 22 +- Objects/clinic/unicodeobject.c.h | 23 +- Objects/stringlib/clinic/transmogrify.h.h | 5 +- PC/clinic/_wmimodule.cpp.h | 5 +- PC/clinic/winreg.c.h | 14 +- Python/clinic/Python-tokenize.c.h | 5 +- Python/clinic/_warnings.c.h | 8 +- Python/clinic/bltinmodule.c.h | 26 +- Python/clinic/import.c.h | 8 +- Python/clinic/instruction_sequence.c.h | 11 +- Python/clinic/marshal.c.h | 14 +- Python/clinic/sysmodule.c.h | 17 +- Python/clinic/traceback.c.h | 5 +- Python/getargs.c | 4 +- Tools/clinic/libclinic/parse_args.py | 13 +- 94 files changed, 1220 insertions(+), 685 deletions(-) diff --git a/Include/internal/pycore_modsupport.h b/Include/internal/pycore_modsupport.h index 250106ad83a3ec..c661f1d82a84f6 100644 --- a/Include/internal/pycore_modsupport.h +++ b/Include/internal/pycore_modsupport.h @@ -76,7 +76,7 @@ PyAPI_FUNC(int) _PyArg_ParseStackAndKeywords( ...); // Export for 'math' shared extension -PyAPI_FUNC(PyObject * const *) _PyArg_UnpackKeywordsEx( +PyAPI_FUNC(PyObject * const *) _PyArg_UnpackKeywords( PyObject *const *args, Py_ssize_t nargs, PyObject *kwargs, @@ -87,17 +87,12 @@ PyAPI_FUNC(PyObject * const *) _PyArg_UnpackKeywordsEx( int minkw, int varpos, PyObject **buf); -#define _PyArg_UnpackKeywords(args, nargs, kwargs, kwnames, parser, minpos, maxpos, minkw, buf) \ +#define _PyArg_UnpackKeywords(args, nargs, kwargs, kwnames, parser, minpos, maxpos, minkw, varpos, buf) \ (((minkw) == 0 && (kwargs) == NULL && (kwnames) == NULL && \ - (minpos) <= (nargs) && (nargs) <= (maxpos) && (args) != NULL) ? \ + (minpos) <= (nargs) && ((varpos) || (nargs) <= (maxpos)) && (args) != NULL) ? \ (args) : \ - _PyArg_UnpackKeywordsEx((args), (nargs), (kwargs), (kwnames), (parser), \ - (minpos), (maxpos), (minkw), 0, (buf))) -#define _PyArg_UnpackKeywordsWithVararg(args, nargs, kwargs, kwnames, parser, minpos, maxpos, minkw, buf) \ - (((minkw) == 0 && (kwargs) == NULL && (kwnames) == NULL && \ - (minpos) <= (nargs) && (args) != NULL) ? (args) : \ - _PyArg_UnpackKeywordsEx((args), (nargs), (kwargs), (kwnames), (parser), \ - (minpos), (maxpos), (minkw), 1, (buf))) + _PyArg_UnpackKeywords((args), (nargs), (kwargs), (kwnames), (parser), \ + (minpos), (maxpos), (minkw), (varpos), (buf))) #ifdef __cplusplus } diff --git a/Lib/test/clinic.test.c b/Lib/test/clinic.test.c index e653052e1b772c..4ad0b8b0910bbe 100644 --- a/Lib/test/clinic.test.c +++ b/Lib/test/clinic.test.c @@ -2199,7 +2199,8 @@ test_keywords(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec PyObject *a; PyObject *b; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2213,7 +2214,7 @@ test_keywords(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec static PyObject * test_keywords_impl(PyObject *module, PyObject *a, PyObject *b) -/*[clinic end generated code: output=13ba007e1c842a37 input=0d3484844749c05b]*/ +/*[clinic end generated code: output=5411fdee0a85d60d input=0d3484844749c05b]*/ /*[clinic input] @@ -2269,7 +2270,8 @@ test_keywords_kwonly(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *a; PyObject *b; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 1, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 1, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2283,7 +2285,7 @@ test_keywords_kwonly(PyObject *module, PyObject *const *args, Py_ssize_t nargs, static PyObject * test_keywords_kwonly_impl(PyObject *module, PyObject *a, PyObject *b) -/*[clinic end generated code: output=789799a6d2d6eb4d input=384adc78bfa0bff7]*/ +/*[clinic end generated code: output=38b11b4dd0a94399 input=384adc78bfa0bff7]*/ /*[clinic input] @@ -2342,7 +2344,8 @@ test_keywords_opt(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyO PyObject *b = Py_None; PyObject *c = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2367,7 +2370,7 @@ test_keywords_opt(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyO static PyObject * test_keywords_opt_impl(PyObject *module, PyObject *a, PyObject *b, PyObject *c) -/*[clinic end generated code: output=42430dd8ea5afde6 input=eda7964f784f4607]*/ +/*[clinic end generated code: output=c59ecb8eca9880cf input=eda7964f784f4607]*/ /*[clinic input] @@ -2429,7 +2432,8 @@ test_keywords_opt_kwonly(PyObject *module, PyObject *const *args, Py_ssize_t nar PyObject *c = Py_None; PyObject *d = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2464,7 +2468,7 @@ test_keywords_opt_kwonly(PyObject *module, PyObject *const *args, Py_ssize_t nar static PyObject * test_keywords_opt_kwonly_impl(PyObject *module, PyObject *a, PyObject *b, PyObject *c, PyObject *d) -/*[clinic end generated code: output=f312c35c380d2bf9 input=209387a4815e5082]*/ +/*[clinic end generated code: output=b9572975a3e5d857 input=209387a4815e5082]*/ /*[clinic input] @@ -2524,7 +2528,8 @@ test_keywords_kwonly_opt(PyObject *module, PyObject *const *args, Py_ssize_t nar PyObject *b = Py_None; PyObject *c = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2549,7 +2554,7 @@ test_keywords_kwonly_opt(PyObject *module, PyObject *const *args, Py_ssize_t nar static PyObject * test_keywords_kwonly_opt_impl(PyObject *module, PyObject *a, PyObject *b, PyObject *c) -/*[clinic end generated code: output=3937da2a8233ebe0 input=18393cc64fa000f4]*/ +/*[clinic end generated code: output=94114334841ff55a input=18393cc64fa000f4]*/ /*[clinic input] @@ -2605,7 +2610,8 @@ test_posonly_keywords(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *a; PyObject *b; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2619,7 +2625,7 @@ test_posonly_keywords(PyObject *module, PyObject *const *args, Py_ssize_t nargs, static PyObject * test_posonly_keywords_impl(PyObject *module, PyObject *a, PyObject *b) -/*[clinic end generated code: output=6b4f6dd5f4db3877 input=1767b0ebdf06060e]*/ +/*[clinic end generated code: output=4abfbb43b77fd29a input=1767b0ebdf06060e]*/ /*[clinic input] @@ -2676,7 +2682,8 @@ test_posonly_kwonly(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P PyObject *a; PyObject *c; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 1, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 1, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2690,7 +2697,7 @@ test_posonly_kwonly(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P static PyObject * test_posonly_kwonly_impl(PyObject *module, PyObject *a, PyObject *c) -/*[clinic end generated code: output=8bef2a8198e70b26 input=9042f2818f664839]*/ +/*[clinic end generated code: output=9fe05c76d80d63a9 input=9042f2818f664839]*/ /*[clinic input] @@ -2750,7 +2757,8 @@ test_posonly_keywords_kwonly(PyObject *module, PyObject *const *args, Py_ssize_t PyObject *b; PyObject *c; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 1, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 1, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2766,7 +2774,7 @@ test_posonly_keywords_kwonly(PyObject *module, PyObject *const *args, Py_ssize_t static PyObject * test_posonly_keywords_kwonly_impl(PyObject *module, PyObject *a, PyObject *b, PyObject *c) -/*[clinic end generated code: output=a44b8ae8300955e1 input=29546ebdca492fea]*/ +/*[clinic end generated code: output=de2b8c631dc1b5d3 input=29546ebdca492fea]*/ /*[clinic input] @@ -2828,7 +2836,8 @@ test_posonly_keywords_opt(PyObject *module, PyObject *const *args, Py_ssize_t na PyObject *c = Py_None; PyObject *d = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 4, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 4, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2854,7 +2863,7 @@ test_posonly_keywords_opt(PyObject *module, PyObject *const *args, Py_ssize_t na static PyObject * test_posonly_keywords_opt_impl(PyObject *module, PyObject *a, PyObject *b, PyObject *c, PyObject *d) -/*[clinic end generated code: output=cae6647c9e8e0238 input=cdf5a9625e554e9b]*/ +/*[clinic end generated code: output=70002e6beef31864 input=cdf5a9625e554e9b]*/ /*[clinic input] @@ -2914,7 +2923,8 @@ test_posonly_keywords_opt2(PyObject *module, PyObject *const *args, Py_ssize_t n PyObject *b = Py_None; PyObject *c = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2939,7 +2949,7 @@ test_posonly_keywords_opt2(PyObject *module, PyObject *const *args, Py_ssize_t n static PyObject * test_posonly_keywords_opt2_impl(PyObject *module, PyObject *a, PyObject *b, PyObject *c) -/*[clinic end generated code: output=6526fd08aafa2149 input=1581299d21d16f14]*/ +/*[clinic end generated code: output=b0f78abf242565cf input=1581299d21d16f14]*/ /*[clinic input] @@ -3001,7 +3011,8 @@ test_posonly_opt_keywords_opt(PyObject *module, PyObject *const *args, Py_ssize_ PyObject *c = Py_None; PyObject *d = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 4, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 4, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -3032,7 +3043,7 @@ test_posonly_opt_keywords_opt(PyObject *module, PyObject *const *args, Py_ssize_ static PyObject * test_posonly_opt_keywords_opt_impl(PyObject *module, PyObject *a, PyObject *b, PyObject *c, PyObject *d) -/*[clinic end generated code: output=b8d01e98443738c2 input=408798ec3d42949f]*/ +/*[clinic end generated code: output=75708b03d2837419 input=408798ec3d42949f]*/ /*[clinic input] @@ -3095,7 +3106,8 @@ test_posonly_kwonly_opt(PyObject *module, PyObject *const *args, Py_ssize_t narg PyObject *c = Py_None; PyObject *d = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 1, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 1, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -3121,7 +3133,7 @@ test_posonly_kwonly_opt(PyObject *module, PyObject *const *args, Py_ssize_t narg static PyObject * test_posonly_kwonly_opt_impl(PyObject *module, PyObject *a, PyObject *b, PyObject *c, PyObject *d) -/*[clinic end generated code: output=81d71c288f13d4dc input=8d8e5643bbbc2309]*/ +/*[clinic end generated code: output=3aab93ed7b9cc713 input=8d8e5643bbbc2309]*/ /*[clinic input] @@ -3182,7 +3194,8 @@ test_posonly_kwonly_opt2(PyObject *module, PyObject *const *args, Py_ssize_t nar PyObject *b = Py_None; PyObject *c = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -3207,7 +3220,7 @@ test_posonly_kwonly_opt2(PyObject *module, PyObject *const *args, Py_ssize_t nar static PyObject * test_posonly_kwonly_opt2_impl(PyObject *module, PyObject *a, PyObject *b, PyObject *c) -/*[clinic end generated code: output=a717d2a1a3310289 input=f7e5eed94f75fff0]*/ +/*[clinic end generated code: output=ac73a9a84a83e0c7 input=f7e5eed94f75fff0]*/ /*[clinic input] @@ -3270,7 +3283,8 @@ test_posonly_opt_kwonly_opt(PyObject *module, PyObject *const *args, Py_ssize_t PyObject *c = Py_None; PyObject *d = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -3301,7 +3315,7 @@ test_posonly_opt_kwonly_opt(PyObject *module, PyObject *const *args, Py_ssize_t static PyObject * test_posonly_opt_kwonly_opt_impl(PyObject *module, PyObject *a, PyObject *b, PyObject *c, PyObject *d) -/*[clinic end generated code: output=0f50b4b8d45cf2de input=1e557dc979d120fd]*/ +/*[clinic end generated code: output=21a1bfee0c50ec98 input=1e557dc979d120fd]*/ /*[clinic input] @@ -3367,7 +3381,8 @@ test_posonly_keywords_kwonly_opt(PyObject *module, PyObject *const *args, Py_ssi PyObject *d = Py_None; PyObject *e = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 1, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 1, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -3395,7 +3410,7 @@ static PyObject * test_posonly_keywords_kwonly_opt_impl(PyObject *module, PyObject *a, PyObject *b, PyObject *c, PyObject *d, PyObject *e) -/*[clinic end generated code: output=8dac8d2a4e6105fa input=c3884a4f956fdc89]*/ +/*[clinic end generated code: output=60ac8589c5331fc9 input=c3884a4f956fdc89]*/ /*[clinic input] @@ -3458,7 +3473,8 @@ test_posonly_keywords_kwonly_opt2(PyObject *module, PyObject *const *args, Py_ss PyObject *c = Py_None; PyObject *d = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -3484,7 +3500,7 @@ test_posonly_keywords_kwonly_opt2(PyObject *module, PyObject *const *args, Py_ss static PyObject * test_posonly_keywords_kwonly_opt2_impl(PyObject *module, PyObject *a, PyObject *b, PyObject *c, PyObject *d) -/*[clinic end generated code: output=5a96d521e6414f5d input=68d01d7c0f6dafb0]*/ +/*[clinic end generated code: output=349e1a19c507956a input=68d01d7c0f6dafb0]*/ /*[clinic input] @@ -3551,7 +3567,8 @@ test_posonly_keywords_opt_kwonly_opt(PyObject *module, PyObject *const *args, Py PyObject *d = Py_None; PyObject *e = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -3588,7 +3605,7 @@ static PyObject * test_posonly_keywords_opt_kwonly_opt_impl(PyObject *module, PyObject *a, PyObject *b, PyObject *c, PyObject *d, PyObject *e) -/*[clinic end generated code: output=d5a474dcd5dc3e9f input=d0883d45876f186c]*/ +/*[clinic end generated code: output=94787c9e20861a56 input=d0883d45876f186c]*/ /*[clinic input] @@ -3655,7 +3672,8 @@ test_posonly_keywords_opt2_kwonly_opt(PyObject *module, PyObject *const *args, P PyObject *d = Py_None; PyObject *e = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -3697,7 +3715,7 @@ static PyObject * test_posonly_keywords_opt2_kwonly_opt_impl(PyObject *module, PyObject *a, PyObject *b, PyObject *c, PyObject *d, PyObject *e) -/*[clinic end generated code: output=ac239c5ee8a74408 input=c95e2e1ec93035ad]*/ +/*[clinic end generated code: output=ea8664ae9fdb4769 input=c95e2e1ec93035ad]*/ /*[clinic input] @@ -3767,7 +3785,8 @@ test_posonly_opt_keywords_opt_kwonly_opt(PyObject *module, PyObject *const *args PyObject *e = Py_None; PyObject *f = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 4, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 4, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -3816,7 +3835,7 @@ test_posonly_opt_keywords_opt_kwonly_opt_impl(PyObject *module, PyObject *a, PyObject *b, PyObject *c, PyObject *d, PyObject *e, PyObject *f) -/*[clinic end generated code: output=638bbd0005639342 input=9914857713c5bbf8]*/ +/*[clinic end generated code: output=6e0ee12cf458acb1 input=9914857713c5bbf8]*/ /*[clinic input] test_keyword_only_parameter @@ -3871,7 +3890,8 @@ test_keyword_only_parameter(PyObject *module, PyObject *const *args, Py_ssize_t Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; PyBytesObject *co_lnotab = (PyBytesObject *)self->co_lnotab; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 0, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -3892,7 +3912,7 @@ test_keyword_only_parameter(PyObject *module, PyObject *const *args, Py_ssize_t static PyObject * test_keyword_only_parameter_impl(PyObject *module, PyBytesObject *co_lnotab) -/*[clinic end generated code: output=b12fe2e515a62603 input=303df5046c7e37a3]*/ +/*[clinic end generated code: output=18b5b3c80412bc4a input=303df5046c7e37a3]*/ /*[clinic input] @@ -4270,7 +4290,8 @@ test_vararg(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject PyObject *a; PyObject *__clinic_args = NULL; - fastargs = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 1, argsbuf); if (!fastargs) { goto exit; } @@ -4292,7 +4313,7 @@ test_vararg(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject static PyObject * test_vararg_impl(PyObject *module, PyObject *a, PyObject *args) -/*[clinic end generated code: output=e7d7da6a7e008125 input=7448995636d9186a]*/ +/*[clinic end generated code: output=5fa7dc68243a5211 input=7448995636d9186a]*/ /*[clinic input] test_vararg_with_default @@ -4352,7 +4373,8 @@ test_vararg_with_default(PyObject *module, PyObject *const *args, Py_ssize_t nar PyObject *__clinic_args = NULL; int b = 0; - fastargs = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 1, argsbuf); if (!fastargs) { goto exit; } @@ -4383,7 +4405,7 @@ test_vararg_with_default(PyObject *module, PyObject *const *args, Py_ssize_t nar static PyObject * test_vararg_with_default_impl(PyObject *module, PyObject *a, PyObject *args, int b) -/*[clinic end generated code: output=46781f9920ecedcf input=3a0f9f557ce1f712]*/ +/*[clinic end generated code: output=603ae52bfa307337 input=3a0f9f557ce1f712]*/ /*[clinic input] test_vararg_with_only_defaults @@ -4443,7 +4465,8 @@ test_vararg_with_only_defaults(PyObject *module, PyObject *const *args, Py_ssize int b = 0; PyObject *c = " "; - fastargs = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 0, /*minkw*/ 0, /*varpos*/ 1, argsbuf); if (!fastargs) { goto exit; } @@ -4477,7 +4500,7 @@ test_vararg_with_only_defaults(PyObject *module, PyObject *const *args, Py_ssize static PyObject * test_vararg_with_only_defaults_impl(PyObject *module, PyObject *args, int b, PyObject *c) -/*[clinic end generated code: output=d03daf5067039c03 input=6983e66817f82924]*/ +/*[clinic end generated code: output=195fe286176f4ee8 input=6983e66817f82924]*/ /*[clinic input] test_paramname_module @@ -4528,7 +4551,8 @@ test_paramname_module(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *argsbuf[1]; PyObject *mod; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -4541,7 +4565,7 @@ test_paramname_module(PyObject *module, PyObject *const *args, Py_ssize_t nargs, static PyObject * test_paramname_module_impl(PyObject *module, PyObject *mod) -/*[clinic end generated code: output=4a2a849ecbcc8b53 input=afefe259667f13ba]*/ +/*[clinic end generated code: output=7ab63a2adaea2873 input=afefe259667f13ba]*/ /*[clinic input] mangle1 @@ -4613,7 +4637,8 @@ mangle1(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwn PyObject *__clinic_nargs; PyObject *__clinic_noptargs; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 9, 9, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 9, /*maxpos*/ 9, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -4637,7 +4662,7 @@ mangle1_impl(PyObject *module, PyObject *args, PyObject *kwnames, PyObject *return_value, PyObject *_keywords, PyObject *_parser, PyObject *argsbuf, PyObject *fastargs, PyObject *nargs, PyObject *noptargs) -/*[clinic end generated code: output=083e5076be9987c3 input=a3ed51bdedf8a3c7]*/ +/*[clinic end generated code: output=f9127cea2f508da3 input=a3ed51bdedf8a3c7]*/ /*[clinic input] mangle2 @@ -4694,7 +4719,8 @@ mangle2(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwn PyObject *__clinic_kwargs; PyObject *__clinic_return_value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 3, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 3, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -4710,7 +4736,7 @@ mangle2(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwn static PyObject * mangle2_impl(PyObject *module, PyObject *args, PyObject *kwargs, PyObject *return_value) -/*[clinic end generated code: output=2ebb62aaefe7590a input=391766fee51bad7a]*/ +/*[clinic end generated code: output=3afbc3260971e5d3 input=391766fee51bad7a]*/ /*[clinic input] @@ -4763,7 +4789,8 @@ Test_cls_with_param(TestObj *self, PyTypeObject *cls, PyObject *const *args, Py_ PyObject *argsbuf[1]; int a; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -4779,7 +4806,7 @@ Test_cls_with_param(TestObj *self, PyTypeObject *cls, PyObject *const *args, Py_ static PyObject * Test_cls_with_param_impl(TestObj *self, PyTypeObject *cls, int a) -/*[clinic end generated code: output=a3a968137b0f320a input=af158077bd237ef9]*/ +/*[clinic end generated code: output=83a391eea66d08f8 input=af158077bd237ef9]*/ /*[clinic input] @@ -5174,7 +5201,8 @@ Test___init__(PyObject *self, PyObject *args, PyObject *kwargs) Py_ssize_t nargs = PyTuple_GET_SIZE(args); PyObject *a; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 1, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -5187,7 +5215,7 @@ Test___init__(PyObject *self, PyObject *args, PyObject *kwargs) static int Test___init___impl(TestObj *self, PyObject *a) -/*[clinic end generated code: output=0e1239b9bc247bc1 input=a8f9222a6ab35c59]*/ +/*[clinic end generated code: output=aa1f3d73e611e438 input=a8f9222a6ab35c59]*/ /*[clinic input] @@ -5440,7 +5468,8 @@ mangled_c_keyword_identifier(PyObject *module, PyObject *const *args, Py_ssize_t PyObject *argsbuf[1]; int int_value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -5456,7 +5485,7 @@ mangled_c_keyword_identifier(PyObject *module, PyObject *const *args, Py_ssize_t static PyObject * mangled_c_keyword_identifier_impl(PyObject *module, int int_value) -/*[clinic end generated code: output=01a8088b57632916 input=060876448ab567a2]*/ +/*[clinic end generated code: output=19ec1a02f0819cf7 input=060876448ab567a2]*/ /*[clinic input] @@ -5699,7 +5728,8 @@ fn_with_default_binop_expr(PyObject *module, PyObject *const *args, Py_ssize_t n Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; PyObject *arg = CONST_A + CONST_B; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -5716,7 +5746,7 @@ fn_with_default_binop_expr(PyObject *module, PyObject *const *args, Py_ssize_t n static PyObject * fn_with_default_binop_expr_impl(PyObject *module, PyObject *arg) -/*[clinic end generated code: output=018672772e4092ff input=1b55c8ae68d89453]*/ +/*[clinic end generated code: output=e15a6f5d9f7bea0c input=1b55c8ae68d89453]*/ /*[python input] @@ -5787,7 +5817,8 @@ docstr_fallback_to_converter_default(PyObject *module, PyObject *const *args, Py PyObject *argsbuf[1]; str a; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -5802,7 +5833,7 @@ docstr_fallback_to_converter_default(PyObject *module, PyObject *const *args, Py static PyObject * docstr_fallback_to_converter_default_impl(PyObject *module, str a) -/*[clinic end generated code: output=3fff7b702f0065cb input=0cbe6a4d24bc2274]*/ +/*[clinic end generated code: output=15bc9f32945ed0da input=0cbe6a4d24bc2274]*/ /*[clinic input] diff --git a/Lib/test/test_call.py b/Lib/test/test_call.py index 68e3b2a0d4d932..9d5256b566b8af 100644 --- a/Lib/test/test_call.py +++ b/Lib/test/test_call.py @@ -168,7 +168,7 @@ def test_varargs17_kw(self): print, 0, sep=1, end=2, file=3, flush=4, foo=5) def test_varargs18_kw(self): - # _PyArg_UnpackKeywordsWithVararg() + # _PyArg_UnpackKeywords() with varpos msg = r"invalid keyword argument for print\(\)$" with self.assertRaisesRegex(TypeError, msg): print(0, 1, **{BadStr('foo'): ','}) diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index 3936acd81d4d5a..11054963b6ff03 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -2703,7 +2703,7 @@ def test_cli_force(self): # Verify by checking the checksum. checksum = ( "/*[clinic end generated code: " - "output=0acbef4794cb933e input=9543a8d2da235301]*/\n" + "output=00512eb783a9b748 input=9543a8d2da235301]*/\n" ) with open(fn, encoding='utf-8') as f: generated = f.read() diff --git a/Modules/_ctypes/clinic/_ctypes.c.h b/Modules/_ctypes/clinic/_ctypes.c.h index e1d5a17cbe7d68..1332ba04cdfecd 100644 --- a/Modules/_ctypes/clinic/_ctypes.c.h +++ b/Modules/_ctypes/clinic/_ctypes.c.h @@ -65,7 +65,8 @@ CDataType_from_address(PyObject *type, PyTypeObject *cls, PyObject *const *args, PyObject *argsbuf[1]; PyObject *value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -112,7 +113,8 @@ CDataType_from_buffer(PyObject *type, PyTypeObject *cls, PyObject *const *args, PyObject *obj; Py_ssize_t offset = 0; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -175,7 +177,8 @@ CDataType_from_buffer_copy(PyObject *type, PyTypeObject *cls, PyObject *const *a Py_buffer buffer = {NULL, NULL}; Py_ssize_t offset = 0; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -245,7 +248,8 @@ CDataType_in_dll(PyObject *type, PyTypeObject *cls, PyObject *const *args, Py_ss PyObject *dll; const char *name; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -301,7 +305,8 @@ CDataType_from_param(PyObject *type, PyTypeObject *cls, PyObject *const *args, P PyObject *argsbuf[1]; PyObject *value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -344,7 +349,8 @@ PyCPointerType_set_type(PyTypeObject *self, PyTypeObject *cls, PyObject *const * PyObject *argsbuf[1]; PyObject *type; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -388,7 +394,8 @@ PyCPointerType_from_param(PyObject *type, PyTypeObject *cls, PyObject *const *ar PyObject *argsbuf[1]; PyObject *value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -430,7 +437,8 @@ c_wchar_p_from_param(PyObject *type, PyTypeObject *cls, PyObject *const *args, P PyObject *argsbuf[1]; PyObject *value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -472,7 +480,8 @@ c_char_p_from_param(PyObject *type, PyTypeObject *cls, PyObject *const *args, Py PyObject *argsbuf[1]; PyObject *value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -514,7 +523,8 @@ c_void_p_from_param(PyObject *type, PyTypeObject *cls, PyObject *const *args, Py PyObject *argsbuf[1]; PyObject *value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -558,7 +568,8 @@ PyCSimpleType_from_param(PyObject *type, PyTypeObject *cls, PyObject *const *arg PyObject *argsbuf[1]; PyObject *value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -610,4 +621,4 @@ Simple_from_outparm(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py } return Simple_from_outparm_impl(self, cls); } -/*[clinic end generated code: output=a90886be2a294ee6 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=52724c091e3a8b8d input=a9049054013a1b77]*/ diff --git a/Modules/_ctypes/clinic/cfield.c.h b/Modules/_ctypes/clinic/cfield.c.h index df5da783e050ed..bbf108a1a07b67 100644 --- a/Modules/_ctypes/clinic/cfield.c.h +++ b/Modules/_ctypes/clinic/cfield.c.h @@ -54,7 +54,8 @@ PyCField_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) Py_ssize_t index; PyObject *bit_size_obj = Py_None; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 5, 6, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 5, /*maxpos*/ 6, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -110,4 +111,4 @@ PyCField_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=27c010bae9be7213 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=6b450bdd861571e7 input=a9049054013a1b77]*/ diff --git a/Modules/_io/clinic/_iomodule.c.h b/Modules/_io/clinic/_iomodule.c.h index 112408a95df036..82932a23331ab6 100644 --- a/Modules/_io/clinic/_iomodule.c.h +++ b/Modules/_io/clinic/_iomodule.c.h @@ -175,7 +175,8 @@ _io_open(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kw int closefd = 1; PyObject *opener = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 8, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 8, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -390,7 +391,8 @@ _io_open_code(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec PyObject *argsbuf[1]; PyObject *path; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -404,4 +406,4 @@ _io_open_code(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec exit: return return_value; } -/*[clinic end generated code: output=5d60f4e778a600a4 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=ec1df2ff5265ab16 input=a9049054013a1b77]*/ diff --git a/Modules/_io/clinic/bufferedio.c.h b/Modules/_io/clinic/bufferedio.c.h index 708bef638887e2..b703d7e6855398 100644 --- a/Modules/_io/clinic/bufferedio.c.h +++ b/Modules/_io/clinic/bufferedio.c.h @@ -151,7 +151,8 @@ _io__BufferedIOBase_read(PyObject *self, PyTypeObject *cls, PyObject *const *arg PyObject *argsbuf[1]; int size = -1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -205,7 +206,8 @@ _io__BufferedIOBase_read1(PyObject *self, PyTypeObject *cls, PyObject *const *ar PyObject *argsbuf[1]; int size = -1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -262,7 +264,8 @@ _io__BufferedIOBase_write(PyObject *self, PyTypeObject *cls, PyObject *const *ar PyObject *argsbuf[1]; PyObject *b; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -910,7 +913,8 @@ _io__Buffered_truncate(buffered *self, PyTypeObject *cls, PyObject *const *args, PyObject *argsbuf[1]; PyObject *pos = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -973,7 +977,8 @@ _io_BufferedReader___init__(PyObject *self, PyObject *args, PyObject *kwargs) PyObject *raw; Py_ssize_t buffer_size = DEFAULT_BUFFER_SIZE; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 2, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -1050,7 +1055,8 @@ _io_BufferedWriter___init__(PyObject *self, PyObject *args, PyObject *kwargs) PyObject *raw; Py_ssize_t buffer_size = DEFAULT_BUFFER_SIZE; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 2, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -1219,7 +1225,8 @@ _io_BufferedRandom___init__(PyObject *self, PyObject *args, PyObject *kwargs) PyObject *raw; Py_ssize_t buffer_size = DEFAULT_BUFFER_SIZE; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 2, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -1245,4 +1252,4 @@ _io_BufferedRandom___init__(PyObject *self, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=8eead000083dc5fa input=a9049054013a1b77]*/ +/*[clinic end generated code: output=36abca5bd2f63ea1 input=a9049054013a1b77]*/ diff --git a/Modules/_io/clinic/bytesio.c.h b/Modules/_io/clinic/bytesio.c.h index 620e9e3b84ea19..98d88698c5e9b7 100644 --- a/Modules/_io/clinic/bytesio.c.h +++ b/Modules/_io/clinic/bytesio.c.h @@ -520,7 +520,8 @@ _io_BytesIO___init__(PyObject *self, PyObject *args, PyObject *kwargs) Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 0; PyObject *initvalue = NULL; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 0, 1, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -534,4 +535,4 @@ _io_BytesIO___init__(PyObject *self, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=ef116925b8b9e535 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=985ff54e89f6036e input=a9049054013a1b77]*/ diff --git a/Modules/_io/clinic/fileio.c.h b/Modules/_io/clinic/fileio.c.h index 5b5487d63eba90..0b8b4a49ac24b6 100644 --- a/Modules/_io/clinic/fileio.c.h +++ b/Modules/_io/clinic/fileio.c.h @@ -94,7 +94,8 @@ _io_FileIO___init__(PyObject *self, PyObject *args, PyObject *kwargs) int closefd = 1; PyObject *opener = Py_None; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 4, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 1, /*maxpos*/ 4, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -241,7 +242,8 @@ _io_FileIO_readinto(fileio *self, PyTypeObject *cls, PyObject *const *args, Py_s PyObject *argsbuf[1]; Py_buffer buffer = {NULL, NULL}; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -317,7 +319,8 @@ _io_FileIO_read(fileio *self, PyTypeObject *cls, PyObject *const *args, Py_ssize PyObject *argsbuf[1]; Py_ssize_t size = -1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -370,7 +373,8 @@ _io_FileIO_write(fileio *self, PyTypeObject *cls, PyObject *const *args, Py_ssiz PyObject *argsbuf[1]; Py_buffer b = {NULL, NULL}; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -490,7 +494,8 @@ _io_FileIO_truncate(fileio *self, PyTypeObject *cls, PyObject *const *args, Py_s PyObject *argsbuf[1]; PyObject *posobj = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -528,4 +533,4 @@ _io_FileIO_isatty(fileio *self, PyObject *Py_UNUSED(ignored)) #ifndef _IO_FILEIO_TRUNCATE_METHODDEF #define _IO_FILEIO_TRUNCATE_METHODDEF #endif /* !defined(_IO_FILEIO_TRUNCATE_METHODDEF) */ -/*[clinic end generated code: output=e3d9446b4087020e input=a9049054013a1b77]*/ +/*[clinic end generated code: output=1c262ae135da4dcb input=a9049054013a1b77]*/ diff --git a/Modules/_io/clinic/iobase.c.h b/Modules/_io/clinic/iobase.c.h index a35cac7dc0b8d7..402448545dfc51 100644 --- a/Modules/_io/clinic/iobase.c.h +++ b/Modules/_io/clinic/iobase.c.h @@ -56,7 +56,8 @@ _io__IOBase_seek(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ss int offset; int whence = 0; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -132,7 +133,8 @@ _io__IOBase_truncate(PyObject *self, PyTypeObject *cls, PyObject *const *args, P PyObject *argsbuf[1]; PyObject *size = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -441,4 +443,4 @@ _io__RawIOBase_readall(PyObject *self, PyObject *Py_UNUSED(ignored)) { return _io__RawIOBase_readall_impl(self); } -/*[clinic end generated code: output=dab5e9323d191e32 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=9359e74d95534bef input=a9049054013a1b77]*/ diff --git a/Modules/_io/clinic/stringio.c.h b/Modules/_io/clinic/stringio.c.h index 6bdb2181985f7d..ac8b4b7b85b76d 100644 --- a/Modules/_io/clinic/stringio.c.h +++ b/Modules/_io/clinic/stringio.c.h @@ -336,7 +336,8 @@ _io_StringIO___init__(PyObject *self, PyObject *args, PyObject *kwargs) PyObject *value = NULL; PyObject *newline_obj = NULL; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 0, 2, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -555,4 +556,4 @@ _io_StringIO_newlines_get(stringio *self, void *Py_UNUSED(context)) return return_value; } -/*[clinic end generated code: output=9ffea20cd32d4cd8 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=8c8d4f8fa32986bb input=a9049054013a1b77]*/ diff --git a/Modules/_io/clinic/textio.c.h b/Modules/_io/clinic/textio.c.h index 669e2aa637ebbf..c9301c5a23fa86 100644 --- a/Modules/_io/clinic/textio.c.h +++ b/Modules/_io/clinic/textio.c.h @@ -70,7 +70,8 @@ _io__TextIOBase_read(PyObject *self, PyTypeObject *cls, PyObject *const *args, P PyObject *argsbuf[1]; int size = -1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -124,7 +125,8 @@ _io__TextIOBase_readline(PyObject *self, PyTypeObject *cls, PyObject *const *arg PyObject *argsbuf[1]; int size = -1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -178,7 +180,8 @@ _io__TextIOBase_write(PyObject *self, PyTypeObject *cls, PyObject *const *args, PyObject *argsbuf[1]; const char *s; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -339,7 +342,8 @@ _io_IncrementalNewlineDecoder___init__(PyObject *self, PyObject *args, PyObject int translate; PyObject *errors = NULL; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 2, 3, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 2, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -405,7 +409,8 @@ _io_IncrementalNewlineDecoder_decode(nldecoder_object *self, PyObject *const *ar PyObject *input; int final = 0; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -546,7 +551,8 @@ _io_TextIOWrapper___init__(PyObject *self, PyObject *args, PyObject *kwargs) int line_buffering = 0; int write_through = 0; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 6, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 1, /*maxpos*/ 6, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -681,7 +687,8 @@ _io_TextIOWrapper_reconfigure(textio *self, PyObject *const *args, Py_ssize_t na PyObject *line_buffering_obj = Py_None; PyObject *write_through_obj = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 0, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1292,4 +1299,4 @@ _io_TextIOWrapper__CHUNK_SIZE_set(textio *self, PyObject *value, void *Py_UNUSED return return_value; } -/*[clinic end generated code: output=04cb7c67791a9ec1 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=459c0e50acd772b1 input=a9049054013a1b77]*/ diff --git a/Modules/_io/clinic/winconsoleio.c.h b/Modules/_io/clinic/winconsoleio.c.h index 4696ecc5c843e6..df281dfeb13fef 100644 --- a/Modules/_io/clinic/winconsoleio.c.h +++ b/Modules/_io/clinic/winconsoleio.c.h @@ -93,7 +93,8 @@ _io__WindowsConsoleIO___init__(PyObject *self, PyObject *args, PyObject *kwargs) int closefd = 1; PyObject *opener = Py_None; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 4, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 1, /*maxpos*/ 4, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -239,7 +240,8 @@ _io__WindowsConsoleIO_readinto(winconsoleio *self, PyTypeObject *cls, PyObject * PyObject *argsbuf[1]; Py_buffer buffer = {NULL, NULL}; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -323,7 +325,8 @@ _io__WindowsConsoleIO_read(winconsoleio *self, PyTypeObject *cls, PyObject *cons PyObject *argsbuf[1]; Py_ssize_t size = -1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -380,7 +383,8 @@ _io__WindowsConsoleIO_write(winconsoleio *self, PyTypeObject *cls, PyObject *con PyObject *argsbuf[1]; Py_buffer b = {NULL, NULL}; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -457,4 +461,4 @@ _io__WindowsConsoleIO_isatty(winconsoleio *self, PyObject *Py_UNUSED(ignored)) #ifndef _IO__WINDOWSCONSOLEIO_ISATTY_METHODDEF #define _IO__WINDOWSCONSOLEIO_ISATTY_METHODDEF #endif /* !defined(_IO__WINDOWSCONSOLEIO_ISATTY_METHODDEF) */ -/*[clinic end generated code: output=2c2bc86713b21dd6 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=78e0f6abf4de2d6d input=a9049054013a1b77]*/ diff --git a/Modules/_multiprocessing/clinic/semaphore.c.h b/Modules/_multiprocessing/clinic/semaphore.c.h index 512e5a016192fb..2702c3369c76ed 100644 --- a/Modules/_multiprocessing/clinic/semaphore.c.h +++ b/Modules/_multiprocessing/clinic/semaphore.c.h @@ -58,7 +58,8 @@ _multiprocessing_SemLock_acquire(SemLockObject *self, PyObject *const *args, Py_ int blocking = 1; PyObject *timeout_obj = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -163,7 +164,8 @@ _multiprocessing_SemLock_acquire(SemLockObject *self, PyObject *const *args, Py_ int blocking = 1; PyObject *timeout_obj = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -263,7 +265,8 @@ _multiprocessing_SemLock(PyTypeObject *type, PyObject *args, PyObject *kwargs) const char *name; int unlink; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 5, 5, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 5, /*maxpos*/ 5, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -573,4 +576,4 @@ _multiprocessing_SemLock___exit__(SemLockObject *self, PyObject *const *args, Py #ifndef _MULTIPROCESSING_SEMLOCK___EXIT___METHODDEF #define _MULTIPROCESSING_SEMLOCK___EXIT___METHODDEF #endif /* !defined(_MULTIPROCESSING_SEMLOCK___EXIT___METHODDEF) */ -/*[clinic end generated code: output=dea36482d23a355f input=a9049054013a1b77]*/ +/*[clinic end generated code: output=9023d3e48a24afd2 input=a9049054013a1b77]*/ diff --git a/Modules/_sqlite/clinic/connection.c.h b/Modules/_sqlite/clinic/connection.c.h index 6cb610a12b59c6..42eb6eb2f12554 100644 --- a/Modules/_sqlite/clinic/connection.c.h +++ b/Modules/_sqlite/clinic/connection.c.h @@ -81,7 +81,8 @@ pysqlite_connection_init(PyObject *self, PyObject *args, PyObject *kwargs) goto exit; } } - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 8, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 1, /*maxpos*/ 8, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -213,7 +214,8 @@ pysqlite_connection_cursor(pysqlite_Connection *self, PyObject *const *args, Py_ Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; PyObject *factory = NULL; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -289,7 +291,8 @@ blobopen(pysqlite_Connection *self, PyObject *const *args, Py_ssize_t nargs, PyO int readonly = 0; const char *name = "main"; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 3, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 3, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -481,7 +484,8 @@ pysqlite_connection_create_function(pysqlite_Connection *self, PyTypeObject *cls PyObject *func; int deterministic = 0; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 3, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 3, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -574,7 +578,8 @@ create_window_function(pysqlite_Connection *self, PyTypeObject *cls, PyObject *c int num_params; PyObject *aggregate_class; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 3, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 3, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -670,7 +675,8 @@ pysqlite_connection_create_aggregate(pysqlite_Connection *self, PyTypeObject *cl int n_arg; PyObject *aggregate_class; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 3, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 3, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -770,7 +776,8 @@ pysqlite_connection_set_authorizer(pysqlite_Connection *self, PyTypeObject *cls, PyObject *argsbuf[1]; PyObject *callable; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -864,7 +871,8 @@ pysqlite_connection_set_progress_handler(pysqlite_Connection *self, PyTypeObject PyObject *callable; int n; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -951,7 +959,8 @@ pysqlite_connection_set_trace_callback(pysqlite_Connection *self, PyTypeObject * PyObject *argsbuf[1]; PyObject *callable; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1055,7 +1064,8 @@ pysqlite_connection_load_extension(pysqlite_Connection *self, PyObject *const *a const char *extension_name; const char *entrypoint = NULL; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1252,7 +1262,8 @@ pysqlite_connection_iterdump(pysqlite_Connection *self, PyObject *const *args, P Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; PyObject *filter = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 0, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1320,7 +1331,8 @@ pysqlite_connection_backup(pysqlite_Connection *self, PyObject *const *args, Py_ const char *name = "main"; double sleep = 0.25; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1418,7 +1430,8 @@ pysqlite_connection_create_collation(pysqlite_Connection *self, PyTypeObject *cl const char *name; PyObject *callable; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1497,7 +1510,8 @@ serialize(pysqlite_Connection *self, PyObject *const *args, Py_ssize_t nargs, Py Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; const char *name = "main"; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 0, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1587,7 +1601,8 @@ deserialize(pysqlite_Connection *self, PyObject *const *args, Py_ssize_t nargs, Py_buffer data = {NULL, NULL}; const char *name = "main"; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1866,4 +1881,4 @@ getconfig(pysqlite_Connection *self, PyObject *arg) #ifndef DESERIALIZE_METHODDEF #define DESERIALIZE_METHODDEF #endif /* !defined(DESERIALIZE_METHODDEF) */ -/*[clinic end generated code: output=5d4d7e4abe17b164 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=a8fd19301c7390cc input=a9049054013a1b77]*/ diff --git a/Modules/_sqlite/clinic/cursor.c.h b/Modules/_sqlite/clinic/cursor.c.h index a13e0d0745b58d..ca7823cf5aef5b 100644 --- a/Modules/_sqlite/clinic/cursor.c.h +++ b/Modules/_sqlite/clinic/cursor.c.h @@ -216,7 +216,8 @@ pysqlite_cursor_fetchmany(pysqlite_Cursor *self, PyObject *const *args, Py_ssize Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; int maxrows = self->arraysize; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -313,4 +314,4 @@ pysqlite_cursor_close(pysqlite_Cursor *self, PyObject *Py_UNUSED(ignored)) { return pysqlite_cursor_close_impl(self); } -/*[clinic end generated code: output=a8ce095c3c80cf65 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=f0804afc5f8646c1 input=a9049054013a1b77]*/ diff --git a/Modules/_sqlite/clinic/module.c.h b/Modules/_sqlite/clinic/module.c.h index 529dc4e281e0eb..5569c45451647f 100644 --- a/Modules/_sqlite/clinic/module.c.h +++ b/Modules/_sqlite/clinic/module.c.h @@ -52,7 +52,8 @@ pysqlite_complete_statement(PyObject *module, PyObject *const *args, Py_ssize_t PyObject *argsbuf[1]; const char *statement; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -208,4 +209,4 @@ pysqlite_adapt(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=457ab0fdbb9e1880 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=db9bf7ecad197343 input=a9049054013a1b77]*/ diff --git a/Modules/_sre/clinic/sre.c.h b/Modules/_sre/clinic/sre.c.h index 48336c7a2fca26..e287f3d5ad3991 100644 --- a/Modules/_sre/clinic/sre.c.h +++ b/Modules/_sre/clinic/sre.c.h @@ -213,7 +213,8 @@ _sre_SRE_Pattern_match(PatternObject *self, PyTypeObject *cls, PyObject *const * Py_ssize_t pos = 0; Py_ssize_t endpos = PY_SSIZE_T_MAX; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -306,7 +307,8 @@ _sre_SRE_Pattern_fullmatch(PatternObject *self, PyTypeObject *cls, PyObject *con Py_ssize_t pos = 0; Py_ssize_t endpos = PY_SSIZE_T_MAX; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -401,7 +403,8 @@ _sre_SRE_Pattern_search(PatternObject *self, PyTypeObject *cls, PyObject *const Py_ssize_t pos = 0; Py_ssize_t endpos = PY_SSIZE_T_MAX; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -493,7 +496,8 @@ _sre_SRE_Pattern_findall(PatternObject *self, PyObject *const *args, Py_ssize_t Py_ssize_t pos = 0; Py_ssize_t endpos = PY_SSIZE_T_MAX; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -588,7 +592,8 @@ _sre_SRE_Pattern_finditer(PatternObject *self, PyTypeObject *cls, PyObject *cons Py_ssize_t pos = 0; Py_ssize_t endpos = PY_SSIZE_T_MAX; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -680,7 +685,8 @@ _sre_SRE_Pattern_scanner(PatternObject *self, PyTypeObject *cls, PyObject *const Py_ssize_t pos = 0; Py_ssize_t endpos = PY_SSIZE_T_MAX; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -771,7 +777,8 @@ _sre_SRE_Pattern_split(PatternObject *self, PyObject *const *args, Py_ssize_t na PyObject *string; Py_ssize_t maxsplit = 0; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -846,7 +853,8 @@ _sre_SRE_Pattern_sub(PatternObject *self, PyTypeObject *cls, PyObject *const *ar PyObject *string; Py_ssize_t count = 0; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -923,7 +931,8 @@ _sre_SRE_Pattern_subn(PatternObject *self, PyTypeObject *cls, PyObject *const *a PyObject *string; Py_ssize_t count = 0; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1027,7 +1036,8 @@ _sre_compile(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject PyObject *groupindex; PyObject *indexgroup; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 6, 6, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 6, /*maxpos*/ 6, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1152,7 +1162,8 @@ _sre_SRE_Match_expand(MatchObject *self, PyObject *const *args, Py_ssize_t nargs PyObject *argsbuf[1]; PyObject *template; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1211,7 +1222,8 @@ _sre_SRE_Match_groups(MatchObject *self, PyObject *const *args, Py_ssize_t nargs Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; PyObject *default_value = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1274,7 +1286,8 @@ _sre_SRE_Match_groupdict(MatchObject *self, PyObject *const *args, Py_ssize_t na Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; PyObject *default_value = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1461,4 +1474,4 @@ _sre_SRE_Scanner_search(ScannerObject *self, PyTypeObject *cls, PyObject *const } return _sre_SRE_Scanner_search_impl(self, cls); } -/*[clinic end generated code: output=c3e711f0b2f43d66 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=afaa301d55957cb0 input=a9049054013a1b77]*/ diff --git a/Modules/_ssl/clinic/cert.c.h b/Modules/_ssl/clinic/cert.c.h index 0748d32291e3c5..19559442cd9b88 100644 --- a/Modules/_ssl/clinic/cert.c.h +++ b/Modules/_ssl/clinic/cert.c.h @@ -52,7 +52,8 @@ _ssl_Certificate_public_bytes(PySSLCertificate *self, PyObject *const *args, Py_ Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; int format = PY_SSL_ENCODING_PEM; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -86,4 +87,4 @@ _ssl_Certificate_get_info(PySSLCertificate *self, PyObject *Py_UNUSED(ignored)) { return _ssl_Certificate_get_info_impl(self); } -/*[clinic end generated code: output=5b668226f933a677 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=e5fa354db5fc56b4 input=a9049054013a1b77]*/ diff --git a/Modules/_testcapi/clinic/exceptions.c.h b/Modules/_testcapi/clinic/exceptions.c.h index a797444c1a72b9..7c18bac0f686e2 100644 --- a/Modules/_testcapi/clinic/exceptions.c.h +++ b/Modules/_testcapi/clinic/exceptions.c.h @@ -104,7 +104,8 @@ _testcapi_make_exception_with_doc(PyObject *module, PyObject *const *args, Py_ss PyObject *base = NULL; PyObject *dict = NULL; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 4, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 4, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -456,4 +457,4 @@ _testcapi_unstable_exc_prep_reraise_star(PyObject *module, PyObject *const *args exit: return return_value; } -/*[clinic end generated code: output=0b11ef105030a48e input=a9049054013a1b77]*/ +/*[clinic end generated code: output=d917e9ec082e69ee input=a9049054013a1b77]*/ diff --git a/Modules/cjkcodecs/clinic/multibytecodec.c.h b/Modules/cjkcodecs/clinic/multibytecodec.c.h index 73edd5c3b25553..7e7ea9e0fdfa8e 100644 --- a/Modules/cjkcodecs/clinic/multibytecodec.c.h +++ b/Modules/cjkcodecs/clinic/multibytecodec.c.h @@ -61,7 +61,8 @@ _multibytecodec_MultibyteCodec_encode(MultibyteCodecObject *self, PyObject *cons PyObject *input; const char *errors = NULL; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -147,7 +148,8 @@ _multibytecodec_MultibyteCodec_decode(MultibyteCodecObject *self, PyObject *cons Py_buffer input = {NULL, NULL}; const char *errors = NULL; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -234,7 +236,8 @@ _multibytecodec_MultibyteIncrementalEncoder_encode(MultibyteIncrementalEncoderOb PyObject *input; int final = 0; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -363,7 +366,8 @@ _multibytecodec_MultibyteIncrementalDecoder_decode(MultibyteIncrementalDecoderOb Py_buffer input = {NULL, NULL}; int final = 0; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -598,7 +602,8 @@ _multibytecodec_MultibyteStreamWriter_write(MultibyteStreamWriterObject *self, P PyObject *argsbuf[1]; PyObject *strobj; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -642,7 +647,8 @@ _multibytecodec_MultibyteStreamWriter_writelines(MultibyteStreamWriterObject *se PyObject *argsbuf[1]; PyObject *lines; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -682,4 +688,4 @@ PyDoc_STRVAR(_multibytecodec___create_codec__doc__, #define _MULTIBYTECODEC___CREATE_CODEC_METHODDEF \ {"__create_codec", (PyCFunction)_multibytecodec___create_codec, METH_O, _multibytecodec___create_codec__doc__}, -/*[clinic end generated code: output=f09052c5a28cc6e6 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=60e1fa3a7615c148 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_asynciomodule.c.h b/Modules/clinic/_asynciomodule.c.h index d619a124ccead5..32045804c35004 100644 --- a/Modules/clinic/_asynciomodule.c.h +++ b/Modules/clinic/_asynciomodule.c.h @@ -63,7 +63,8 @@ _asyncio_Future___init__(PyObject *self, PyObject *args, PyObject *kwargs) Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 0; PyObject *loop = Py_None; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 0, 0, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 0, /*maxpos*/ 0, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -163,7 +164,8 @@ _asyncio_Future_set_result(FutureObj *self, PyTypeObject *cls, PyObject *const * PyObject *argsbuf[1]; PyObject *result; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -210,7 +212,8 @@ _asyncio_Future_set_exception(FutureObj *self, PyTypeObject *cls, PyObject *cons PyObject *argsbuf[1]; PyObject *exception; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -272,7 +275,8 @@ _asyncio_Future_add_done_callback(FutureObj *self, PyTypeObject *cls, PyObject * PyObject *fn; PyObject *context = NULL; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -323,7 +327,8 @@ _asyncio_Future_remove_done_callback(FutureObj *self, PyTypeObject *cls, PyObjec PyObject *argsbuf[1]; PyObject *fn; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -384,7 +389,8 @@ _asyncio_Future_cancel(FutureObj *self, PyTypeObject *cls, PyObject *const *args Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; PyObject *msg = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -531,7 +537,8 @@ _asyncio_Task___init__(PyObject *self, PyObject *args, PyObject *kwargs) PyObject *context = Py_None; int eager_start = 0; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 1, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -653,7 +660,8 @@ _asyncio_Task_cancel(TaskObj *self, PyObject *const *args, Py_ssize_t nargs, PyO Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; PyObject *msg = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -776,7 +784,8 @@ _asyncio_Task_get_stack(TaskObj *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; PyObject *limit = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 0, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -844,7 +853,8 @@ _asyncio_Task_print_stack(TaskObj *self, PyTypeObject *cls, PyObject *const *arg PyObject *limit = Py_None; PyObject *file = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 0, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1064,7 +1074,8 @@ _asyncio__register_task(PyObject *module, PyObject *const *args, Py_ssize_t narg PyObject *argsbuf[1]; PyObject *task; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1121,7 +1132,8 @@ _asyncio__register_eager_task(PyObject *module, PyObject *const *args, Py_ssize_ PyObject *argsbuf[1]; PyObject *task; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1178,7 +1190,8 @@ _asyncio__unregister_task(PyObject *module, PyObject *const *args, Py_ssize_t na PyObject *argsbuf[1]; PyObject *task; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1235,7 +1248,8 @@ _asyncio__unregister_eager_task(PyObject *module, PyObject *const *args, Py_ssiz PyObject *argsbuf[1]; PyObject *task; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1295,7 +1309,8 @@ _asyncio__enter_task(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *loop; PyObject *task; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1356,7 +1371,8 @@ _asyncio__leave_task(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *loop; PyObject *task; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1416,7 +1432,8 @@ _asyncio__swap_current_task(PyObject *module, PyObject *const *args, Py_ssize_t PyObject *loop; PyObject *task; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1473,7 +1490,8 @@ _asyncio_current_task(PyObject *module, PyObject *const *args, Py_ssize_t nargs, Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; PyObject *loop = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1533,7 +1551,8 @@ _asyncio_all_tasks(PyObject *module, PyObject *const *args, Py_ssize_t nargs, Py Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; PyObject *loop = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1547,4 +1566,4 @@ _asyncio_all_tasks(PyObject *module, PyObject *const *args, Py_ssize_t nargs, Py exit: return return_value; } -/*[clinic end generated code: output=ffe9b71bc65888b3 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=e5d95a0ec229ffcd input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_bisectmodule.c.h b/Modules/clinic/_bisectmodule.c.h index 528602c84a9b23..910fd52c07dd30 100644 --- a/Modules/clinic/_bisectmodule.c.h +++ b/Modules/clinic/_bisectmodule.c.h @@ -69,7 +69,8 @@ _bisect_bisect_right(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *key = Py_None; Py_ssize_t _return_value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 4, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 4, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -176,7 +177,8 @@ _bisect_insort_right(PyObject *module, PyObject *const *args, Py_ssize_t nargs, Py_ssize_t hi = -1; PyObject *key = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 4, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 4, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -282,7 +284,8 @@ _bisect_bisect_left(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P PyObject *key = Py_None; Py_ssize_t _return_value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 4, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 4, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -389,7 +392,8 @@ _bisect_insort_left(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P Py_ssize_t hi = -1; PyObject *key = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 4, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 4, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -434,4 +438,4 @@ _bisect_insort_left(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P exit: return return_value; } -/*[clinic end generated code: output=0a8d5a32dd0a3f04 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=972e8938ba5a0eac input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_bz2module.c.h b/Modules/clinic/_bz2module.c.h index de7b3993596446..93988bf48a1b00 100644 --- a/Modules/clinic/_bz2module.c.h +++ b/Modules/clinic/_bz2module.c.h @@ -170,7 +170,8 @@ _bz2_BZ2Decompressor_decompress(BZ2Decompressor *self, PyObject *const *args, Py Py_buffer data = {NULL, NULL}; Py_ssize_t max_length = -1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -234,4 +235,4 @@ _bz2_BZ2Decompressor(PyTypeObject *type, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=8daa62f47cc4853d input=a9049054013a1b77]*/ +/*[clinic end generated code: output=701a383434374c36 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_codecsmodule.c.h b/Modules/clinic/_codecsmodule.c.h index 01855aec5e123e..46476a948b66d9 100644 --- a/Modules/clinic/_codecsmodule.c.h +++ b/Modules/clinic/_codecsmodule.c.h @@ -123,7 +123,8 @@ _codecs_encode(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObje const char *encoding = NULL; const char *errors = NULL; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -223,7 +224,8 @@ _codecs_decode(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObje const char *encoding = NULL; const char *errors = NULL; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2796,4 +2798,4 @@ _codecs_lookup_error(PyObject *module, PyObject *arg) #ifndef _CODECS_CODE_PAGE_ENCODE_METHODDEF #define _CODECS_CODE_PAGE_ENCODE_METHODDEF #endif /* !defined(_CODECS_CODE_PAGE_ENCODE_METHODDEF) */ -/*[clinic end generated code: output=b3013d4709d96ffe input=a9049054013a1b77]*/ +/*[clinic end generated code: output=f8a7fdd0b7edc0c4 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_collectionsmodule.c.h b/Modules/clinic/_collectionsmodule.c.h index fa0ff2133a05d2..b4e3325e89502b 100644 --- a/Modules/clinic/_collectionsmodule.c.h +++ b/Modules/clinic/_collectionsmodule.c.h @@ -497,7 +497,8 @@ deque_init(PyObject *deque, PyObject *args, PyObject *kwargs) PyObject *iterable = NULL; PyObject *maxlenobj = NULL; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 0, 2, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -629,4 +630,4 @@ tuplegetter_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=64c32b16df7be07a input=a9049054013a1b77]*/ +/*[clinic end generated code: output=65f896fb13902f6d input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_csv.c.h b/Modules/clinic/_csv.c.h index 2442bdcde5679f..b5608c850b6a97 100644 --- a/Modules/clinic/_csv.c.h +++ b/Modules/clinic/_csv.c.h @@ -74,7 +74,8 @@ _csv_unregister_dialect(PyObject *module, PyObject *const *args, Py_ssize_t narg PyObject *argsbuf[1]; PyObject *name; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -131,7 +132,8 @@ _csv_get_dialect(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb PyObject *argsbuf[1]; PyObject *name; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -192,7 +194,8 @@ _csv_field_size_limit(PyObject *module, PyObject *const *args, Py_ssize_t nargs, Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; PyObject *new_limit = NULL; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -206,4 +209,4 @@ _csv_field_size_limit(PyObject *module, PyObject *const *args, Py_ssize_t nargs, exit: return return_value; } -/*[clinic end generated code: output=9ec59717f5414d8b input=a9049054013a1b77]*/ +/*[clinic end generated code: output=9c7314c2434156b3 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_curses_panel.c.h b/Modules/clinic/_curses_panel.c.h index c8788c461f745c..b6bff5274a3a91 100644 --- a/Modules/clinic/_curses_panel.c.h +++ b/Modules/clinic/_curses_panel.c.h @@ -185,7 +185,8 @@ _curses_panel_panel_move(PyCursesPanelObject *self, PyTypeObject *cls, PyObject int y; int x; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -255,7 +256,8 @@ _curses_panel_panel_replace(PyCursesPanelObject *self, PyTypeObject *cls, PyObje PyObject *argsbuf[1]; PyCursesWindowObject *win; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -303,7 +305,8 @@ _curses_panel_panel_set_userptr(PyCursesPanelObject *self, PyTypeObject *cls, Py PyObject *argsbuf[1]; PyObject *obj; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -421,4 +424,4 @@ _curses_panel_update_panels(PyObject *module, PyObject *Py_UNUSED(ignored)) { return _curses_panel_update_panels_impl(module); } -/*[clinic end generated code: output=18dc5571174c7189 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=298e49d54c0b14a0 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_cursesmodule.c.h b/Modules/clinic/_cursesmodule.c.h index 8399c5620f125b..524a114aba98bc 100644 --- a/Modules/clinic/_cursesmodule.c.h +++ b/Modules/clinic/_cursesmodule.c.h @@ -2714,7 +2714,8 @@ _curses_setupterm(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyO const char *term = NULL; int fd = -1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -4378,4 +4379,4 @@ _curses_has_extended_color_support(PyObject *module, PyObject *Py_UNUSED(ignored #ifndef _CURSES_USE_DEFAULT_COLORS_METHODDEF #define _CURSES_USE_DEFAULT_COLORS_METHODDEF #endif /* !defined(_CURSES_USE_DEFAULT_COLORS_METHODDEF) */ -/*[clinic end generated code: output=cd1273354b08948f input=a9049054013a1b77]*/ +/*[clinic end generated code: output=26fe38c09ff8ca44 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_datetimemodule.c.h b/Modules/clinic/_datetimemodule.c.h index 48499e0aaf7783..72c230fc8aee68 100644 --- a/Modules/clinic/_datetimemodule.c.h +++ b/Modules/clinic/_datetimemodule.c.h @@ -60,7 +60,8 @@ iso_calendar_date_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) int week; int weekday; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 3, 3, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 3, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -130,7 +131,8 @@ datetime_date_replace(PyDateTime_Date *self, PyObject *const *args, Py_ssize_t n int month = GET_MONTH(self); int day = GET_DAY(self); - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -219,7 +221,8 @@ datetime_time_replace(PyDateTime_Time *self, PyObject *const *args, Py_ssize_t n PyObject *tzinfo = HASTZINFO(self) ? self->tzinfo : Py_None; int fold = TIME_GET_FOLD(self); - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 5, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 5, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -333,7 +336,8 @@ datetime_datetime_now(PyTypeObject *type, PyObject *const *args, Py_ssize_t narg Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; PyObject *tz = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -406,7 +410,8 @@ datetime_datetime_replace(PyDateTime_DateTime *self, PyObject *const *args, Py_s PyObject *tzinfo = HASTZINFO(self) ? self->tzinfo : Py_None; int fold = DATE_GET_FOLD(self); - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 8, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 8, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -496,4 +501,4 @@ datetime_datetime_replace(PyDateTime_DateTime *self, PyObject *const *args, Py_s exit: return return_value; } -/*[clinic end generated code: output=c7a04b865b1e0890 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=203217a61ea14171 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_elementtree.c.h b/Modules/clinic/_elementtree.c.h index 1a5a820d1f00b5..07045e72040664 100644 --- a/Modules/clinic/_elementtree.c.h +++ b/Modules/clinic/_elementtree.c.h @@ -41,7 +41,8 @@ _elementtree_Element_append(ElementObject *self, PyTypeObject *cls, PyObject *co PyObject *argsbuf[1]; PyObject *subelement; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -198,7 +199,8 @@ _elementtree_Element___setstate__(ElementObject *self, PyTypeObject *cls, PyObje PyObject *argsbuf[1]; PyObject *state; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -241,7 +243,8 @@ _elementtree_Element_extend(ElementObject *self, PyTypeObject *cls, PyObject *co PyObject *argsbuf[1]; PyObject *elements; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -298,7 +301,8 @@ _elementtree_Element_find(ElementObject *self, PyTypeObject *cls, PyObject *cons PyObject *path; PyObject *namespaces = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -362,7 +366,8 @@ _elementtree_Element_findtext(ElementObject *self, PyTypeObject *cls, PyObject * PyObject *default_value = Py_None; PyObject *namespaces = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -430,7 +435,8 @@ _elementtree_Element_findall(ElementObject *self, PyTypeObject *cls, PyObject *c PyObject *path; PyObject *namespaces = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -492,7 +498,8 @@ _elementtree_Element_iterfind(ElementObject *self, PyTypeObject *cls, PyObject * PyObject *path; PyObject *namespaces = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -554,7 +561,8 @@ _elementtree_Element_get(ElementObject *self, PyObject *const *args, Py_ssize_t PyObject *key; PyObject *default_value = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -615,7 +623,8 @@ _elementtree_Element_iter(ElementObject *self, PyTypeObject *cls, PyObject *cons Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; PyObject *tag = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -763,7 +772,8 @@ _elementtree_Element_makeelement(ElementObject *self, PyTypeObject *cls, PyObjec PyObject *tag; PyObject *attrib; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -883,7 +893,8 @@ _elementtree_TreeBuilder___init__(PyObject *self, PyObject *args, PyObject *kwar int insert_comments = 0; int insert_pis = 0; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 0, 1, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -1114,7 +1125,8 @@ _elementtree_XMLParser___init__(PyObject *self, PyObject *args, PyObject *kwargs PyObject *target = Py_None; const char *encoding = NULL; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 0, 0, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 0, /*maxpos*/ 0, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -1236,4 +1248,4 @@ _elementtree_XMLParser__setevents(XMLParserObject *self, PyObject *const *args, exit: return return_value; } -/*[clinic end generated code: output=bd28eba33d9c1f25 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=b713bf59fd0fef9b input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_functoolsmodule.c.h b/Modules/clinic/_functoolsmodule.c.h index 0564921034be47..760877928db60d 100644 --- a/Modules/clinic/_functoolsmodule.c.h +++ b/Modules/clinic/_functoolsmodule.c.h @@ -56,7 +56,8 @@ _functools_cmp_to_key(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *argsbuf[1]; PyObject *mycmp; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -158,4 +159,4 @@ _functools__lru_cache_wrapper_cache_clear(PyObject *self, PyObject *Py_UNUSED(ig return return_value; } -/*[clinic end generated code: output=214d6c6307cfcd91 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=0c3df7e5131200b7 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_hashopenssl.c.h b/Modules/clinic/_hashopenssl.c.h index 1d85ab1c524082..f54f065f7d2b71 100644 --- a/Modules/clinic/_hashopenssl.c.h +++ b/Modules/clinic/_hashopenssl.c.h @@ -118,7 +118,8 @@ EVPXOF_digest(EVPobject *self, PyObject *const *args, Py_ssize_t nargs, PyObject PyObject *argsbuf[1]; Py_ssize_t length; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -188,7 +189,8 @@ EVPXOF_hexdigest(EVPobject *self, PyObject *const *args, Py_ssize_t nargs, PyObj PyObject *argsbuf[1]; Py_ssize_t length; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -265,7 +267,8 @@ EVP_new(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwn PyObject *data_obj = NULL; int usedforsecurity = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -341,7 +344,8 @@ _hashlib_openssl_md5(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *data_obj = NULL; int usedforsecurity = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -416,7 +420,8 @@ _hashlib_openssl_sha1(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *data_obj = NULL; int usedforsecurity = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -491,7 +496,8 @@ _hashlib_openssl_sha224(PyObject *module, PyObject *const *args, Py_ssize_t narg PyObject *data_obj = NULL; int usedforsecurity = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -566,7 +572,8 @@ _hashlib_openssl_sha256(PyObject *module, PyObject *const *args, Py_ssize_t narg PyObject *data_obj = NULL; int usedforsecurity = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -641,7 +648,8 @@ _hashlib_openssl_sha384(PyObject *module, PyObject *const *args, Py_ssize_t narg PyObject *data_obj = NULL; int usedforsecurity = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -716,7 +724,8 @@ _hashlib_openssl_sha512(PyObject *module, PyObject *const *args, Py_ssize_t narg PyObject *data_obj = NULL; int usedforsecurity = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -793,7 +802,8 @@ _hashlib_openssl_sha3_224(PyObject *module, PyObject *const *args, Py_ssize_t na PyObject *data_obj = NULL; int usedforsecurity = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -872,7 +882,8 @@ _hashlib_openssl_sha3_256(PyObject *module, PyObject *const *args, Py_ssize_t na PyObject *data_obj = NULL; int usedforsecurity = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -951,7 +962,8 @@ _hashlib_openssl_sha3_384(PyObject *module, PyObject *const *args, Py_ssize_t na PyObject *data_obj = NULL; int usedforsecurity = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1030,7 +1042,8 @@ _hashlib_openssl_sha3_512(PyObject *module, PyObject *const *args, Py_ssize_t na PyObject *data_obj = NULL; int usedforsecurity = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1109,7 +1122,8 @@ _hashlib_openssl_shake_128(PyObject *module, PyObject *const *args, Py_ssize_t n PyObject *data_obj = NULL; int usedforsecurity = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1188,7 +1202,8 @@ _hashlib_openssl_shake_256(PyObject *module, PyObject *const *args, Py_ssize_t n PyObject *data_obj = NULL; int usedforsecurity = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1270,7 +1285,8 @@ pbkdf2_hmac(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject long iterations; PyObject *dklen_obj = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 4, 5, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 4, /*maxpos*/ 5, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1373,7 +1389,8 @@ _hashlib_scrypt(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj long maxmem = 0; long dklen = 64; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1499,7 +1516,8 @@ _hashlib_hmac_singleshot(PyObject *module, PyObject *const *args, Py_ssize_t nar Py_buffer msg = {NULL, NULL}; PyObject *digest; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 3, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 3, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1573,7 +1591,8 @@ _hashlib_hmac_new(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyO PyObject *msg_obj = NULL; PyObject *digestmod = NULL; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1664,7 +1683,8 @@ _hashlib_HMAC_update(HMACobject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *argsbuf[1]; PyObject *msg; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1824,4 +1844,4 @@ _hashlib_compare_digest(PyObject *module, PyObject *const *args, Py_ssize_t narg #ifndef _HASHLIB_SCRYPT_METHODDEF #define _HASHLIB_SCRYPT_METHODDEF #endif /* !defined(_HASHLIB_SCRYPT_METHODDEF) */ -/*[clinic end generated code: output=fef43fd9f4dbea49 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=c3ef67e4a573cc7a input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_lsprof.c.h b/Modules/clinic/_lsprof.c.h index 234cc9ef3c0eaf..e19840f97e5c69 100644 --- a/Modules/clinic/_lsprof.c.h +++ b/Modules/clinic/_lsprof.c.h @@ -242,7 +242,8 @@ _lsprof_Profiler_enable(ProfilerObject *self, PyObject *const *args, Py_ssize_t int subcalls = 1; int builtins = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -358,7 +359,8 @@ profiler_init(PyObject *self, PyObject *args, PyObject *kwargs) int subcalls = 1; int builtins = 1; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 0, 4, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 0, /*maxpos*/ 4, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -405,4 +407,4 @@ profiler_init(PyObject *self, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=0b71f52bee9a7bb1 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=e56d849e35d005a5 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_lzmamodule.c.h b/Modules/clinic/_lzmamodule.c.h index 51fab5eab3f7dc..187f7b183dca84 100644 --- a/Modules/clinic/_lzmamodule.c.h +++ b/Modules/clinic/_lzmamodule.c.h @@ -128,7 +128,8 @@ _lzma_LZMADecompressor_decompress(Decompressor *self, PyObject *const *args, Py_ Py_buffer data = {NULL, NULL}; Py_ssize_t max_length = -1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -226,7 +227,8 @@ _lzma_LZMADecompressor(PyTypeObject *type, PyObject *args, PyObject *kwargs) PyObject *memlimit = Py_None; PyObject *filters = Py_None; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 0, 3, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 0, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -327,4 +329,4 @@ _lzma__decode_filter_properties(PyObject *module, PyObject *const *args, Py_ssiz return return_value; } -/*[clinic end generated code: output=5e79c05ace76dc96 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=52e1b68d0886cebb input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_opcode.c.h b/Modules/clinic/_opcode.c.h index 32ac9521a2b5cf..e61762e124ad72 100644 --- a/Modules/clinic/_opcode.c.h +++ b/Modules/clinic/_opcode.c.h @@ -57,7 +57,8 @@ _opcode_stack_effect(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *jump = Py_None; int _return_value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -131,7 +132,8 @@ _opcode_is_valid(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb int opcode; int _return_value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -194,7 +196,8 @@ _opcode_has_arg(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj int opcode; int _return_value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -257,7 +260,8 @@ _opcode_has_const(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyO int opcode; int _return_value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -320,7 +324,8 @@ _opcode_has_name(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb int opcode; int _return_value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -383,7 +388,8 @@ _opcode_has_jump(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb int opcode; int _return_value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -451,7 +457,8 @@ _opcode_has_free(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb int opcode; int _return_value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -514,7 +521,8 @@ _opcode_has_local(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyO int opcode; int _return_value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -577,7 +585,8 @@ _opcode_has_exc(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj int opcode; int _return_value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -732,7 +741,8 @@ _opcode_get_executor(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *code; int offset; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -746,4 +756,4 @@ _opcode_get_executor(PyObject *module, PyObject *const *args, Py_ssize_t nargs, exit: return return_value; } -/*[clinic end generated code: output=3b4d4f32eedd636e input=a9049054013a1b77]*/ +/*[clinic end generated code: output=2e0c39286d0253e7 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_pickle.c.h b/Modules/clinic/_pickle.c.h index 40f1309b6aa03c..2e84bb83e21ca6 100644 --- a/Modules/clinic/_pickle.c.h +++ b/Modules/clinic/_pickle.c.h @@ -64,7 +64,8 @@ _pickle_Pickler_dump(PicklerObject *self, PyTypeObject *cls, PyObject *const *ar PyObject *argsbuf[1]; PyObject *obj; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -181,7 +182,8 @@ _pickle_Pickler___init__(PyObject *self, PyObject *args, PyObject *kwargs) int fix_imports = 1; PyObject *buffer_callback = Py_None; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 4, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 1, /*maxpos*/ 4, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -298,7 +300,8 @@ _pickle_Unpickler_persistent_load(UnpicklerObject *self, PyTypeObject *cls, PyOb PyObject *argsbuf[1]; PyObject *pid; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -377,7 +380,8 @@ _pickle_Unpickler_find_class(UnpicklerObject *self, PyTypeObject *cls, PyObject PyObject *module_name; PyObject *global_name; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -487,7 +491,8 @@ _pickle_Unpickler___init__(PyObject *self, PyObject *args, PyObject *kwargs) const char *errors = "strict"; PyObject *buffers = NULL; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 1, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -679,7 +684,8 @@ _pickle_dump(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject int fix_imports = 1; PyObject *buffer_callback = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -782,7 +788,8 @@ _pickle_dumps(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec int fix_imports = 1; PyObject *buffer_callback = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -891,7 +898,8 @@ _pickle_load(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject const char *errors = "strict"; PyObject *buffers = NULL; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1017,7 +1025,8 @@ _pickle_loads(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec const char *errors = "strict"; PyObject *buffers = NULL; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1077,4 +1086,4 @@ _pickle_loads(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec exit: return return_value; } -/*[clinic end generated code: output=a9452cf1219f2e7a input=a9049054013a1b77]*/ +/*[clinic end generated code: output=48ceb6687a8e716c input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_queuemodule.c.h b/Modules/clinic/_queuemodule.c.h index 6f4c715c722965..f0d4a3a164cd5f 100644 --- a/Modules/clinic/_queuemodule.c.h +++ b/Modules/clinic/_queuemodule.c.h @@ -89,7 +89,8 @@ _queue_SimpleQueue_put(simplequeueobject *self, PyObject *const *args, Py_ssize_ int block = 1; PyObject *timeout = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -163,7 +164,8 @@ _queue_SimpleQueue_put_nowait(simplequeueobject *self, PyObject *const *args, Py PyObject *argsbuf[1]; PyObject *item; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -231,7 +233,8 @@ _queue_SimpleQueue_get(simplequeueobject *self, PyTypeObject *cls, PyObject *con int block = 1; PyObject *timeout_obj = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -349,4 +352,4 @@ _queue_SimpleQueue_qsize(simplequeueobject *self, PyObject *Py_UNUSED(ignored)) exit: return return_value; } -/*[clinic end generated code: output=44a718f40072018a input=a9049054013a1b77]*/ +/*[clinic end generated code: output=07b5742dca7692d9 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_ssl.c.h b/Modules/clinic/_ssl.c.h index 9d5b70dfad553d..582eef16c13244 100644 --- a/Modules/clinic/_ssl.c.h +++ b/Modules/clinic/_ssl.c.h @@ -610,7 +610,8 @@ _ssl__SSLSocket_get_channel_binding(PySSLSocket *self, PyObject *const *args, Py Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; const char *cb_type = "tls-unique"; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1407,7 +1408,8 @@ _ssl__SSLContext_load_cert_chain(PySSLContext *self, PyObject *const *args, Py_s PyObject *keyfile = Py_None; PyObject *password = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1480,7 +1482,8 @@ _ssl__SSLContext_load_verify_locations(PySSLContext *self, PyObject *const *args PyObject *capath = Py_None; PyObject *cadata = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1583,7 +1586,8 @@ _ssl__SSLContext__wrap_socket(PySSLContext *self, PyObject *const *args, Py_ssiz PyObject *owner = Py_None; PyObject *session = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1678,7 +1682,8 @@ _ssl__SSLContext__wrap_bio(PySSLContext *self, PyObject *const *args, Py_ssize_t PyObject *owner = Py_None; PyObject *session = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 3, 4, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 3, /*maxpos*/ 4, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1929,7 +1934,8 @@ _ssl__SSLContext_get_ca_certs(PySSLContext *self, PyObject *const *args, Py_ssiz Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; int binary_form = 0; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1993,7 +1999,8 @@ _ssl__SSLContext_set_psk_client_callback(PySSLContext *self, PyObject *const *ar PyObject *argsbuf[1]; PyObject *callback; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2053,7 +2060,8 @@ _ssl__SSLContext_set_psk_server_callback(PySSLContext *self, PyObject *const *ar PyObject *callback; const char *identity_hint = NULL; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2601,7 +2609,8 @@ _ssl_txt2obj(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject const char *txt; int name = 0; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2716,7 +2725,8 @@ _ssl_enum_certificates(PyObject *module, PyObject *const *args, Py_ssize_t nargs PyObject *argsbuf[1]; const char *store_name; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2794,7 +2804,8 @@ _ssl_enum_crls(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObje PyObject *argsbuf[1]; const char *store_name; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2828,4 +2839,4 @@ _ssl_enum_crls(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObje #ifndef _SSL_ENUM_CRLS_METHODDEF #define _SSL_ENUM_CRLS_METHODDEF #endif /* !defined(_SSL_ENUM_CRLS_METHODDEF) */ -/*[clinic end generated code: output=c1489122072a9f5e input=a9049054013a1b77]*/ +/*[clinic end generated code: output=4c2af0c8fab7ec4e input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_struct.c.h b/Modules/clinic/_struct.c.h index 1a07532bdd75ad..cfc2fe7fc1dd58 100644 --- a/Modules/clinic/_struct.c.h +++ b/Modules/clinic/_struct.c.h @@ -57,7 +57,8 @@ Struct___init__(PyObject *self, PyObject *args, PyObject *kwargs) Py_ssize_t nargs = PyTuple_GET_SIZE(args); PyObject *format; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 1, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -159,7 +160,8 @@ Struct_unpack_from(PyStructObject *self, PyObject *const *args, Py_ssize_t nargs Py_buffer buffer = {NULL, NULL}; Py_ssize_t offset = 0; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -357,7 +359,8 @@ unpack_from(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject Py_buffer buffer = {NULL, NULL}; Py_ssize_t offset = 0; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -436,4 +439,4 @@ iter_unpack(PyObject *module, PyObject *const *args, Py_ssize_t nargs) return return_value; } -/*[clinic end generated code: output=67bd299e5d72fee0 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=faff90f99c6bd09f input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_testclinic.c.h b/Modules/clinic/_testclinic.c.h index 0f5ae5ec869753..434488adbbb77c 100644 --- a/Modules/clinic/_testclinic.c.h +++ b/Modules/clinic/_testclinic.c.h @@ -1479,7 +1479,8 @@ keywords(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kw PyObject *a; PyObject *b; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1535,7 +1536,8 @@ keywords_kwonly(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj PyObject *a; PyObject *b; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 1, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 1, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1593,7 +1595,8 @@ keywords_opt(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject PyObject *b = Py_None; PyObject *c = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1663,7 +1666,8 @@ keywords_opt_kwonly(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P PyObject *c = Py_None; PyObject *d = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1742,7 +1746,8 @@ keywords_kwonly_opt(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P PyObject *b = Py_None; PyObject *c = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1808,7 +1813,8 @@ posonly_keywords(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb PyObject *a; PyObject *b; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1864,7 +1870,8 @@ posonly_kwonly(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObje PyObject *a; PyObject *b; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 1, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 1, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1922,7 +1929,8 @@ posonly_keywords_kwonly(PyObject *module, PyObject *const *args, Py_ssize_t narg PyObject *b; PyObject *c; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 1, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 1, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1983,7 +1991,8 @@ posonly_keywords_opt(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *c = Py_None; PyObject *d = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 4, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 4, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2054,7 +2063,8 @@ posonly_opt_keywords_opt(PyObject *module, PyObject *const *args, Py_ssize_t nar PyObject *c = Py_None; PyObject *d = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 4, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 4, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2130,7 +2140,8 @@ posonly_kwonly_opt(PyObject *module, PyObject *const *args, Py_ssize_t nargs, Py PyObject *c = Py_None; PyObject *d = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 1, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 1, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2201,7 +2212,8 @@ posonly_opt_kwonly_opt(PyObject *module, PyObject *const *args, Py_ssize_t nargs PyObject *c = Py_None; PyObject *d = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2278,7 +2290,8 @@ posonly_keywords_kwonly_opt(PyObject *module, PyObject *const *args, Py_ssize_t PyObject *d = Py_None; PyObject *e = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 1, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 1, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2353,7 +2366,8 @@ posonly_keywords_opt_kwonly_opt(PyObject *module, PyObject *const *args, Py_ssiz PyObject *d = Py_None; PyObject *e = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2436,7 +2450,8 @@ posonly_opt_keywords_opt_kwonly_opt(PyObject *module, PyObject *const *args, Py_ PyObject *c = Py_None; PyObject *d = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2511,7 +2526,8 @@ keyword_only_parameter(PyObject *module, PyObject *const *args, Py_ssize_t nargs PyObject *argsbuf[1]; PyObject *a; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 0, 1, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 0, /*minkw*/ 1, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2681,7 +2697,8 @@ posonly_poskw_varpos(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *b; PyObject *__clinic_args = NULL; - fastargs = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 1, argsbuf); if (!fastargs) { goto exit; } @@ -2747,7 +2764,8 @@ poskw_varpos(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject PyObject *a; PyObject *__clinic_args = NULL; - fastargs = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 1, argsbuf); if (!fastargs) { goto exit; } @@ -2815,7 +2833,8 @@ poskw_varpos_kwonly_opt(PyObject *module, PyObject *const *args, Py_ssize_t narg PyObject *__clinic_args = NULL; int b = 0; - fastargs = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 1, argsbuf); if (!fastargs) { goto exit; } @@ -2892,7 +2911,8 @@ poskw_varpos_kwonly_opt2(PyObject *module, PyObject *const *args, Py_ssize_t nar PyObject *b = Py_False; PyObject *c = Py_False; - fastargs = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 1, argsbuf); if (!fastargs) { goto exit; } @@ -2969,7 +2989,8 @@ varpos_kwonly_opt(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyO PyObject *__clinic_args = NULL; PyObject *b = Py_False; - fastargs = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 0, /*minkw*/ 0, /*varpos*/ 1, argsbuf); if (!fastargs) { goto exit; } @@ -3040,7 +3061,8 @@ varpos_kwonly_req_opt(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *b = Py_False; PyObject *c = Py_False; - fastargs = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 0, 0, 1, argsbuf); + fastargs = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 0, /*minkw*/ 1, /*varpos*/ 1, argsbuf); if (!fastargs) { goto exit; } @@ -3217,7 +3239,8 @@ posonly_poskw_varpos_array(PyObject *module, PyObject *const *args, Py_ssize_t n PyObject * const *__clinic_args; Py_ssize_t args_length; - fastargs = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 1, argsbuf); if (!fastargs) { goto exit; } @@ -3282,7 +3305,8 @@ gh_32092_oob(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject PyObject *kw1 = Py_None; PyObject *kw2 = Py_None; - fastargs = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 1, argsbuf); if (!fastargs) { goto exit; } @@ -3363,7 +3387,8 @@ gh_32092_kw_pass(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb PyObject *__clinic_args = NULL; PyObject *kw = Py_None; - fastargs = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 1, argsbuf); if (!fastargs) { goto exit; } @@ -3502,7 +3527,8 @@ null_or_tuple_for_varargs(PyObject *module, PyObject *const *args, Py_ssize_t na PyObject *constraints = NULL; int covariant = 0; - fastargs = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 1, argsbuf); if (!fastargs) { goto exit; } @@ -3573,7 +3599,8 @@ clone_f1(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kw PyObject *argsbuf[1]; const char *path; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -3639,7 +3666,8 @@ clone_f2(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kw PyObject *argsbuf[1]; const char *path; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -3708,7 +3736,8 @@ clone_with_conv_f1(PyObject *module, PyObject *const *args, Py_ssize_t nargs, Py .name = "clone_with_conv_f1", }; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -3771,7 +3800,8 @@ clone_with_conv_f2(PyObject *module, PyObject *const *args, Py_ssize_t nargs, Py .name = "clone_with_conv_f2", }; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -3855,7 +3885,8 @@ _testclinic_TestClass_get_defining_class_arg(PyObject *self, PyTypeObject *cls, PyObject *argsbuf[1]; PyObject *arg; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -3899,7 +3930,8 @@ _testclinic_TestClass_defclass_varpos(PyObject *self, PyTypeObject *cls, PyObjec PyObject * const *fastargs; PyObject *__clinic_args = NULL; - fastargs = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 0, /*minkw*/ 0, /*varpos*/ 1, argsbuf); if (!fastargs) { goto exit; } @@ -3953,7 +3985,8 @@ _testclinic_TestClass_defclass_posonly_varpos(PyObject *self, PyTypeObject *cls, PyObject *b; PyObject *__clinic_args = NULL; - fastargs = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 1, argsbuf); if (!fastargs) { goto exit; } @@ -4110,7 +4143,8 @@ posonly_poskw_varpos_no_fastcall(PyTypeObject *type, PyObject *args, PyObject *k PyObject *b; PyObject *__clinic_args = NULL; - fastargs = _PyArg_UnpackKeywordsWithVararg(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 2, 2, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 1, argsbuf); if (!fastargs) { goto exit; } @@ -4265,7 +4299,8 @@ posonly_poskw_varpos_array_no_fastcall(PyTypeObject *type, PyObject *args, PyObj PyObject * const *__clinic_args; Py_ssize_t args_length; - fastargs = _PyArg_UnpackKeywordsWithVararg(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 2, 2, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 1, argsbuf); if (!fastargs) { goto exit; } @@ -4278,4 +4313,4 @@ posonly_poskw_varpos_array_no_fastcall(PyTypeObject *type, PyObject *args, PyObj exit: return return_value; } -/*[clinic end generated code: output=ed3408af146a746c input=a9049054013a1b77]*/ +/*[clinic end generated code: output=00085e5637450fe6 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_testclinic_depr.c.h b/Modules/clinic/_testclinic_depr.c.h index 0e374f100d6239..9378038e6b9549 100644 --- a/Modules/clinic/_testclinic_depr.c.h +++ b/Modules/clinic/_testclinic_depr.c.h @@ -80,7 +80,8 @@ depr_star_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) goto exit; } } - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 0, 1, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -163,7 +164,8 @@ depr_star_new_clone(PyObject *type, PyObject *const *args, Py_ssize_t nargs, PyO goto exit; } } - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -247,7 +249,8 @@ depr_star_init(PyObject *self, PyObject *args, PyObject *kwargs) goto exit; } } - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 0, 1, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -330,7 +333,8 @@ depr_star_init_clone(PyObject *self, PyObject *const *args, Py_ssize_t nargs, Py goto exit; } } - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -474,7 +478,8 @@ depr_kwd_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 0; PyObject *a = Py_None; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 0, 1, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -557,7 +562,8 @@ depr_kwd_init(PyObject *self, PyObject *args, PyObject *kwargs) Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 0; PyObject *a = Py_None; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 0, 1, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -721,7 +727,8 @@ depr_star_pos0_len1(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P goto exit; } } - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -800,7 +807,8 @@ depr_star_pos0_len2(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P goto exit; } } - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -883,7 +891,8 @@ depr_star_pos0_len3_with_kwd(PyObject *module, PyObject *const *args, Py_ssize_t goto exit; } } - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 3, 3, 1, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 3, /*maxpos*/ 3, /*minkw*/ 1, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -966,7 +975,8 @@ depr_star_pos1_len1_opt(PyObject *module, PyObject *const *args, Py_ssize_t narg goto exit; } } - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1050,7 +1060,8 @@ depr_star_pos1_len1(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P goto exit; } } - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1133,7 +1144,8 @@ depr_star_pos1_len2_with_kwd(PyObject *module, PyObject *const *args, Py_ssize_t goto exit; } } - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 3, 3, 1, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 3, /*maxpos*/ 3, /*minkw*/ 1, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1217,7 +1229,8 @@ depr_star_pos2_len1(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P goto exit; } } - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 3, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 3, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1301,7 +1314,8 @@ depr_star_pos2_len2(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P goto exit; } } - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 4, 4, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 4, /*maxpos*/ 4, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1387,7 +1401,8 @@ depr_star_pos2_len2_with_kwd(PyObject *module, PyObject *const *args, Py_ssize_t goto exit; } } - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 4, 4, 1, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 4, /*maxpos*/ 4, /*minkw*/ 1, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1563,7 +1578,8 @@ depr_star_multi(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj goto exit; } } - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 7, 7, 1, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 7, /*maxpos*/ 7, /*minkw*/ 1, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1639,7 +1655,8 @@ depr_kwd_required_1(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P PyObject *a; PyObject *b; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1721,7 +1738,8 @@ depr_kwd_required_2(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P PyObject *b; PyObject *c; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 3, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 3, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1802,7 +1820,8 @@ depr_kwd_optional_1(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P PyObject *a; PyObject *b = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1889,7 +1908,8 @@ depr_kwd_optional_2(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P PyObject *b = Py_None; PyObject *c = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1982,7 +2002,8 @@ depr_kwd_optional_3(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P PyObject *b = Py_None; PyObject *c = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2080,7 +2101,8 @@ depr_kwd_required_optional(PyObject *module, PyObject *const *args, Py_ssize_t n PyObject *b; PyObject *c = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2259,7 +2281,8 @@ depr_kwd_multi(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObje PyObject *g; PyObject *h; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 8, 8, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 8, /*maxpos*/ 8, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2369,7 +2392,8 @@ depr_multi(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject * goto exit; } } - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 6, 6, 1, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 6, /*maxpos*/ 6, /*minkw*/ 1, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2394,4 +2418,4 @@ depr_multi(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject * exit: return return_value; } -/*[clinic end generated code: output=5dda27c80df7351e input=a9049054013a1b77]*/ +/*[clinic end generated code: output=836affc1a13d67a1 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_testinternalcapi.c.h b/Modules/clinic/_testinternalcapi.c.h index dcca9ecf735723..d98d69df22f982 100644 --- a/Modules/clinic/_testinternalcapi.c.h +++ b/Modules/clinic/_testinternalcapi.c.h @@ -52,7 +52,8 @@ _testinternalcapi_compiler_cleandoc(PyObject *module, PyObject *const *args, Py_ PyObject *argsbuf[1]; PyObject *doc; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -135,7 +136,8 @@ _testinternalcapi_compiler_codegen(PyObject *module, PyObject *const *args, Py_s int optimize; int compile_mode = 0; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 3, 4, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 3, /*maxpos*/ 4, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -206,7 +208,8 @@ _testinternalcapi_optimize_cfg(PyObject *module, PyObject *const *args, Py_ssize PyObject *consts; int nlocals; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 3, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 3, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -271,7 +274,8 @@ _testinternalcapi_assemble_code_object(PyObject *module, PyObject *const *args, PyObject *instructions; PyObject *metadata; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 3, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 3, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -346,7 +350,8 @@ gh_119213_getargs(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyO Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; PyObject *spam = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -360,4 +365,4 @@ gh_119213_getargs(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyO exit: return return_value; } -/*[clinic end generated code: output=4d0770a1c20fbf40 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=ec77971c6c2663da input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_testmultiphase.c.h b/Modules/clinic/_testmultiphase.c.h index 452897b3fae99e..5a432a6f70386d 100644 --- a/Modules/clinic/_testmultiphase.c.h +++ b/Modules/clinic/_testmultiphase.c.h @@ -109,7 +109,8 @@ _testmultiphase_StateAccessType_increment_count_clinic(StateAccessTypeObject *se int n = 1; int twice = 0; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -162,4 +163,4 @@ _testmultiphase_StateAccessType_get_count(StateAccessTypeObject *self, PyTypeObj } return _testmultiphase_StateAccessType_get_count_impl(self, cls); } -/*[clinic end generated code: output=59cb50dae2d11dc1 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=c1aa0af3572bf059 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_winapi.c.h b/Modules/clinic/_winapi.c.h index b0c54fc809f4c1..8bbecc44dc9c11 100644 --- a/Modules/clinic/_winapi.c.h +++ b/Modules/clinic/_winapi.c.h @@ -793,7 +793,8 @@ _winapi_GetLongPathName(PyObject *module, PyObject *const *args, Py_ssize_t narg PyObject *argsbuf[1]; LPCWSTR path = NULL; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -897,7 +898,8 @@ _winapi_GetShortPathName(PyObject *module, PyObject *const *args, Py_ssize_t nar PyObject *argsbuf[1]; LPCWSTR path = NULL; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2004,7 +2006,8 @@ _winapi__mimetypes_read_windows_registry(PyObject *module, PyObject *const *args PyObject *argsbuf[1]; PyObject *on_type_read; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2124,4 +2127,4 @@ _winapi_CopyFile2(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyO return return_value; } -/*[clinic end generated code: output=2304c62187a90140 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=b2a178bde6868e88 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_zoneinfo.c.h b/Modules/clinic/_zoneinfo.c.h index bde88b5c4fa65b..dd72b5e663aea6 100644 --- a/Modules/clinic/_zoneinfo.c.h +++ b/Modules/clinic/_zoneinfo.c.h @@ -52,7 +52,8 @@ zoneinfo_ZoneInfo(PyTypeObject *type, PyObject *args, PyObject *kwargs) Py_ssize_t nargs = PyTuple_GET_SIZE(args); PyObject *key; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 1, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -112,7 +113,8 @@ zoneinfo_ZoneInfo_from_file(PyTypeObject *type, PyTypeObject *cls, PyObject *con PyObject *file_obj; PyObject *key = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -173,7 +175,8 @@ zoneinfo_ZoneInfo_no_cache(PyTypeObject *type, PyTypeObject *cls, PyObject *cons PyObject *argsbuf[1]; PyObject *key; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -230,7 +233,8 @@ zoneinfo_ZoneInfo_clear_cache(PyTypeObject *type, PyTypeObject *cls, PyObject *c Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; PyObject *only_keys = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 0, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -280,7 +284,8 @@ zoneinfo_ZoneInfo_utcoffset(PyObject *self, PyTypeObject *cls, PyObject *const * PyObject *argsbuf[1]; PyObject *dt; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -323,7 +328,8 @@ zoneinfo_ZoneInfo_dst(PyObject *self, PyTypeObject *cls, PyObject *const *args, PyObject *argsbuf[1]; PyObject *dt; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -367,7 +373,8 @@ zoneinfo_ZoneInfo_tzname(PyObject *self, PyTypeObject *cls, PyObject *const *arg PyObject *argsbuf[1]; PyObject *dt; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -412,7 +419,8 @@ zoneinfo_ZoneInfo__unpickle(PyTypeObject *type, PyTypeObject *cls, PyObject *con PyObject *key; unsigned char from_cache; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -431,4 +439,4 @@ zoneinfo_ZoneInfo__unpickle(PyTypeObject *type, PyTypeObject *cls, PyObject *con exit: return return_value; } -/*[clinic end generated code: output=b4fdc0b30247110a input=a9049054013a1b77]*/ +/*[clinic end generated code: output=f8a4fb4ff634d6c9 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/arraymodule.c.h b/Modules/clinic/arraymodule.c.h index 2ed7eaa6abf7af..4a7266ecb8b84f 100644 --- a/Modules/clinic/arraymodule.c.h +++ b/Modules/clinic/arraymodule.c.h @@ -194,7 +194,8 @@ array_array_extend(arrayobject *self, PyTypeObject *cls, PyObject *const *args, PyObject *argsbuf[1]; PyObject *bb; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -349,7 +350,8 @@ array_array_fromfile(arrayobject *self, PyTypeObject *cls, PyObject *const *args PyObject *f; Py_ssize_t n; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -404,7 +406,8 @@ array_array_tofile(arrayobject *self, PyTypeObject *cls, PyObject *const *args, PyObject *argsbuf[1]; PyObject *f; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -650,7 +653,8 @@ array_array___reduce_ex__(arrayobject *self, PyTypeObject *cls, PyObject *const PyObject *argsbuf[1]; PyObject *value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -691,4 +695,4 @@ PyDoc_STRVAR(array_arrayiterator___setstate____doc__, #define ARRAY_ARRAYITERATOR___SETSTATE___METHODDEF \ {"__setstate__", (PyCFunction)array_arrayiterator___setstate__, METH_O, array_arrayiterator___setstate____doc__}, -/*[clinic end generated code: output=ecd63acd7924c223 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=22dbe12826bfa86f input=a9049054013a1b77]*/ diff --git a/Modules/clinic/binascii.c.h b/Modules/clinic/binascii.c.h index 1adca415dfee12..f81f12c388f373 100644 --- a/Modules/clinic/binascii.c.h +++ b/Modules/clinic/binascii.c.h @@ -85,7 +85,8 @@ binascii_b2a_uu(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj Py_buffer data = {NULL, NULL}; int backtick = 0; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -161,7 +162,8 @@ binascii_a2b_base64(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P Py_buffer data = {NULL, NULL}; int strict_mode = 0; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -232,7 +234,8 @@ binascii_b2a_base64(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P Py_buffer data = {NULL, NULL}; int newline = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -412,7 +415,8 @@ binascii_b2a_hex(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb PyObject *sep = NULL; int bytes_per_sep = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -501,7 +505,8 @@ binascii_hexlify(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb PyObject *sep = NULL; int bytes_per_sep = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -646,7 +651,8 @@ binascii_a2b_qp(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj Py_buffer data = {NULL, NULL}; int header = 0; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -724,7 +730,8 @@ binascii_b2a_qp(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj int istext = 1; int header = 0; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 4, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 4, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -767,4 +774,4 @@ binascii_b2a_qp(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj return return_value; } -/*[clinic end generated code: output=968767b663ed889d input=a9049054013a1b77]*/ +/*[clinic end generated code: output=9ed7fbeec13c6606 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/blake2module.c.h b/Modules/clinic/blake2module.c.h index 50478bcbecf8e3..f695f27e9e6c42 100644 --- a/Modules/clinic/blake2module.c.h +++ b/Modules/clinic/blake2module.c.h @@ -72,7 +72,8 @@ py_blake2b_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) int last_node = 0; int usedforsecurity = 1; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 0, 1, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -266,7 +267,8 @@ py_blake2s_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) int last_node = 0; int usedforsecurity = 1; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 0, 1, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -459,4 +461,4 @@ _blake2_blake2b_hexdigest(Blake2Object *self, PyObject *Py_UNUSED(ignored)) { return _blake2_blake2b_hexdigest_impl(self); } -/*[clinic end generated code: output=d1a351f44e20e273 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=e0aaaf112d023b79 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/cmathmodule.c.h b/Modules/clinic/cmathmodule.c.h index 50745fd4f407a3..16eaba7aa7ee39 100644 --- a/Modules/clinic/cmathmodule.c.h +++ b/Modules/clinic/cmathmodule.c.h @@ -932,7 +932,8 @@ cmath_isclose(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec double abs_tol = 0.0; int _return_value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -982,4 +983,4 @@ cmath_isclose(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec exit: return return_value; } -/*[clinic end generated code: output=454309b21cfa9bf6 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=5fda69f15dc9dfc9 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/gcmodule.c.h b/Modules/clinic/gcmodule.c.h index 34b2a79275c279..0147020bd483f9 100644 --- a/Modules/clinic/gcmodule.c.h +++ b/Modules/clinic/gcmodule.c.h @@ -126,7 +126,8 @@ gc_collect(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject * int generation = NUM_GENERATIONS - 1; Py_ssize_t _return_value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -416,7 +417,8 @@ gc_get_objects(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObje Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; Py_ssize_t generation = -1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -578,4 +580,4 @@ gc_get_freeze_count(PyObject *module, PyObject *Py_UNUSED(ignored)) exit: return return_value; } -/*[clinic end generated code: output=4f35875870da17c9 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=3e33248997e06c34 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/itertoolsmodule.c.h b/Modules/clinic/itertoolsmodule.c.h index 050c21460d79d7..ea0cc495ffb969 100644 --- a/Modules/clinic/itertoolsmodule.c.h +++ b/Modules/clinic/itertoolsmodule.c.h @@ -71,7 +71,8 @@ batched_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) Py_ssize_t n; int strict = 0; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 2, 2, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -186,7 +187,8 @@ itertools_groupby(PyTypeObject *type, PyObject *args, PyObject *kwargs) PyObject *it; PyObject *keyfunc = Py_None; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 2, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -531,7 +533,8 @@ itertools_combinations(PyTypeObject *type, PyObject *args, PyObject *kwargs) PyObject *iterable; Py_ssize_t r; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 2, 2, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -602,7 +605,8 @@ itertools_combinations_with_replacement(PyTypeObject *type, PyObject *args, PyOb PyObject *iterable; Py_ssize_t r; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 2, 2, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -673,7 +677,8 @@ itertools_permutations(PyTypeObject *type, PyObject *args, PyObject *kwargs) PyObject *iterable; PyObject *robj = Py_None; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 2, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -736,7 +741,8 @@ itertools_accumulate(PyTypeObject *type, PyObject *args, PyObject *kwargs) PyObject *binop = Py_None; PyObject *initial = Py_None; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 2, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -809,7 +815,8 @@ itertools_compress(PyTypeObject *type, PyObject *args, PyObject *kwargs) PyObject *seq1; PyObject *seq2; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 2, 2, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -908,7 +915,8 @@ itertools_count(PyTypeObject *type, PyObject *args, PyObject *kwargs) PyObject *long_cnt = NULL; PyObject *long_step = NULL; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 0, 2, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -928,4 +936,4 @@ itertools_count(PyTypeObject *type, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=7b13be3075f2d6d3 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=199eeac2d17e8f23 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/mathmodule.c.h b/Modules/clinic/mathmodule.c.h index 461f77183cd45b..a76dde1eb4350e 100644 --- a/Modules/clinic/mathmodule.c.h +++ b/Modules/clinic/mathmodule.c.h @@ -764,7 +764,8 @@ math_isclose(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject double abs_tol = 0.0; int _return_value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -879,7 +880,8 @@ math_prod(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *k PyObject *iterable; PyObject *start = NULL; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1030,7 +1032,8 @@ math_nextafter(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObje double y; PyObject *steps = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1103,4 +1106,4 @@ math_ulp(PyObject *module, PyObject *arg) exit: return return_value; } -/*[clinic end generated code: output=cb506f61bc5ef862 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=1ccb4b9f570d6dad input=a9049054013a1b77]*/ diff --git a/Modules/clinic/md5module.c.h b/Modules/clinic/md5module.c.h index ee7fb3d7c613f2..7721616862ed0d 100644 --- a/Modules/clinic/md5module.c.h +++ b/Modules/clinic/md5module.c.h @@ -121,7 +121,8 @@ _md5_md5(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kw PyObject *string = NULL; int usedforsecurity = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -148,4 +149,4 @@ _md5_md5(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kw exit: return return_value; } -/*[clinic end generated code: output=4dbca39332d3f52f input=a9049054013a1b77]*/ +/*[clinic end generated code: output=62ebf28802ae8b5f input=a9049054013a1b77]*/ diff --git a/Modules/clinic/overlapped.c.h b/Modules/clinic/overlapped.c.h index 8b285e4a8f0a72..9d5adb5193f297 100644 --- a/Modules/clinic/overlapped.c.h +++ b/Modules/clinic/overlapped.c.h @@ -484,7 +484,8 @@ _overlapped_Overlapped(PyTypeObject *type, PyObject *args, PyObject *kwargs) Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 0; HANDLE event = INVALID_HANDLE_VALUE; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 0, 1, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -1239,4 +1240,4 @@ _overlapped_Overlapped_WSARecvFromInto(OverlappedObject *self, PyObject *const * return return_value; } -/*[clinic end generated code: output=958cbddbcc355f47 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=14c4f87906f28dc5 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index 1857fca736ef20..dce0ea100ec435 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -76,7 +76,8 @@ os_stat(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwn int dir_fd = DEFAULT_DIR_FD; int follow_symlinks = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -157,7 +158,8 @@ os_lstat(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kw path_t path = PATH_T_INITIALIZE_P("lstat", "path", 0, 0, 0, 0); int dir_fd = DEFAULT_DIR_FD; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -257,7 +259,8 @@ os_access(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *k int follow_symlinks = 1; int _return_value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -411,7 +414,8 @@ os_chdir(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kw PyObject *argsbuf[1]; path_t path = PATH_T_INITIALIZE_P("chdir", "path", 0, 0, 0, PATH_HAVE_FCHDIR); - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -476,7 +480,8 @@ os_fchdir(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *k PyObject *argsbuf[1]; int fd; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -565,7 +570,8 @@ os_chmod(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kw int dir_fd = DEFAULT_DIR_FD; int follow_symlinks = CHMOD_DEFAULT_FOLLOW_SYMLINKS; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -658,7 +664,8 @@ os_fchmod(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *k int fd; int mode; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -728,7 +735,8 @@ os_lchmod(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *k path_t path = PATH_T_INITIALIZE_P("lchmod", "path", 0, 0, 0, 0); int mode; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -806,7 +814,8 @@ os_chflags(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject * unsigned long flags; int follow_symlinks = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -887,7 +896,8 @@ os_lchflags(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject path_t path = PATH_T_INITIALIZE_P("lchflags", "path", 0, 0, 0, 0); unsigned long flags; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -956,7 +966,8 @@ os_chroot(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *k PyObject *argsbuf[1]; path_t path = PATH_T_INITIALIZE_P("chroot", "path", 0, 0, 0, 0); - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1020,7 +1031,8 @@ os_fsync(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kw PyObject *argsbuf[1]; int fd; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1104,7 +1116,8 @@ os_fdatasync(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject PyObject *argsbuf[1]; int fd; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1196,7 +1209,8 @@ os_chown(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kw int dir_fd = DEFAULT_DIR_FD; int follow_symlinks = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 3, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 3, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1286,7 +1300,8 @@ os_fchown(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *k uid_t uid; gid_t gid; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 3, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 3, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1359,7 +1374,8 @@ os_lchown(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *k uid_t uid; gid_t gid; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 3, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 3, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1482,7 +1498,8 @@ os_link(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwn int dst_dir_fd = DEFAULT_DIR_FD; int follow_symlinks = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1585,7 +1602,8 @@ os_listdir(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject * Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; path_t path = PATH_T_INITIALIZE_P("listdir", "path", 1, 0, 0, PATH_HAVE_FDOPENDIR); - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1701,7 +1719,8 @@ os_listmounts(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec PyObject *argsbuf[1]; path_t volume = PATH_T_INITIALIZE_P("listmounts", "volume", 0, 0, 0, 0); - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1765,7 +1784,8 @@ os__path_isdevdrive(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P PyObject *argsbuf[1]; path_t path = PATH_T_INITIALIZE_P("_path_isdevdrive", "path", 0, 0, 0, 0); - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1930,7 +1950,8 @@ os__getvolumepathname(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *argsbuf[1]; path_t path = PATH_T_INITIALIZE_P("_getvolumepathname", "path", 0, 0, 0, 0); - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1994,7 +2015,8 @@ os__path_splitroot(PyObject *module, PyObject *const *args, Py_ssize_t nargs, Py PyObject *argsbuf[1]; path_t path = PATH_T_INITIALIZE_P("_path_splitroot", "path", 0, 0, 0, 0); - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2059,7 +2081,8 @@ os__path_exists(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj path_t path = PATH_T_INITIALIZE_P("_path_exists", "path", 0, 0, 1, 1); int _return_value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2128,7 +2151,8 @@ os__path_lexists(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb path_t path = PATH_T_INITIALIZE_P("_path_lexists", "path", 0, 0, 1, 1); int _return_value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2197,7 +2221,8 @@ os__path_isdir(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObje path_t path = PATH_T_INITIALIZE_P("_path_isdir", "path", 0, 0, 1, 1); int _return_value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2266,7 +2291,8 @@ os__path_isfile(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj path_t path = PATH_T_INITIALIZE_P("_path_isfile", "path", 0, 0, 1, 1); int _return_value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2335,7 +2361,8 @@ os__path_islink(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj path_t path = PATH_T_INITIALIZE_P("_path_islink", "path", 0, 0, 1, 1); int _return_value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2404,7 +2431,8 @@ os__path_isjunction(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P path_t path = PATH_T_INITIALIZE_P("_path_isjunction", "path", 0, 0, 1, 1); int _return_value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2472,7 +2500,8 @@ os__path_splitroot_ex(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *argsbuf[1]; path_t path = PATH_T_INITIALIZE("_path_splitroot_ex", "path", 0, 1, 1, 0, 0); - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2532,7 +2561,8 @@ os__path_normpath(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyO PyObject *argsbuf[1]; path_t path = PATH_T_INITIALIZE("_path_normpath", "path", 0, 1, 1, 0, 0); - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2603,7 +2633,8 @@ os_mkdir(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kw int mode = 511; int dir_fd = DEFAULT_DIR_FD; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2718,7 +2749,8 @@ os_getpriority(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObje int which; int who; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2786,7 +2818,8 @@ os_setpriority(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObje int who; int priority; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 3, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 3, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2865,7 +2898,8 @@ os_rename(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *k int src_dir_fd = DEFAULT_DIR_FD; int dst_dir_fd = DEFAULT_DIR_FD; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -2956,7 +2990,8 @@ os_replace(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject * int src_dir_fd = DEFAULT_DIR_FD; int dst_dir_fd = DEFAULT_DIR_FD; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -3043,7 +3078,8 @@ os_rmdir(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kw path_t path = PATH_T_INITIALIZE_P("rmdir", "path", 0, 0, 0, 0); int dir_fd = DEFAULT_DIR_FD; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -3113,7 +3149,8 @@ os_system(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *k const wchar_t *command = NULL; long _return_value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -3187,7 +3224,8 @@ os_system(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *k PyObject *command = NULL; long _return_value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -3292,7 +3330,8 @@ os_unlink(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *k path_t path = PATH_T_INITIALIZE_P("unlink", "path", 0, 0, 0, 0); int dir_fd = DEFAULT_DIR_FD; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -3366,7 +3405,8 @@ os_remove(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *k path_t path = PATH_T_INITIALIZE_P("remove", "path", 0, 0, 0, 0); int dir_fd = DEFAULT_DIR_FD; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -3487,7 +3527,8 @@ os_utime(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kw int dir_fd = DEFAULT_DIR_FD; int follow_symlinks = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -3579,7 +3620,8 @@ os__exit(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kw PyObject *argsbuf[1]; int status; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -3692,7 +3734,8 @@ os_execve(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *k PyObject *argv; PyObject *env; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 3, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 3, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -3795,7 +3838,8 @@ os_posix_spawn(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObje PyObject *setsigdef = NULL; PyObject *scheduler = NULL; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 3, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 3, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -3945,7 +3989,8 @@ os_posix_spawnp(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj PyObject *setsigdef = NULL; PyObject *scheduler = NULL; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 3, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 3, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -4181,7 +4226,8 @@ os_register_at_fork(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P PyObject *after_in_child = NULL; PyObject *after_in_parent = NULL; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 0, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -4304,7 +4350,8 @@ os_sched_get_priority_max(PyObject *module, PyObject *const *args, Py_ssize_t na PyObject *argsbuf[1]; int policy; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -4366,7 +4413,8 @@ os_sched_get_priority_min(PyObject *module, PyObject *const *args, Py_ssize_t na PyObject *argsbuf[1]; int policy; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -4464,7 +4512,8 @@ os_sched_param(PyTypeObject *type, PyObject *args, PyObject *kwargs) Py_ssize_t nargs = PyTuple_GET_SIZE(args); PyObject *sched_priority; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 1, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -5327,7 +5376,8 @@ os_getpgid(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject * PyObject *argsbuf[1]; pid_t pid; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -5838,7 +5888,8 @@ os_wait3(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kw PyObject *argsbuf[1]; int options; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -5904,7 +5955,8 @@ os_wait4(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kw pid_t pid; int options; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -6145,7 +6197,8 @@ os_pidfd_open(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec pid_t pid; unsigned int flags = 0; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -6221,7 +6274,8 @@ os_setns(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kw int fd; int nstype = 0; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -6294,7 +6348,8 @@ os_unshare(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject * PyObject *argsbuf[1]; int flags; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -6364,7 +6419,8 @@ os_readlink(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject path_t path = PATH_T_INITIALIZE_P("readlink", "path", 0, 0, 0, 0); int dir_fd = DEFAULT_DIR_FD; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -6450,7 +6506,8 @@ os_symlink(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject * int target_is_directory = 0; int dir_fd = DEFAULT_DIR_FD; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -6579,7 +6636,8 @@ os_timerfd_create(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyO int clockid; int flags = 0; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -6663,7 +6721,8 @@ os_timerfd_settime(PyObject *module, PyObject *const *args, Py_ssize_t nargs, Py double initial_double = 0.0; double interval_double = 0.0; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -6777,7 +6836,8 @@ os_timerfd_settime_ns(PyObject *module, PyObject *const *args, Py_ssize_t nargs, long long initial = 0; long long interval = 0; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -7109,7 +7169,8 @@ os_open(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwn int dir_fd = DEFAULT_DIR_FD; int _return_value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -7197,7 +7258,8 @@ os_close(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kw PyObject *argsbuf[1]; int fd; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -7330,7 +7392,8 @@ os_dup2(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwn int inheritable = 1; int _return_value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -7790,7 +7853,8 @@ os_sendfile(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject PyObject *trailers = NULL; int flags = 0; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 4, 7, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 4, /*maxpos*/ 7, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -7892,7 +7956,8 @@ os_sendfile(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject PyObject *trailers = NULL; int flags = 0; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 4, 7, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 4, /*maxpos*/ 7, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -7997,7 +8062,8 @@ os_sendfile(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject PyObject *offobj; Py_ssize_t count; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 4, 4, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 4, /*maxpos*/ 4, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -8122,7 +8188,8 @@ os_fstat(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kw PyObject *argsbuf[1]; int fd; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -8475,7 +8542,8 @@ os_copy_file_range(PyObject *module, PyObject *const *args, Py_ssize_t nargs, Py PyObject *offset_src = Py_None; PyObject *offset_dst = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 3, 5, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 3, /*maxpos*/ 5, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -8590,7 +8658,8 @@ os_splice(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *k PyObject *offset_dst = Py_None; unsigned int flags = 0; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 3, 6, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 3, /*maxpos*/ 6, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -8695,7 +8764,8 @@ os_mkfifo(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *k int mode = 438; int dir_fd = DEFAULT_DIR_FD; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -8796,7 +8866,8 @@ os_mknod(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kw dev_t device = 0; int dir_fd = DEFAULT_DIR_FD; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -9036,7 +9107,8 @@ os_truncate(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject path_t path = PATH_T_INITIALIZE_P("truncate", "path", 0, 0, 0, PATH_HAVE_FTRUNCATE); Py_off_t length; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -9428,7 +9500,8 @@ os_WIFCONTINUED(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj int status; int _return_value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -9495,7 +9568,8 @@ os_WIFSTOPPED(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec int status; int _return_value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -9562,7 +9636,8 @@ os_WIFSIGNALED(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObje int status; int _return_value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -9629,7 +9704,8 @@ os_WIFEXITED(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject int status; int _return_value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -9696,7 +9772,8 @@ os_WEXITSTATUS(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObje int status; int _return_value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -9763,7 +9840,8 @@ os_WTERMSIG(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject int status; int _return_value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -9830,7 +9908,8 @@ os_WSTOPSIG(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject int status; int _return_value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -9934,7 +10013,8 @@ os_statvfs(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject * PyObject *argsbuf[1]; path_t path = PATH_T_INITIALIZE_P("statvfs", "path", 0, 0, 0, PATH_HAVE_FSTATVFS); - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -9998,7 +10078,8 @@ os__getdiskusage(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb PyObject *argsbuf[1]; path_t path = PATH_T_INITIALIZE_P("_getdiskusage", "path", 0, 0, 0, 0); - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -10114,7 +10195,8 @@ os_pathconf(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject int name; long _return_value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -10306,7 +10388,8 @@ os_startfile(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject path_t cwd = PATH_T_INITIALIZE_P("startfile", "cwd", 1, 0, 0, 0); int show_cmd = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 5, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 5, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -10445,7 +10528,8 @@ os_device_encoding(PyObject *module, PyObject *const *args, Py_ssize_t nargs, Py PyObject *argsbuf[1]; int fd; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -10642,7 +10726,8 @@ os_getxattr(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject path_t attribute = PATH_T_INITIALIZE_P("getxattr", "attribute", 0, 0, 0, 0); int follow_symlinks = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -10731,7 +10816,8 @@ os_setxattr(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject int flags = 0; int follow_symlinks = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 3, 4, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 3, /*maxpos*/ 4, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -10837,7 +10923,8 @@ os_removexattr(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObje path_t attribute = PATH_T_INITIALIZE_P("removexattr", "attribute", 0, 0, 0, 0); int follow_symlinks = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -10922,7 +11009,8 @@ os_listxattr(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject path_t path = PATH_T_INITIALIZE_P("listxattr", "path", 1, 0, 0, 1); int follow_symlinks = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -11040,7 +11128,8 @@ os_memfd_create(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj PyObject *name = NULL; unsigned int flags = MFD_CLOEXEC; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -11114,7 +11203,8 @@ os_eventfd(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject * unsigned int initval; int flags = EFD_CLOEXEC; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -11183,7 +11273,8 @@ os_eventfd_read(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj PyObject *argsbuf[1]; int fd; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -11246,7 +11337,8 @@ os_eventfd_write(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb int fd; unsigned long long value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -11663,7 +11755,8 @@ os_DirEntry_stat(DirEntry *self, PyTypeObject *defining_class, PyObject *const * Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; int follow_symlinks = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 0, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -11728,7 +11821,8 @@ os_DirEntry_is_dir(DirEntry *self, PyTypeObject *defining_class, PyObject *const int follow_symlinks = 1; int _return_value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 0, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -11797,7 +11891,8 @@ os_DirEntry_is_file(DirEntry *self, PyTypeObject *defining_class, PyObject *cons int follow_symlinks = 1; int _return_value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 0, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -11906,7 +12001,8 @@ os_scandir(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject * Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; path_t path = PATH_T_INITIALIZE_P("scandir", "path", 1, 0, 0, PATH_HAVE_FDOPENDIR); - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -11974,7 +12070,8 @@ os_fspath(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *k PyObject *argsbuf[1]; PyObject *path; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -12033,7 +12130,8 @@ os_getrandom(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject Py_ssize_t size; int flags = 0; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -12118,7 +12216,8 @@ os__add_dll_directory(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *argsbuf[1]; path_t path = PATH_T_INITIALIZE_P("_add_dll_directory", "path", 0, 0, 0, 0); - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -12186,7 +12285,8 @@ os__remove_dll_directory(PyObject *module, PyObject *const *args, Py_ssize_t nar PyObject *argsbuf[1]; PyObject *cookie; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -12257,7 +12357,8 @@ os_waitstatus_to_exitcode(PyObject *module, PyObject *const *args, Py_ssize_t na PyObject *argsbuf[1]; PyObject *status_obj; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -13013,4 +13114,4 @@ os__create_environ(PyObject *module, PyObject *Py_UNUSED(ignored)) #ifndef OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF #define OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF #endif /* !defined(OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF) */ -/*[clinic end generated code: output=9756767bdbdabe94 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=5358a13b4ce6148b input=a9049054013a1b77]*/ diff --git a/Modules/clinic/pyexpat.c.h b/Modules/clinic/pyexpat.c.h index 682d8481a2a2f4..e57aa8a07d78c7 100644 --- a/Modules/clinic/pyexpat.c.h +++ b/Modules/clinic/pyexpat.c.h @@ -91,7 +91,8 @@ pyexpat_xmlparser_Parse(xmlparseobject *self, PyTypeObject *cls, PyObject *const PyObject *data; int isfinal = 0; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -143,7 +144,8 @@ pyexpat_xmlparser_ParseFile(xmlparseobject *self, PyTypeObject *cls, PyObject *c PyObject *argsbuf[1]; PyObject *file; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -267,7 +269,8 @@ pyexpat_xmlparser_ExternalEntityParserCreate(xmlparseobject *self, PyTypeObject const char *context; const char *encoding = NULL; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -384,7 +387,8 @@ pyexpat_xmlparser_UseForeignDTD(xmlparseobject *self, PyTypeObject *cls, PyObjec PyObject *argsbuf[1]; int flag = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -453,7 +457,8 @@ pyexpat_ParserCreate(PyObject *module, PyObject *const *args, Py_ssize_t nargs, const char *namespace_separator = NULL; PyObject *intern = NULL; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -545,4 +550,4 @@ pyexpat_ErrorString(PyObject *module, PyObject *arg) #ifndef PYEXPAT_XMLPARSER_USEFOREIGNDTD_METHODDEF #define PYEXPAT_XMLPARSER_USEFOREIGNDTD_METHODDEF #endif /* !defined(PYEXPAT_XMLPARSER_USEFOREIGNDTD_METHODDEF) */ -/*[clinic end generated code: output=9f1e9a7192d29976 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=63be65cb1823b5f8 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/selectmodule.c.h b/Modules/clinic/selectmodule.c.h index 49c0e48d2e0eac..806a888d6b8cd9 100644 --- a/Modules/clinic/selectmodule.c.h +++ b/Modules/clinic/selectmodule.c.h @@ -596,7 +596,8 @@ select_epoll(PyTypeObject *type, PyObject *args, PyObject *kwargs) int sizehint = -1; int flags = 0; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 0, 2, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -766,7 +767,8 @@ select_epoll_register(pyEpoll_Object *self, PyObject *const *args, Py_ssize_t na int fd; unsigned int eventmask = EPOLLIN | EPOLLPRI | EPOLLOUT; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -843,7 +845,8 @@ select_epoll_modify(pyEpoll_Object *self, PyObject *const *args, Py_ssize_t narg int fd; unsigned int eventmask; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -912,7 +915,8 @@ select_epoll_unregister(pyEpoll_Object *self, PyObject *const *args, Py_ssize_t PyObject *argsbuf[1]; int fd; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -986,7 +990,8 @@ select_epoll_poll(pyEpoll_Object *self, PyObject *const *args, Py_ssize_t nargs, PyObject *timeout_obj = Py_None; int maxevents = -1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1360,4 +1365,4 @@ select_kqueue_control(kqueue_queue_Object *self, PyObject *const *args, Py_ssize #ifndef SELECT_KQUEUE_CONTROL_METHODDEF #define SELECT_KQUEUE_CONTROL_METHODDEF #endif /* !defined(SELECT_KQUEUE_CONTROL_METHODDEF) */ -/*[clinic end generated code: output=f99427b75cbe6d44 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=78b4e67f7d401b5e input=a9049054013a1b77]*/ diff --git a/Modules/clinic/sha1module.c.h b/Modules/clinic/sha1module.c.h index b89c7e505c788e..6af77ba64ecce6 100644 --- a/Modules/clinic/sha1module.c.h +++ b/Modules/clinic/sha1module.c.h @@ -121,7 +121,8 @@ _sha1_sha1(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject * PyObject *string = NULL; int usedforsecurity = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -148,4 +149,4 @@ _sha1_sha1(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject * exit: return return_value; } -/*[clinic end generated code: output=af5a640df662066f input=a9049054013a1b77]*/ +/*[clinic end generated code: output=917e2789f1f5ebf9 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/sha2module.c.h b/Modules/clinic/sha2module.c.h index cf4b88d52856b8..fec655a0dfaa58 100644 --- a/Modules/clinic/sha2module.c.h +++ b/Modules/clinic/sha2module.c.h @@ -188,7 +188,8 @@ _sha2_sha256(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject PyObject *string = NULL; int usedforsecurity = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -262,7 +263,8 @@ _sha2_sha224(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject PyObject *string = NULL; int usedforsecurity = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -336,7 +338,8 @@ _sha2_sha512(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject PyObject *string = NULL; int usedforsecurity = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -410,7 +413,8 @@ _sha2_sha384(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject PyObject *string = NULL; int usedforsecurity = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -437,4 +441,4 @@ _sha2_sha384(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject exit: return return_value; } -/*[clinic end generated code: output=b46da764024b1764 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=602a6939b8ec0927 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/sha3module.c.h b/Modules/clinic/sha3module.c.h index 738e9589503900..d9f4b66f81a038 100644 --- a/Modules/clinic/sha3module.c.h +++ b/Modules/clinic/sha3module.c.h @@ -54,7 +54,8 @@ py_sha3_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) PyObject *data = NULL; int usedforsecurity = 1; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 0, 1, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -194,4 +195,4 @@ _sha3_shake_128_hexdigest(SHA3object *self, PyObject *arg) exit: return return_value; } -/*[clinic end generated code: output=a8e76a880d1421ec input=a9049054013a1b77]*/ +/*[clinic end generated code: output=5c644eb0ed42b993 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/signalmodule.c.h b/Modules/clinic/signalmodule.c.h index 986c0289f2bfcb..dd8da456ce08ce 100644 --- a/Modules/clinic/signalmodule.c.h +++ b/Modules/clinic/signalmodule.c.h @@ -332,7 +332,8 @@ signal_set_wakeup_fd(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *fdobj; int warn_on_full_buffer = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -776,4 +777,4 @@ signal_pidfd_send_signal(PyObject *module, PyObject *const *args, Py_ssize_t nar #ifndef SIGNAL_PIDFD_SEND_SIGNAL_METHODDEF #define SIGNAL_PIDFD_SEND_SIGNAL_METHODDEF #endif /* !defined(SIGNAL_PIDFD_SEND_SIGNAL_METHODDEF) */ -/*[clinic end generated code: output=c57b4b98fad6f4b8 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=356e1acc44a4f377 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/socketmodule.c.h b/Modules/clinic/socketmodule.c.h index 7b0a3f8d4b1cc6..db1a28b86c8773 100644 --- a/Modules/clinic/socketmodule.c.h +++ b/Modules/clinic/socketmodule.c.h @@ -77,7 +77,8 @@ sock_initobj(PyObject *self, PyObject *args, PyObject *kwargs) int proto = -1; PyObject *fdobj = NULL; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 0, 4, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 0, /*maxpos*/ 4, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -286,4 +287,4 @@ _socket_socket_if_nametoindex(PySocketSockObject *self, PyObject *arg) #ifndef _SOCKET_SOCKET_IF_NAMETOINDEX_METHODDEF #define _SOCKET_SOCKET_IF_NAMETOINDEX_METHODDEF #endif /* !defined(_SOCKET_SOCKET_IF_NAMETOINDEX_METHODDEF) */ -/*[clinic end generated code: output=6037e47b012911c5 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=59c36bb31b05de68 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/syslogmodule.c.h b/Modules/clinic/syslogmodule.c.h index 77cf24ea5ba9ca..7c3fcd64191500 100644 --- a/Modules/clinic/syslogmodule.c.h +++ b/Modules/clinic/syslogmodule.c.h @@ -58,7 +58,8 @@ syslog_openlog(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObje long logopt = 0; long facility = LOG_USER; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -262,4 +263,4 @@ syslog_LOG_UPTO(PyObject *module, PyObject *arg) exit: return return_value; } -/*[clinic end generated code: output=8d25899bd31969d3 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=f867a4f7a9267de1 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/zlibmodule.c.h b/Modules/clinic/zlibmodule.c.h index 7ff3edf5a557f8..19906dc328d897 100644 --- a/Modules/clinic/zlibmodule.c.h +++ b/Modules/clinic/zlibmodule.c.h @@ -63,7 +63,8 @@ zlib_compress(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec int level = Z_DEFAULT_COMPRESSION; int wbits = MAX_WBITS; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -153,7 +154,8 @@ zlib_decompress(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj int wbits = MAX_WBITS; Py_ssize_t bufsize = DEF_BUF_SIZE; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -271,7 +273,8 @@ zlib_compressobj(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb int strategy = Z_DEFAULT_STRATEGY; Py_buffer zdict = {NULL, NULL}; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 6, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 6, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -390,7 +393,8 @@ zlib_decompressobj(PyObject *module, PyObject *const *args, Py_ssize_t nargs, Py int wbits = MAX_WBITS; PyObject *zdict = NULL; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -454,7 +458,8 @@ zlib_Compress_compress(compobject *self, PyTypeObject *cls, PyObject *const *arg PyObject *argsbuf[1]; Py_buffer data = {NULL, NULL}; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -530,7 +535,8 @@ zlib_Decompress_decompress(compobject *self, PyTypeObject *cls, PyObject *const Py_buffer data = {NULL, NULL}; Py_ssize_t max_length = 0; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -602,7 +608,8 @@ zlib_Compress_flush(compobject *self, PyTypeObject *cls, PyObject *const *args, PyObject *argsbuf[1]; int mode = Z_FINISH; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -705,7 +712,8 @@ zlib_Compress___deepcopy__(compobject *self, PyTypeObject *cls, PyObject *const PyObject *argsbuf[1]; PyObject *memo; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -803,7 +811,8 @@ zlib_Decompress___deepcopy__(compobject *self, PyTypeObject *cls, PyObject *cons PyObject *argsbuf[1]; PyObject *memo; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -852,7 +861,8 @@ zlib_Decompress_flush(compobject *self, PyTypeObject *cls, PyObject *const *args PyObject *argsbuf[1]; Py_ssize_t length = DEF_BUF_SIZE; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -938,7 +948,8 @@ zlib_ZlibDecompressor_decompress(ZlibDecompressor *self, PyObject *const *args, Py_buffer data = {NULL, NULL}; Py_ssize_t max_length = -1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1098,4 +1109,4 @@ zlib_crc32(PyObject *module, PyObject *const *args, Py_ssize_t nargs) #ifndef ZLIB_DECOMPRESS___DEEPCOPY___METHODDEF #define ZLIB_DECOMPRESS___DEEPCOPY___METHODDEF #endif /* !defined(ZLIB_DECOMPRESS___DEEPCOPY___METHODDEF) */ -/*[clinic end generated code: output=8bb840fb6af43dd4 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=2fef49f168842b17 input=a9049054013a1b77]*/ diff --git a/Objects/clinic/bytearrayobject.c.h b/Objects/clinic/bytearrayobject.c.h index c748c53e1c0a75..dee7c1e8bffd25 100644 --- a/Objects/clinic/bytearrayobject.c.h +++ b/Objects/clinic/bytearrayobject.c.h @@ -50,7 +50,8 @@ bytearray___init__(PyObject *self, PyObject *args, PyObject *kwargs) const char *encoding = NULL; const char *errors = NULL; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 0, 3, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 0, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -617,7 +618,8 @@ bytearray_translate(PyByteArrayObject *self, PyObject *const *args, Py_ssize_t n PyObject *table; PyObject *deletechars = NULL; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -804,7 +806,8 @@ bytearray_split(PyByteArrayObject *self, PyObject *const *args, Py_ssize_t nargs PyObject *sep = Py_None; Py_ssize_t maxsplit = -1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -926,7 +929,8 @@ bytearray_rsplit(PyByteArrayObject *self, PyObject *const *args, Py_ssize_t narg PyObject *sep = Py_None; Py_ssize_t maxsplit = -1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1303,7 +1307,8 @@ bytearray_decode(PyByteArrayObject *self, PyObject *const *args, Py_ssize_t narg const char *encoding = NULL; const char *errors = NULL; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1409,7 +1414,8 @@ bytearray_splitlines(PyByteArrayObject *self, PyObject *const *args, Py_ssize_t Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; int keepends = 0; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1522,7 +1528,8 @@ bytearray_hex(PyByteArrayObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *sep = NULL; int bytes_per_sep = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1616,4 +1623,4 @@ bytearray_sizeof(PyByteArrayObject *self, PyObject *Py_UNUSED(ignored)) { return bytearray_sizeof_impl(self); } -/*[clinic end generated code: output=5f861b02e3fa278b input=a9049054013a1b77]*/ +/*[clinic end generated code: output=4488e38e7ffcc6ec input=a9049054013a1b77]*/ diff --git a/Objects/clinic/bytesobject.c.h b/Objects/clinic/bytesobject.c.h index 0b4b37501735c1..d2c6cc88770999 100644 --- a/Objects/clinic/bytesobject.c.h +++ b/Objects/clinic/bytesobject.c.h @@ -81,7 +81,8 @@ bytes_split(PyBytesObject *self, PyObject *const *args, Py_ssize_t nargs, PyObje PyObject *sep = Py_None; Py_ssize_t maxsplit = -1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -247,7 +248,8 @@ bytes_rsplit(PyBytesObject *self, PyObject *const *args, Py_ssize_t nargs, PyObj PyObject *sep = Py_None; Py_ssize_t maxsplit = -1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -702,7 +704,8 @@ bytes_translate(PyBytesObject *self, PyObject *const *args, Py_ssize_t nargs, Py PyObject *table; PyObject *deletechars = NULL; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1063,7 +1066,8 @@ bytes_decode(PyBytesObject *self, PyObject *const *args, Py_ssize_t nargs, PyObj const char *encoding = NULL; const char *errors = NULL; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1156,7 +1160,8 @@ bytes_splitlines(PyBytesObject *self, PyObject *const *args, Py_ssize_t nargs, P Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; int keepends = 0; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1269,7 +1274,8 @@ bytes_hex(PyBytesObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject PyObject *sep = NULL; int bytes_per_sep = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1334,7 +1340,8 @@ bytes_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) const char *encoding = NULL; const char *errors = NULL; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 0, 3, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 0, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -1384,4 +1391,4 @@ bytes_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=d6801c6001e57f91 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=fb7939a1983e463a input=a9049054013a1b77]*/ diff --git a/Objects/clinic/codeobject.c.h b/Objects/clinic/codeobject.c.h index 68e2d7f528c061..45738f767df50f 100644 --- a/Objects/clinic/codeobject.c.h +++ b/Objects/clinic/codeobject.c.h @@ -223,7 +223,8 @@ code_replace(PyCodeObject *self, PyObject *const *args, Py_ssize_t nargs, PyObje PyObject *co_linetable = self->co_linetable; PyObject *co_exceptiontable = self->co_exceptiontable; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 0, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -451,7 +452,8 @@ code__varname_from_oparg(PyCodeObject *self, PyObject *const *args, Py_ssize_t n PyObject *argsbuf[1]; int oparg; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -464,4 +466,4 @@ code__varname_from_oparg(PyCodeObject *self, PyObject *const *args, Py_ssize_t n exit: return return_value; } -/*[clinic end generated code: output=d604263a3ca72a0f input=a9049054013a1b77]*/ +/*[clinic end generated code: output=e919ea67a1bcf524 input=a9049054013a1b77]*/ diff --git a/Objects/clinic/complexobject.c.h b/Objects/clinic/complexobject.c.h index 58fd4e26871b4d..3c3d1071b6eec3 100644 --- a/Objects/clinic/complexobject.c.h +++ b/Objects/clinic/complexobject.c.h @@ -140,7 +140,8 @@ complex_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) PyObject *r = NULL; PyObject *i = NULL; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 0, 2, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -169,4 +170,4 @@ PyDoc_STRVAR(complex_from_number__doc__, #define COMPLEX_FROM_NUMBER_METHODDEF \ {"from_number", (PyCFunction)complex_from_number, METH_O|METH_CLASS, complex_from_number__doc__}, -/*[clinic end generated code: output=188438cc9ae167f7 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=8c49a41c5a7f0aee input=a9049054013a1b77]*/ diff --git a/Objects/clinic/descrobject.c.h b/Objects/clinic/descrobject.c.h index d79be80d3ec165..2f96adb5094fe3 100644 --- a/Objects/clinic/descrobject.c.h +++ b/Objects/clinic/descrobject.c.h @@ -51,7 +51,8 @@ mappingproxy_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) Py_ssize_t nargs = PyTuple_GET_SIZE(args); PyObject *mapping; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 1, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -141,7 +142,8 @@ property_init(PyObject *self, PyObject *args, PyObject *kwargs) PyObject *fdel = NULL; PyObject *doc = NULL; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 0, 4, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 0, /*maxpos*/ 4, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -173,4 +175,4 @@ property_init(PyObject *self, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=050e331316a04207 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=2f43f9297a36aa40 input=a9049054013a1b77]*/ diff --git a/Objects/clinic/enumobject.c.h b/Objects/clinic/enumobject.c.h index 09774a73c8145c..998b2eb121c6d9 100644 --- a/Objects/clinic/enumobject.c.h +++ b/Objects/clinic/enumobject.c.h @@ -62,7 +62,8 @@ enum_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) PyObject *iterable; PyObject *start = 0; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 2, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -107,4 +108,4 @@ reversed_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=5c48a9a482a52e91 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=2e339ade8bedb3a0 input=a9049054013a1b77]*/ diff --git a/Objects/clinic/funcobject.c.h b/Objects/clinic/funcobject.c.h index 8f20bda26438cf..3f95a1db7b2788 100644 --- a/Objects/clinic/funcobject.c.h +++ b/Objects/clinic/funcobject.c.h @@ -73,7 +73,8 @@ func_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) PyObject *closure = Py_None; PyObject *kwdefaults = Py_None; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 2, 6, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 2, /*maxpos*/ 6, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -115,4 +116,4 @@ func_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=10947342188f38a9 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=bad4e19757dd26c3 input=a9049054013a1b77]*/ diff --git a/Objects/clinic/listobject.c.h b/Objects/clinic/listobject.c.h index 588e021fb71fd3..975f253c096275 100644 --- a/Objects/clinic/listobject.c.h +++ b/Objects/clinic/listobject.c.h @@ -235,7 +235,8 @@ list_sort(PyListObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject PyObject *keyfunc = Py_None; int reverse = 0; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 0, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -439,4 +440,4 @@ list___reversed__(PyListObject *self, PyObject *Py_UNUSED(ignored)) { return list___reversed___impl(self); } -/*[clinic end generated code: output=854957a1d4a89bbd input=a9049054013a1b77]*/ +/*[clinic end generated code: output=9357151278d77ea1 input=a9049054013a1b77]*/ diff --git a/Objects/clinic/longobject.c.h b/Objects/clinic/longobject.c.h index 90375b9a082cca..c025f74af5cb58 100644 --- a/Objects/clinic/longobject.c.h +++ b/Objects/clinic/longobject.c.h @@ -48,7 +48,8 @@ long_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) PyObject *x = NULL; PyObject *obase = NULL; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 0, 2, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -315,7 +316,8 @@ int_to_bytes(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject * PyObject *byteorder = NULL; int is_signed = 0; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -426,7 +428,8 @@ int_from_bytes(PyTypeObject *type, PyObject *const *args, Py_ssize_t nargs, PyOb PyObject *byteorder = NULL; int is_signed = 0; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -476,4 +479,4 @@ int_is_integer(PyObject *self, PyObject *Py_UNUSED(ignored)) { return int_is_integer_impl(self); } -/*[clinic end generated code: output=a53f5ba9a6c16737 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=fb96bd642a643f75 input=a9049054013a1b77]*/ diff --git a/Objects/clinic/memoryobject.c.h b/Objects/clinic/memoryobject.c.h index f199434dacb9e8..185d7819b6b84a 100644 --- a/Objects/clinic/memoryobject.c.h +++ b/Objects/clinic/memoryobject.c.h @@ -51,7 +51,8 @@ memoryview(PyTypeObject *type, PyObject *args, PyObject *kwargs) Py_ssize_t nargs = PyTuple_GET_SIZE(args); PyObject *object; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 1, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -107,7 +108,8 @@ memoryview__from_flags(PyTypeObject *type, PyObject *const *args, Py_ssize_t nar PyObject *object; int flags; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -187,7 +189,8 @@ memoryview_cast(PyMemoryViewObject *self, PyObject *const *args, Py_ssize_t narg PyObject *format; PyObject *shape = NULL; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -294,7 +297,8 @@ memoryview_tobytes(PyMemoryViewObject *self, PyObject *const *args, Py_ssize_t n Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; const char *order = NULL; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -390,7 +394,8 @@ memoryview_hex(PyMemoryViewObject *self, PyObject *const *args, Py_ssize_t nargs PyObject *sep = NULL; int bytes_per_sep = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -413,4 +418,4 @@ memoryview_hex(PyMemoryViewObject *self, PyObject *const *args, Py_ssize_t nargs exit: return return_value; } -/*[clinic end generated code: output=7e76a09106921ba2 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=0a93f08110630633 input=a9049054013a1b77]*/ diff --git a/Objects/clinic/moduleobject.c.h b/Objects/clinic/moduleobject.c.h index 3c0bbe22d5ebab..5535a2f1178b9f 100644 --- a/Objects/clinic/moduleobject.c.h +++ b/Objects/clinic/moduleobject.c.h @@ -55,7 +55,8 @@ module___init__(PyObject *self, PyObject *args, PyObject *kwargs) PyObject *name; PyObject *doc = Py_None; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 2, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -74,4 +75,4 @@ module___init__(PyObject *self, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=e8a71bfbed774c15 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=d7da56d5c2eb6d30 input=a9049054013a1b77]*/ diff --git a/Objects/clinic/odictobject.c.h b/Objects/clinic/odictobject.c.h index 5ef5380656905a..4b97596e5dbd2f 100644 --- a/Objects/clinic/odictobject.c.h +++ b/Objects/clinic/odictobject.c.h @@ -54,7 +54,8 @@ OrderedDict_fromkeys(PyTypeObject *type, PyObject *const *args, Py_ssize_t nargs PyObject *seq; PyObject *value = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -119,7 +120,8 @@ OrderedDict_setdefault(PyODictObject *self, PyObject *const *args, Py_ssize_t na PyObject *key; PyObject *default_value = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -185,7 +187,8 @@ OrderedDict_pop(PyODictObject *self, PyObject *const *args, Py_ssize_t nargs, Py PyObject *key; PyObject *default_value = NULL; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -248,7 +251,8 @@ OrderedDict_popitem(PyODictObject *self, PyObject *const *args, Py_ssize_t nargs Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; int last = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -314,7 +318,8 @@ OrderedDict_move_to_end(PyODictObject *self, PyObject *const *args, Py_ssize_t n PyObject *key; int last = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -332,4 +337,4 @@ OrderedDict_move_to_end(PyODictObject *self, PyObject *const *args, Py_ssize_t n exit: return return_value; } -/*[clinic end generated code: output=eff78d2a3f9379bd input=a9049054013a1b77]*/ +/*[clinic end generated code: output=2aa6fc0567c9252c input=a9049054013a1b77]*/ diff --git a/Objects/clinic/structseq.c.h b/Objects/clinic/structseq.c.h index cec49ebccccb24..0afb21ad918669 100644 --- a/Objects/clinic/structseq.c.h +++ b/Objects/clinic/structseq.c.h @@ -47,7 +47,8 @@ structseq_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) PyObject *arg; PyObject *dict = NULL; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 2, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -62,4 +63,4 @@ structseq_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=5bf39b3f06a34ce4 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=ef2406240ce0ad3f input=a9049054013a1b77]*/ diff --git a/Objects/clinic/typevarobject.c.h b/Objects/clinic/typevarobject.c.h index 3f439a78ded241..c17998830b02eb 100644 --- a/Objects/clinic/typevarobject.c.h +++ b/Objects/clinic/typevarobject.c.h @@ -6,7 +6,7 @@ preserve # include "pycore_gc.h" // PyGC_Head # include "pycore_runtime.h" // _Py_ID() #endif -#include "pycore_modsupport.h" // _PyArg_UnpackKeywordsWithVararg() +#include "pycore_modsupport.h" // _PyArg_UnpackKeywords() PyDoc_STRVAR(typevar_new__doc__, "typevar(name, *constraints, bound=None, default=typing.NoDefault,\n" @@ -61,7 +61,8 @@ typevar_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) int contravariant = 0; int infer_variance = 0; - fastargs = _PyArg_UnpackKeywordsWithVararg(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 1, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 1, argsbuf); if (!fastargs) { goto exit; } @@ -236,7 +237,8 @@ paramspecargs_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) Py_ssize_t nargs = PyTuple_GET_SIZE(args); PyObject *origin; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 1, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -290,7 +292,8 @@ paramspeckwargs_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) Py_ssize_t nargs = PyTuple_GET_SIZE(args); PyObject *origin; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 1, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -353,7 +356,8 @@ paramspec_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) int contravariant = 0; int infer_variance = 0; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 1, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -524,7 +528,8 @@ typevartuple(PyTypeObject *type, PyObject *args, PyObject *kwargs) PyObject *name; PyObject *default_value = &_Py_NoDefaultStruct; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 1, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -680,7 +685,8 @@ typealias_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) PyObject *value; PyObject *type_params = NULL; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 2, 2, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -700,4 +706,4 @@ typealias_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=32b9e6ced80d3fb0 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=26351c3549f5ad83 input=a9049054013a1b77]*/ diff --git a/Objects/clinic/unicodeobject.c.h b/Objects/clinic/unicodeobject.c.h index 78e14b0021d006..361f20c0fa4c1d 100644 --- a/Objects/clinic/unicodeobject.c.h +++ b/Objects/clinic/unicodeobject.c.h @@ -246,7 +246,8 @@ unicode_encode(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject const char *encoding = NULL; const char *errors = NULL; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -338,7 +339,8 @@ unicode_expandtabs(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyOb Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; int tabsize = 8; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -958,7 +960,8 @@ unicode_replace(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObjec PyObject *new; Py_ssize_t count = -1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1282,7 +1285,8 @@ unicode_split(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject PyObject *sep = Py_None; Py_ssize_t maxsplit = -1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1404,7 +1408,8 @@ unicode_rsplit(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject PyObject *sep = Py_None; Py_ssize_t maxsplit = -1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1484,7 +1489,8 @@ unicode_splitlines(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyOb Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; int keepends = 0; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1838,7 +1844,8 @@ unicode_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) const char *encoding = NULL; const char *errors = NULL; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 0, 3, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 0, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -1888,4 +1895,4 @@ unicode_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=9fee62bd337f809b input=a9049054013a1b77]*/ +/*[clinic end generated code: output=30efbf79c5a07dd2 input=a9049054013a1b77]*/ diff --git a/Objects/stringlib/clinic/transmogrify.h.h b/Objects/stringlib/clinic/transmogrify.h.h index cef7a9496fa874..5b82c2de737249 100644 --- a/Objects/stringlib/clinic/transmogrify.h.h +++ b/Objects/stringlib/clinic/transmogrify.h.h @@ -56,7 +56,8 @@ stringlib_expandtabs(PyObject *self, PyObject *const *args, Py_ssize_t nargs, Py Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; int tabsize = 8; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -321,4 +322,4 @@ stringlib_zfill(PyObject *self, PyObject *arg) exit: return return_value; } -/*[clinic end generated code: output=06dd79019356b6bb input=a9049054013a1b77]*/ +/*[clinic end generated code: output=0e24a10bac3ec053 input=a9049054013a1b77]*/ diff --git a/PC/clinic/_wmimodule.cpp.h b/PC/clinic/_wmimodule.cpp.h index fccf3d795d5340..f0be691173ad64 100644 --- a/PC/clinic/_wmimodule.cpp.h +++ b/PC/clinic/_wmimodule.cpp.h @@ -55,7 +55,8 @@ _wmi_exec_query(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj PyObject *argsbuf[1]; PyObject *query; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -69,4 +70,4 @@ _wmi_exec_query(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj exit: return return_value; } -/*[clinic end generated code: output=ba04920d127f3ceb input=a9049054013a1b77]*/ +/*[clinic end generated code: output=9ac5fd7faf889e52 input=a9049054013a1b77]*/ diff --git a/PC/clinic/winreg.c.h b/PC/clinic/winreg.c.h index 7e5545e113e718..b14913366b77eb 100644 --- a/PC/clinic/winreg.c.h +++ b/PC/clinic/winreg.c.h @@ -351,7 +351,8 @@ winreg_CreateKeyEx(PyObject *module, PyObject *const *args, Py_ssize_t nargs, Py REGSAM access = KEY_WRITE; HKEY _return_value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 4, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 4, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -534,7 +535,8 @@ winreg_DeleteKeyEx(PyObject *module, PyObject *const *args, Py_ssize_t nargs, Py REGSAM access = KEY_WOW64_64KEY; int reserved = 0; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 4, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 4, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -966,7 +968,8 @@ winreg_OpenKey(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObje REGSAM access = KEY_READ; HKEY _return_value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 4, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 4, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1083,7 +1086,8 @@ winreg_OpenKeyEx(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb REGSAM access = KEY_READ; HKEY _return_value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 4, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 4, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1762,4 +1766,4 @@ winreg_QueryReflectionKey(PyObject *module, PyObject *arg) #ifndef WINREG_QUERYREFLECTIONKEY_METHODDEF #define WINREG_QUERYREFLECTIONKEY_METHODDEF #endif /* !defined(WINREG_QUERYREFLECTIONKEY_METHODDEF) */ -/*[clinic end generated code: output=1ee4098b2f143b6a input=a9049054013a1b77]*/ +/*[clinic end generated code: output=aef4aa8ab8ddf38f input=a9049054013a1b77]*/ diff --git a/Python/clinic/Python-tokenize.c.h b/Python/clinic/Python-tokenize.c.h index 730fa8ef2a2154..2a0d50bda65f00 100644 --- a/Python/clinic/Python-tokenize.c.h +++ b/Python/clinic/Python-tokenize.c.h @@ -49,7 +49,8 @@ tokenizeriter_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) int extra_tokens; const char *encoding = NULL; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 1, 1, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 1, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -80,4 +81,4 @@ tokenizeriter_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=dcd6ec48f06a092e input=a9049054013a1b77]*/ +/*[clinic end generated code: output=831a75133d4a5034 input=a9049054013a1b77]*/ diff --git a/Python/clinic/_warnings.c.h b/Python/clinic/_warnings.c.h index 98dc49db3059b1..9a2c33f2ea8169 100644 --- a/Python/clinic/_warnings.c.h +++ b/Python/clinic/_warnings.c.h @@ -74,7 +74,8 @@ warnings_warn(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec PyObject *source = Py_None; PyTupleObject *skip_file_prefixes = NULL; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 4, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 4, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -184,7 +185,8 @@ warnings_warn_explicit(PyObject *module, PyObject *const *args, Py_ssize_t nargs PyObject *module_globals = Py_None; PyObject *sourceobj = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 4, 8, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 4, /*maxpos*/ 8, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -244,4 +246,4 @@ warnings_filters_mutated(PyObject *module, PyObject *Py_UNUSED(ignored)) { return warnings_filters_mutated_impl(module); } -/*[clinic end generated code: output=f2d4214c382717a6 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=ed02c0f521a03a37 input=a9049054013a1b77]*/ diff --git a/Python/clinic/bltinmodule.c.h b/Python/clinic/bltinmodule.c.h index 39a23f09b7f390..7d348dbebed385 100644 --- a/Python/clinic/bltinmodule.c.h +++ b/Python/clinic/bltinmodule.c.h @@ -73,7 +73,8 @@ builtin___import__(PyObject *module, PyObject *const *args, Py_ssize_t nargs, Py PyObject *fromlist = NULL; int level = 0; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 5, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 5, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -298,7 +299,8 @@ builtin_compile(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj int optimize = -1; int feature_version = -1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 3, 6, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 3, /*maxpos*/ 6, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -448,7 +450,8 @@ builtin_eval(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject PyObject *globals = Py_None; PyObject *locals = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -527,7 +530,8 @@ builtin_exec(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject PyObject *locals = Py_None; PyObject *closure = NULL; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -865,7 +869,8 @@ builtin_pow(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject PyObject *exp; PyObject *mod = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -944,7 +949,8 @@ builtin_print(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec PyObject *file = Py_None; int flush = 0; - fastargs = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 0, /*minkw*/ 0, /*varpos*/ 1, argsbuf); if (!fastargs) { goto exit; } @@ -1080,7 +1086,8 @@ builtin_round(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec PyObject *number; PyObject *ndigits = Py_None; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1146,7 +1153,8 @@ builtin_sum(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject PyObject *iterable; PyObject *start = NULL; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1231,4 +1239,4 @@ builtin_issubclass(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=5c510ec462507656 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=b0178189d13e8bf8 input=a9049054013a1b77]*/ diff --git a/Python/clinic/import.c.h b/Python/clinic/import.c.h index 5edeaef656ef62..0e6a560d57c8ba 100644 --- a/Python/clinic/import.c.h +++ b/Python/clinic/import.c.h @@ -223,7 +223,8 @@ _imp_find_frozen(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb PyObject *name; int withdata = 0; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -594,7 +595,8 @@ _imp_source_hash(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb long key; Py_buffer source = {NULL, NULL}; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -623,4 +625,4 @@ _imp_source_hash(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb #ifndef _IMP_EXEC_DYNAMIC_METHODDEF #define _IMP_EXEC_DYNAMIC_METHODDEF #endif /* !defined(_IMP_EXEC_DYNAMIC_METHODDEF) */ -/*[clinic end generated code: output=dbd63707bd40b07c input=a9049054013a1b77]*/ +/*[clinic end generated code: output=d0e278351d6adbb1 input=a9049054013a1b77]*/ diff --git a/Python/clinic/instruction_sequence.c.h b/Python/clinic/instruction_sequence.c.h index 66c2ccadfa03c9..45693e5856f8a7 100644 --- a/Python/clinic/instruction_sequence.c.h +++ b/Python/clinic/instruction_sequence.c.h @@ -82,7 +82,8 @@ InstructionSequenceType_use_label(_PyInstructionSequence *self, PyObject *const PyObject *argsbuf[1]; int label; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -148,7 +149,8 @@ InstructionSequenceType_addop(_PyInstructionSequence *self, PyObject *const *arg int end_lineno; int end_col_offset; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 6, 6, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 6, /*maxpos*/ 6, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -255,7 +257,8 @@ InstructionSequenceType_add_nested(_PyInstructionSequence *self, PyObject *const PyObject *argsbuf[1]; PyObject *nested; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -301,4 +304,4 @@ InstructionSequenceType_get_instructions(_PyInstructionSequence *self, PyObject { return InstructionSequenceType_get_instructions_impl(self); } -/*[clinic end generated code: output=8809d7aa11d9b2bb input=a9049054013a1b77]*/ +/*[clinic end generated code: output=35163e5b589b4446 input=a9049054013a1b77]*/ diff --git a/Python/clinic/marshal.c.h b/Python/clinic/marshal.c.h index c19a3ed5050ed3..d9bbb0527416c6 100644 --- a/Python/clinic/marshal.c.h +++ b/Python/clinic/marshal.c.h @@ -70,7 +70,8 @@ marshal_dump(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject int version = Py_MARSHAL_VERSION; int allow_code = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -157,7 +158,8 @@ marshal_load(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject PyObject *file; int allow_code = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -234,7 +236,8 @@ marshal_dumps(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec int version = Py_MARSHAL_VERSION; int allow_code = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -314,7 +317,8 @@ marshal_loads(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec Py_buffer bytes = {NULL, NULL}; int allow_code = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -339,4 +343,4 @@ marshal_loads(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec return return_value; } -/*[clinic end generated code: output=1575b9a3ae48ad3d input=a9049054013a1b77]*/ +/*[clinic end generated code: output=c7ef4f599658d8ab input=a9049054013a1b77]*/ diff --git a/Python/clinic/sysmodule.c.h b/Python/clinic/sysmodule.c.h index 8277d286cf51ef..1777dbec11c39b 100644 --- a/Python/clinic/sysmodule.c.h +++ b/Python/clinic/sysmodule.c.h @@ -52,7 +52,8 @@ sys_addaudithook(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb PyObject *argsbuf[1]; PyObject *hook; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -561,7 +562,8 @@ sys_set_coroutine_origin_tracking_depth(PyObject *module, PyObject *const *args, PyObject *argsbuf[1]; int depth; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -862,7 +864,8 @@ sys_set_int_max_str_digits(PyObject *module, PyObject *const *args, Py_ssize_t n PyObject *argsbuf[1]; int maxdigits; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1013,7 +1016,8 @@ sys_getunicodeinternedsize(PyObject *module, PyObject *const *args, Py_ssize_t n int _only_immortal = 0; Py_ssize_t _return_value; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 0, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1479,7 +1483,8 @@ sys__getframemodulename(PyObject *module, PyObject *const *args, Py_ssize_t narg Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; int depth = 0; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } @@ -1614,4 +1619,4 @@ sys__is_gil_enabled(PyObject *module, PyObject *Py_UNUSED(ignored)) #ifndef SYS_GETANDROIDAPILEVEL_METHODDEF #define SYS_GETANDROIDAPILEVEL_METHODDEF #endif /* !defined(SYS_GETANDROIDAPILEVEL_METHODDEF) */ -/*[clinic end generated code: output=9cc9069aef1482bc input=a9049054013a1b77]*/ +/*[clinic end generated code: output=cf24c260a269a5d2 input=a9049054013a1b77]*/ diff --git a/Python/clinic/traceback.c.h b/Python/clinic/traceback.c.h index fe53a2786d1ad6..9607e773f4be88 100644 --- a/Python/clinic/traceback.c.h +++ b/Python/clinic/traceback.c.h @@ -55,7 +55,8 @@ tb_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) int tb_lasti; int tb_lineno; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 4, 4, 0, argsbuf); + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 4, /*maxpos*/ 4, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; } @@ -78,4 +79,4 @@ tb_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=916a759875507c5a input=a9049054013a1b77]*/ +/*[clinic end generated code: output=62ebc0196940f663 input=a9049054013a1b77]*/ diff --git a/Python/getargs.c b/Python/getargs.c index d529994ad3ac0d..6485a714a4e3fe 100644 --- a/Python/getargs.c +++ b/Python/getargs.c @@ -2308,8 +2308,10 @@ vgetargskeywordsfast(PyObject *args, PyObject *keywords, } +#undef _PyArg_UnpackKeywords + PyObject * const * -_PyArg_UnpackKeywordsEx(PyObject *const *args, Py_ssize_t nargs, +_PyArg_UnpackKeywords(PyObject *const *args, Py_ssize_t nargs, PyObject *kwargs, PyObject *kwnames, struct _PyArg_Parser *parser, int minpos, int maxpos, int minkw, int varpos, diff --git a/Tools/clinic/libclinic/parse_args.py b/Tools/clinic/libclinic/parse_args.py index 08cf697b67e5ae..fc2d9fe987096d 100644 --- a/Tools/clinic/libclinic/parse_args.py +++ b/Tools/clinic/libclinic/parse_args.py @@ -602,15 +602,11 @@ def parse_general(self, clang: CLanguage) -> None: use_parser_code = False self.fastcall = False else: + self.codegen.add_include('pycore_modsupport.h', + '_PyArg_UnpackKeywords()') if not self.varpos: - self.codegen.add_include('pycore_modsupport.h', - '_PyArg_UnpackKeywords()') - unpack_func = '_PyArg_UnpackKeywords' nargs = "nargs" else: - self.codegen.add_include('pycore_modsupport.h', - '_PyArg_UnpackKeywordsWithVararg()') - unpack_func = '_PyArg_UnpackKeywordsWithVararg' nargs = f"Py_MIN(nargs, {self.max_pos})" if self.max_pos else "0" if self.fastcall: @@ -641,10 +637,9 @@ def parse_general(self, clang: CLanguage) -> None: if has_optional_kw: self.declarations += "\nPy_ssize_t noptargs = %s + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - %d;" % (nargs, self.min_pos + self.min_kw_only) unpack_args = '_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL' - unpack_args += (f', &_parser, {self.min_pos}, {self.max_pos}, ' - f'{self.min_kw_only}, argsbuf') parser_code = [libclinic.normalize_snippet(f""" - {argsname} = {unpack_func}({unpack_args}); + {argsname} = _PyArg_UnpackKeywords({unpack_args}, &_parser, + /*minpos*/ {self.min_pos}, /*maxpos*/ {self.max_pos}, /*minkw*/ {self.min_kw_only}, /*varpos*/ {1 if self.varpos else 0}, argsbuf); if (!{argsname}) {{{{ goto exit; }}}} From a3e8e7bbc3d39e9e6cfa5605df9524076176a041 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Fri, 8 Nov 2024 18:21:11 +0530 Subject: [PATCH 106/219] GH-107803: use circular double linked list for tasks in `_asyncio` (#126577) --- Modules/_asynciomodule.c | 73 ++++++++++++---------------------------- 1 file changed, 21 insertions(+), 52 deletions(-) diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 214c18e966c4c1..58b57ae7a983c2 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -138,47 +138,18 @@ typedef struct { /* Counter for autogenerated Task names */ uint64_t task_name_counter; - /* Linked-list of all tasks which are instances of asyncio.Task or subclasses + /* Circular linked-list of all tasks which are instances of asyncio.Task or subclasses of it. Third party tasks implementations which don't inherit from asyncio.Task are tracked separately using the 'non_asyncio_tasks' WeakSet. - `tail` is used as a sentinel to mark the end of the linked-list. It avoids one + `first` is used as a sentinel to mark the end of the linked-list. It avoids one branch in checking for empty list when adding a new task, the list is - initialized with `head` pointing to `tail` to mark an empty list. - - Invariants: - * When the list is empty: - - asyncio_tasks.head == &asyncio_tasks.tail - - asyncio_tasks.head->prev == NULL - - asyncio_tasks.head->next == NULL - - * After adding the first task 'task1': - - asyncio_tasks.head == task1 - - task1->next == &asyncio_tasks.tail - - task1->prev == NULL - - asyncio_tasks.tail.prev == task1 - - * After adding a second task 'task2': - - asyncio_tasks.head == task2 - - task2->next == task1 - - task2->prev == NULL - - task1->prev == task2 - - asyncio_tasks.tail.prev == task1 - - * After removing task 'task1': - - asyncio_tasks.head == task2 - - task2->next == &asyncio_tasks.tail - - task2->prev == NULL - - asyncio_tasks.tail.prev == task2 - - * After removing task 'task2', the list is empty: - - asyncio_tasks.head == &asyncio_tasks.tail - - asyncio_tasks.head->prev == NULL - - asyncio_tasks.tail.prev == NULL - - asyncio_tasks.tail.next == NULL + initialized with `head`, `head->next` and `head->prev` pointing to `first` + to mark an empty list. + */ struct { - TaskObj tail; + TaskObj first; TaskObj *head; } asyncio_tasks; @@ -1925,7 +1896,7 @@ register_task(asyncio_state *state, TaskObj *task) { ASYNCIO_STATE_LOCK(state); assert(Task_Check(state, task)); - assert(task != &state->asyncio_tasks.tail); + assert(task != &state->asyncio_tasks.first); if (task->next != NULL) { // already registered goto exit; @@ -1934,8 +1905,10 @@ register_task(asyncio_state *state, TaskObj *task) assert(state->asyncio_tasks.head != NULL); task->next = state->asyncio_tasks.head; + task->prev = state->asyncio_tasks.head->prev; + state->asyncio_tasks.head->prev->next = task; state->asyncio_tasks.head->prev = task; - state->asyncio_tasks.head = task; + exit: ASYNCIO_STATE_UNLOCK(state); } @@ -1951,7 +1924,7 @@ unregister_task(asyncio_state *state, TaskObj *task) { ASYNCIO_STATE_LOCK(state); assert(Task_Check(state, task)); - assert(task != &state->asyncio_tasks.tail); + assert(task != &state->asyncio_tasks.first); if (task->next == NULL) { // not registered assert(task->prev == NULL); @@ -1959,12 +1932,7 @@ unregister_task(asyncio_state *state, TaskObj *task) goto exit; } task->next->prev = task->prev; - if (task->prev == NULL) { - assert(state->asyncio_tasks.head == task); - state->asyncio_tasks.head = task->next; - } else { - task->prev->next = task->next; - } + task->prev->next = task->next; task->next = NULL; task->prev = NULL; assert(state->asyncio_tasks.head != task); @@ -3657,12 +3625,10 @@ _asyncio_all_tasks_impl(PyObject *module, PyObject *loop) Py_DECREF(eager_iter); int err = 0; ASYNCIO_STATE_LOCK(state); - TaskObj *head = state->asyncio_tasks.head; + TaskObj *first = &state->asyncio_tasks.first; + TaskObj *head = state->asyncio_tasks.head->next; Py_INCREF(head); - assert(head != NULL); - assert(head->prev == NULL); - TaskObj *tail = &state->asyncio_tasks.tail; - while (head != tail) + while (head != first) { if (add_one_task(state, tasks, (PyObject *)head, loop) < 0) { Py_DECREF(tasks); @@ -3875,9 +3841,12 @@ static int module_exec(PyObject *mod) { asyncio_state *state = get_asyncio_state(mod); - Py_SET_TYPE(&state->asyncio_tasks.tail, state->TaskType); - _Py_SetImmortalUntracked((PyObject *)&state->asyncio_tasks.tail); - state->asyncio_tasks.head = &state->asyncio_tasks.tail; + + Py_SET_TYPE(&state->asyncio_tasks.first, state->TaskType); + _Py_SetImmortalUntracked((PyObject *)&state->asyncio_tasks.first); + state->asyncio_tasks.head = &state->asyncio_tasks.first; + state->asyncio_tasks.head->next = &state->asyncio_tasks.first; + state->asyncio_tasks.head->prev = &state->asyncio_tasks.first; #define CREATE_TYPE(m, tp, spec, base) \ do { \ From a93fc0969e4d57a2ecfe18cbba7de2d5ee757e2c Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 8 Nov 2024 15:11:44 +0200 Subject: [PATCH 107/219] gh-126579: Adapt sys.audit() to Argument Clinic (GH-126580) --- Python/clinic/sysmodule.c.h | 51 ++++++++++++++++++++++++++++++++++- Python/sysmodule.c | 54 +++++++++---------------------------- 2 files changed, 63 insertions(+), 42 deletions(-) diff --git a/Python/clinic/sysmodule.c.h b/Python/clinic/sysmodule.c.h index 1777dbec11c39b..86c42ceffc5e31 100644 --- a/Python/clinic/sysmodule.c.h +++ b/Python/clinic/sysmodule.c.h @@ -7,6 +7,7 @@ preserve # include "pycore_runtime.h" // _Py_ID() #endif #include "pycore_modsupport.h" // _PyArg_UnpackKeywords() +#include "pycore_tuple.h" // _PyTuple_FromArray() PyDoc_STRVAR(sys_addaudithook__doc__, "addaudithook($module, /, hook)\n" @@ -64,6 +65,54 @@ sys_addaudithook(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb return return_value; } +PyDoc_STRVAR(sys_audit__doc__, +"audit($module, event, /, *args)\n" +"--\n" +"\n" +"Passes the event to any audit hooks that are attached."); + +#define SYS_AUDIT_METHODDEF \ + {"audit", _PyCFunction_CAST(sys_audit), METH_FASTCALL, sys_audit__doc__}, + +static PyObject * +sys_audit_impl(PyObject *module, const char *event, PyObject *args); + +static PyObject * +sys_audit(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + const char *event; + PyObject *__clinic_args = NULL; + + if (!_PyArg_CheckPositional("audit", nargs, 1, PY_SSIZE_T_MAX)) { + goto exit; + } + if (!PyUnicode_Check(args[0])) { + _PyArg_BadArgument("audit", "argument 1", "str", args[0]); + goto exit; + } + Py_ssize_t event_length; + event = PyUnicode_AsUTF8AndSize(args[0], &event_length); + if (event == NULL) { + goto exit; + } + if (strlen(event) != (size_t)event_length) { + PyErr_SetString(PyExc_ValueError, "embedded null character"); + goto exit; + } + __clinic_args = _PyTuple_FromArray(args + 1, nargs - 1); + if (__clinic_args == NULL) { + goto exit; + } + return_value = sys_audit_impl(module, event, __clinic_args); + +exit: + /* Cleanup for args */ + Py_XDECREF(__clinic_args); + + return return_value; +} + PyDoc_STRVAR(sys_displayhook__doc__, "displayhook($module, object, /)\n" "--\n" @@ -1619,4 +1668,4 @@ sys__is_gil_enabled(PyObject *module, PyObject *Py_UNUSED(ignored)) #ifndef SYS_GETANDROIDAPILEVEL_METHODDEF #define SYS_GETANDROIDAPILEVEL_METHODDEF #endif /* !defined(SYS_GETANDROIDAPILEVEL_METHODDEF) */ -/*[clinic end generated code: output=cf24c260a269a5d2 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=6d4f6cd20419b675 input=a9049054013a1b77]*/ diff --git a/Python/sysmodule.c b/Python/sysmodule.c index a086bb979efa9c..aaef5aa532412b 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -499,56 +499,28 @@ sys_addaudithook_impl(PyObject *module, PyObject *hook) Py_RETURN_NONE; } -PyDoc_STRVAR(audit_doc, -"audit($module, event, /, *args)\n\ ---\n\ -\n\ -Passes the event to any audit hooks that are attached."); +/*[clinic input] +sys.audit + + event: str + / + *args: tuple + +Passes the event to any audit hooks that are attached. +[clinic start generated code]*/ static PyObject * -sys_audit(PyObject *self, PyObject *const *args, Py_ssize_t argc) +sys_audit_impl(PyObject *module, const char *event, PyObject *args) +/*[clinic end generated code: output=1d0fc82da768f49d input=ec3b688527945109]*/ { PyThreadState *tstate = _PyThreadState_GET(); _Py_EnsureTstateNotNULL(tstate); - if (argc == 0) { - _PyErr_SetString(tstate, PyExc_TypeError, - "audit() missing 1 required positional argument: " - "'event'"); - return NULL; - } - - assert(args[0] != NULL); - if (!should_audit(tstate->interp)) { Py_RETURN_NONE; } - PyObject *auditEvent = args[0]; - if (!auditEvent) { - _PyErr_SetString(tstate, PyExc_TypeError, - "expected str for argument 'event'"); - return NULL; - } - if (!PyUnicode_Check(auditEvent)) { - _PyErr_Format(tstate, PyExc_TypeError, - "expected str for argument 'event', not %.200s", - Py_TYPE(auditEvent)->tp_name); - return NULL; - } - const char *event = PyUnicode_AsUTF8(auditEvent); - if (!event) { - return NULL; - } - - PyObject *auditArgs = _PyTuple_FromArray(args + 1, argc - 1); - if (!auditArgs) { - return NULL; - } - - int res = _PySys_Audit(tstate, event, "O", auditArgs); - Py_DECREF(auditArgs); - + int res = _PySys_Audit(tstate, event, "O", args); if (res < 0) { return NULL; } @@ -2564,7 +2536,7 @@ PyAPI_FUNC(int) PyUnstable_CopyPerfMapFile(const char* parent_filename) { static PyMethodDef sys_methods[] = { /* Might as well keep this in alphabetic order */ SYS_ADDAUDITHOOK_METHODDEF - {"audit", _PyCFunction_CAST(sys_audit), METH_FASTCALL, audit_doc }, + SYS_AUDIT_METHODDEF {"breakpointhook", _PyCFunction_CAST(sys_breakpointhook), METH_FASTCALL | METH_KEYWORDS, breakpointhook_doc}, SYS__CLEAR_INTERNAL_CACHES_METHODDEF From b19d12f44735583e339ee6ee560588fcf22633b4 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Fri, 8 Nov 2024 18:47:03 +0530 Subject: [PATCH 108/219] remove minor redundant code from `_asyncio` (#126578) --- Modules/_asynciomodule.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 58b57ae7a983c2..617a3dca35d9c2 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -8,7 +8,7 @@ #include "pycore_freelist.h" // _Py_FREELIST_POP() #include "pycore_modsupport.h" // _PyArg_CheckPositional() #include "pycore_moduleobject.h" // _PyModule_GetState() -#include "pycore_object.h" // _Py_SetImmortalUntracked +#include "pycore_object.h" // _Py_SetImmortalUntracked() #include "pycore_pyerrors.h" // _PyErr_ClearExcState() #include "pycore_pylifecycle.h" // _Py_IsInterpreterFinalizing() #include "pycore_pystate.h" // _PyThreadState_GET() @@ -85,8 +85,6 @@ typedef struct { # define ASYNCIO_STATE_UNLOCK(state) ((void)state) #endif -typedef struct futureiterobject futureiterobject; - /* State of the _asyncio module */ typedef struct { #ifdef Py_GIL_DISABLED From 6ec886531f14bdf90bc0c3718ac2ae8e3f8823b8 Mon Sep 17 00:00:00 2001 From: Xuanteng Huang <44627253+xuantengh@users.noreply.github.com> Date: Fri, 8 Nov 2024 23:13:18 +0800 Subject: [PATCH 109/219] gh-126072: Set docstring attribute for module and class (#126231) --- Lib/test/test_code.py | 41 +++++++++++++++++++ Lib/test/test_compile.py | 16 ++++++-- Misc/ACKS | 1 + ...-10-31-21-49-00.gh-issue-126072.o9k8Ns.rst | 2 + Python/codegen.c | 29 ++++++------- Python/symtable.c | 8 ++++ 6 files changed, 77 insertions(+), 20 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-10-31-21-49-00.gh-issue-126072.o9k8Ns.rst diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py index 93c65a82508dcb..2a1b26e8a1ffd1 100644 --- a/Lib/test/test_code.py +++ b/Lib/test/test_code.py @@ -178,6 +178,20 @@ nlocals: 3 flags: 3 consts: ("'hello'", "'world'") + +>>> class class_with_docstring: +... '''This is a docstring for class''' +... '''This line is not docstring''' +... pass + +>>> print(class_with_docstring.__doc__) +This is a docstring for class + +>>> class class_without_docstring: +... pass + +>>> print(class_without_docstring.__doc__) +None """ import copy @@ -854,6 +868,33 @@ def f(): 3 * [(42, 42, None, None)], ) + @cpython_only + def test_docstring_under_o2(self): + code = textwrap.dedent(''' + def has_docstring(x, y): + """This is a first-line doc string""" + """This is a second-line doc string""" + a = x + y + b = x - y + return a, b + + + def no_docstring(x): + def g(y): + return x + y + return g + + + async def async_func(): + """asynf function doc string""" + pass + + + for func in [has_docstring, no_docstring(4), async_func]: + assert(func.__doc__ is None) + ''') + + rc, out, err = assert_python_ok('-OO', '-c', code) if check_impl_detail(cpython=True) and ctypes is not None: py = ctypes.pythonapi diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index 519a1207afb1fc..f7ea923ef17672 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -342,6 +342,10 @@ def test_lambda_doc(self): l = lambda: "foo" self.assertIsNone(l.__doc__) + def test_lambda_consts(self): + l = lambda: "this is the only const" + self.assertEqual(l.__code__.co_consts, ("this is the only const",)) + def test_encoding(self): code = b'# -*- coding: badencoding -*-\npass\n' self.assertRaises(SyntaxError, compile, code, 'tmp', 'exec') @@ -790,10 +794,10 @@ def check_same_constant(const): # Merge constants in tuple or frozenset f1, f2 = lambda: "not a name", lambda: ("not a name",) f3 = lambda x: x in {("not a name",)} - self.assertIs(f1.__code__.co_consts[1], - f2.__code__.co_consts[1][0]) - self.assertIs(next(iter(f3.__code__.co_consts[1])), - f2.__code__.co_consts[1]) + self.assertIs(f1.__code__.co_consts[0], + f2.__code__.co_consts[0][0]) + self.assertIs(next(iter(f3.__code__.co_consts[0])), + f2.__code__.co_consts[0]) # {0} is converted to a constant frozenset({0}) by the peephole # optimizer @@ -902,6 +906,9 @@ def with_fstring(): def with_const_expression(): "also" + " not docstring" + + def multiple_const_strings(): + "not docstring " * 3 """) for opt in [0, 1, 2]: @@ -918,6 +925,7 @@ def with_const_expression(): self.assertIsNone(ns['two_strings'].__doc__) self.assertIsNone(ns['with_fstring'].__doc__) self.assertIsNone(ns['with_const_expression'].__doc__) + self.assertIsNone(ns['multiple_const_strings'].__doc__) @support.cpython_only def test_docstring_interactive_mode(self): diff --git a/Misc/ACKS b/Misc/ACKS index d03c70f6db87bf..1a25088052f4e1 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -820,6 +820,7 @@ Tomáš Hrnčiar Miro Hrončok Chiu-Hsiang Hsu Chih-Hao Huang +Xuanteng Huang Christian Hudon Benoît Hudson Lawrence Hudson diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-10-31-21-49-00.gh-issue-126072.o9k8Ns.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-10-31-21-49-00.gh-issue-126072.o9k8Ns.rst new file mode 100644 index 00000000000000..2464ac78cf429b --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-10-31-21-49-00.gh-issue-126072.o9k8Ns.rst @@ -0,0 +1,2 @@ +Following :gh:`126101`, for :ref:`codeobjects` like lambda, annotation and type alias, +we no longer add ``None`` to its :attr:`~codeobject.co_consts`. diff --git a/Python/codegen.c b/Python/codegen.c index 624d4f7ce14f21..bce3b94b27a45d 100644 --- a/Python/codegen.c +++ b/Python/codegen.c @@ -672,9 +672,7 @@ codegen_setup_annotations_scope(compiler *c, location loc, codegen_enter_scope(c, name, COMPILE_SCOPE_ANNOTATIONS, key, loc.lineno, NULL, &umd)); - // Insert None into consts to prevent an annotation - // appearing to be a docstring - _PyCompile_AddConst(c, Py_None); + assert(!SYMTABLE_ENTRY(c)->ste_has_docstring); // if .format != 1: raise NotImplementedError _Py_DECLARE_STR(format, ".format"); ADDOP_I(c, loc, LOAD_FAST, 0); @@ -770,7 +768,8 @@ _PyCodegen_Body(compiler *c, location loc, asdl_stmt_seq *stmts, bool is_interac /* If from __future__ import annotations is active, * every annotated class and module should have __annotations__. * Else __annotate__ is created when necessary. */ - if ((FUTURE_FEATURES(c) & CO_FUTURE_ANNOTATIONS) && SYMTABLE_ENTRY(c)->ste_annotations_used) { + PySTEntryObject *ste = SYMTABLE_ENTRY(c); + if ((FUTURE_FEATURES(c) & CO_FUTURE_ANNOTATIONS) && ste->ste_annotations_used) { ADDOP(c, loc, SETUP_ANNOTATIONS); } if (!asdl_seq_LEN(stmts)) { @@ -778,8 +777,9 @@ _PyCodegen_Body(compiler *c, location loc, asdl_stmt_seq *stmts, bool is_interac } Py_ssize_t first_instr = 0; if (!is_interactive) { /* A string literal on REPL prompt is not a docstring */ - PyObject *docstring = _PyAST_GetDocString(stmts); - if (docstring) { + if (ste->ste_has_docstring) { + PyObject *docstring = _PyAST_GetDocString(stmts); + assert(docstring); first_instr = 1; /* set docstring */ assert(OPTIMIZATION_LEVEL(c) < 2); @@ -1241,10 +1241,11 @@ codegen_function_body(compiler *c, stmt_ty s, int is_async, Py_ssize_t funcflags RETURN_IF_ERROR( codegen_enter_scope(c, name, scope_type, (void *)s, firstlineno, NULL, &umd)); + PySTEntryObject *ste = SYMTABLE_ENTRY(c); Py_ssize_t first_instr = 0; - PyObject *docstring = _PyAST_GetDocString(body); - assert(OPTIMIZATION_LEVEL(c) < 2 || docstring == NULL); - if (docstring) { + if (ste->ste_has_docstring) { + PyObject *docstring = _PyAST_GetDocString(body); + assert(docstring); first_instr = 1; docstring = _PyCompile_CleanDoc(docstring); if (docstring == NULL) { @@ -1258,7 +1259,6 @@ codegen_function_body(compiler *c, stmt_ty s, int is_async, Py_ssize_t funcflags NEW_JUMP_TARGET_LABEL(c, start); USE_LABEL(c, start); - PySTEntryObject *ste = SYMTABLE_ENTRY(c); bool add_stopiteration_handler = ste->ste_coroutine || ste->ste_generator; if (add_stopiteration_handler) { /* codegen_wrap_in_stopiteration_handler will push a block, so we need to account for that */ @@ -1600,9 +1600,8 @@ codegen_typealias_body(compiler *c, stmt_ty s) ADDOP_LOAD_CONST_NEW(c, loc, defaults); RETURN_IF_ERROR( codegen_setup_annotations_scope(c, LOC(s), s, name)); - /* Make None the first constant, so the evaluate function can't have a - docstring. */ - RETURN_IF_ERROR(_PyCompile_AddConst(c, Py_None)); + + assert(!SYMTABLE_ENTRY(c)->ste_has_docstring); VISIT_IN_SCOPE(c, expr, s->v.TypeAlias.value); ADDOP_IN_SCOPE(c, loc, RETURN_VALUE); PyCodeObject *co = _PyCompile_OptimizeAndAssemble(c, 0); @@ -1898,9 +1897,7 @@ codegen_lambda(compiler *c, expr_ty e) codegen_enter_scope(c, &_Py_STR(anon_lambda), COMPILE_SCOPE_LAMBDA, (void *)e, e->lineno, NULL, &umd)); - /* Make None the first constant, so the lambda can't have a - docstring. */ - RETURN_IF_ERROR(_PyCompile_AddConst(c, Py_None)); + assert(!SYMTABLE_ENTRY(c)->ste_has_docstring); VISIT_IN_SCOPE(c, expr, e->v.Lambda.body); if (SYMTABLE_ENTRY(c)->ste_generator) { diff --git a/Python/symtable.c b/Python/symtable.c index 32d715197c541b..ebddb0b93fca0a 100644 --- a/Python/symtable.c +++ b/Python/symtable.c @@ -434,6 +434,9 @@ _PySymtable_Build(mod_ty mod, PyObject *filename, _PyFutureFeatures *future) switch (mod->kind) { case Module_kind: seq = mod->v.Module.body; + if (_PyAST_GetDocString(seq)) { + st->st_cur->ste_has_docstring = 1; + } for (i = 0; i < asdl_seq_LEN(seq); i++) if (!symtable_visit_stmt(st, (stmt_ty)asdl_seq_GET(seq, i))) @@ -1909,6 +1912,11 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s) return 0; } } + + if (_PyAST_GetDocString(s->v.ClassDef.body)) { + st->st_cur->ste_has_docstring = 1; + } + VISIT_SEQ(st, stmt, s->v.ClassDef.body); if (!symtable_exit_block(st)) return 0; From 75ffac296ef24758b7e5bd9316f32a8170ade37f Mon Sep 17 00:00:00 2001 From: "RUANG (James Roy)" Date: Fri, 8 Nov 2024 23:29:16 +0800 Subject: [PATCH 110/219] gh-125298: Remove misleading text in os.kill documentation (GH-125749) Windows has not accepted process handles in many releases. --- Doc/library/os.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst index c0354b2280c45c..61144256f47ddb 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -4577,8 +4577,7 @@ written in Python, such as a mail server's external command delivery program. only be sent to console processes which share a common console window, e.g., some subprocesses. Any other value for *sig* will cause the process to be unconditionally killed by the TerminateProcess API, and the exit code - will be set to *sig*. The Windows version of :func:`kill` additionally takes - process handles to be killed. + will be set to *sig*. See also :func:`signal.pthread_kill`. From fd5580cd151e07c690e9d7594513be5fa3102a2e Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Fri, 8 Nov 2024 16:09:34 +0000 Subject: [PATCH 111/219] gh-126497: Add missing venv redirectors to freethreaded installer (GH-126556) --- .../2024-11-07-20-42-31.gh-issue-126497.EARpd-.rst | 2 ++ Tools/msi/freethreaded/freethreaded_files.wxs | 10 ++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Windows/2024-11-07-20-42-31.gh-issue-126497.EARpd-.rst diff --git a/Misc/NEWS.d/next/Windows/2024-11-07-20-42-31.gh-issue-126497.EARpd-.rst b/Misc/NEWS.d/next/Windows/2024-11-07-20-42-31.gh-issue-126497.EARpd-.rst new file mode 100644 index 00000000000000..c902b9d6da8c65 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2024-11-07-20-42-31.gh-issue-126497.EARpd-.rst @@ -0,0 +1,2 @@ +Fixes venv failure due to missing redirector executables in experimental +free-threaded installs. diff --git a/Tools/msi/freethreaded/freethreaded_files.wxs b/Tools/msi/freethreaded/freethreaded_files.wxs index 49ecb3429ad8f3..367fd978efd484 100644 --- a/Tools/msi/freethreaded/freethreaded_files.wxs +++ b/Tools/msi/freethreaded/freethreaded_files.wxs @@ -159,11 +159,13 @@ - - + + + - - + + + From fa4092259763ffad45a5bb9ef55f515dc6a69ad2 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 8 Nov 2024 16:44:44 +0000 Subject: [PATCH 112/219] GH-126547: Pre-assign version numbers for a few common classes (GH-126551) --- Include/internal/pycore_runtime_init.h | 2 +- Include/internal/pycore_typeobject.h | 15 +++++++++++++++ Objects/bytearrayobject.c | 1 + Objects/bytesobject.c | 1 + Objects/complexobject.c | 1 + Objects/dictobject.c | 1 + Objects/floatobject.c | 1 + Objects/listobject.c | 1 + Objects/longobject.c | 1 + Objects/setobject.c | 2 ++ Objects/tupleobject.c | 1 + Objects/typeobject.c | 4 +++- 12 files changed, 29 insertions(+), 2 deletions(-) diff --git a/Include/internal/pycore_runtime_init.h b/Include/internal/pycore_runtime_init.h index bd3d704cb77730..8a8f47695fb8b0 100644 --- a/Include/internal/pycore_runtime_init.h +++ b/Include/internal/pycore_runtime_init.h @@ -87,7 +87,7 @@ extern PyTypeObject _PyExc_MemoryError; .double_format = _py_float_format_unknown, \ }, \ .types = { \ - .next_version_tag = 1, \ + .next_version_tag = _Py_TYPE_VERSION_NEXT, \ }, \ .static_objects = { \ .singletons = { \ diff --git a/Include/internal/pycore_typeobject.h b/Include/internal/pycore_typeobject.h index e72592b8e98ef8..5debdd68fe94ca 100644 --- a/Include/internal/pycore_typeobject.h +++ b/Include/internal/pycore_typeobject.h @@ -14,6 +14,21 @@ extern "C" { /* state */ +#define _Py_TYPE_VERSION_INT 1 +#define _Py_TYPE_VERSION_FLOAT 2 +#define _Py_TYPE_VERSION_LIST 3 +#define _Py_TYPE_VERSION_TUPLE 4 +#define _Py_TYPE_VERSION_STR 5 +#define _Py_TYPE_VERSION_SET 6 +#define _Py_TYPE_VERSION_FROZEN_SET 7 +#define _Py_TYPE_VERSION_DICT 8 +#define _Py_TYPE_VERSION_BYTEARRAY 9 +#define _Py_TYPE_VERSION_BYTES 10 +#define _Py_TYPE_VERSION_COMPLEX 11 + +#define _Py_TYPE_VERSION_NEXT 16 + + #define _Py_TYPE_BASE_VERSION_TAG (2<<16) #define _Py_MAX_GLOBAL_TYPE_VERSION_TAG (_Py_TYPE_BASE_VERSION_TAG - 1) diff --git a/Objects/bytearrayobject.c b/Objects/bytearrayobject.c index fd2a85a3fe0a61..5a52b2f702ad0b 100644 --- a/Objects/bytearrayobject.c +++ b/Objects/bytearrayobject.c @@ -2452,6 +2452,7 @@ PyTypeObject PyByteArray_Type = { PyType_GenericAlloc, /* tp_alloc */ PyType_GenericNew, /* tp_new */ PyObject_Free, /* tp_free */ + .tp_version_tag = _Py_TYPE_VERSION_BYTEARRAY, }; /*********************** Bytearray Iterator ****************************/ diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index dcc1aba76abbed..ac02cfe7cf01c5 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -3080,6 +3080,7 @@ PyTypeObject PyBytes_Type = { bytes_alloc, /* tp_alloc */ bytes_new, /* tp_new */ PyObject_Free, /* tp_free */ + .tp_version_tag = _Py_TYPE_VERSION_BYTES, }; void diff --git a/Objects/complexobject.c b/Objects/complexobject.c index 787235c63a6be1..7b4948fc8ebe3d 100644 --- a/Objects/complexobject.c +++ b/Objects/complexobject.c @@ -1250,4 +1250,5 @@ PyTypeObject PyComplex_Type = { PyType_GenericAlloc, /* tp_alloc */ actual_complex_new, /* tp_new */ PyObject_Free, /* tp_free */ + .tp_version_tag = _Py_TYPE_VERSION_COMPLEX, }; diff --git a/Objects/dictobject.c b/Objects/dictobject.c index f28a92657834be..2090008055b7c0 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -4912,6 +4912,7 @@ PyTypeObject PyDict_Type = { dict_new, /* tp_new */ PyObject_GC_Del, /* tp_free */ .tp_vectorcall = dict_vectorcall, + .tp_version_tag = _Py_TYPE_VERSION_DICT, }; /* For backward compatibility with old dictionary interface */ diff --git a/Objects/floatobject.c b/Objects/floatobject.c index 7e14a8ad959590..f00b6a6b4b2bdc 100644 --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -1916,6 +1916,7 @@ PyTypeObject PyFloat_Type = { 0, /* tp_alloc */ float_new, /* tp_new */ .tp_vectorcall = (vectorcallfunc)float_vectorcall, + .tp_version_tag = _Py_TYPE_VERSION_FLOAT, }; static void diff --git a/Objects/listobject.c b/Objects/listobject.c index 930aefde325a7c..bb0040cbe9f272 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -3774,6 +3774,7 @@ PyTypeObject PyList_Type = { PyType_GenericNew, /* tp_new */ PyObject_GC_Del, /* tp_free */ .tp_vectorcall = list_vectorcall, + .tp_version_tag = _Py_TYPE_VERSION_LIST, }; /*********************** List Iterator **************************/ diff --git a/Objects/longobject.c b/Objects/longobject.c index 4e948940485730..b4c0f63a9843ce 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -6584,6 +6584,7 @@ PyTypeObject PyLong_Type = { long_new, /* tp_new */ PyObject_Free, /* tp_free */ .tp_vectorcall = long_vectorcall, + .tp_version_tag = _Py_TYPE_VERSION_INT, }; static PyTypeObject Int_InfoType; diff --git a/Objects/setobject.c b/Objects/setobject.c index 2671792190d7a7..955ccbebf74b54 100644 --- a/Objects/setobject.c +++ b/Objects/setobject.c @@ -2520,6 +2520,7 @@ PyTypeObject PySet_Type = { set_new, /* tp_new */ PyObject_GC_Del, /* tp_free */ .tp_vectorcall = set_vectorcall, + .tp_version_tag = _Py_TYPE_VERSION_SET, }; /* frozenset object ********************************************************/ @@ -2610,6 +2611,7 @@ PyTypeObject PyFrozenSet_Type = { frozenset_new, /* tp_new */ PyObject_GC_Del, /* tp_free */ .tp_vectorcall = frozenset_vectorcall, + .tp_version_tag = _Py_TYPE_VERSION_FROZEN_SET, }; diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c index f3132e0933ac30..193914d54bd90e 100644 --- a/Objects/tupleobject.c +++ b/Objects/tupleobject.c @@ -909,6 +909,7 @@ PyTypeObject PyTuple_Type = { tuple_new, /* tp_new */ PyObject_GC_Del, /* tp_free */ .tp_vectorcall = tuple_vectorcall, + .tp_version_tag = _Py_TYPE_VERSION_TUPLE, }; /* The following function breaks the notion that tuples are immutable: diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 88db29e14b0d44..4af7f0273aae91 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -8613,7 +8613,9 @@ init_static_type(PyInterpreterState *interp, PyTypeObject *self, self->tp_flags |= Py_TPFLAGS_IMMUTABLETYPE; assert(NEXT_GLOBAL_VERSION_TAG <= _Py_MAX_GLOBAL_TYPE_VERSION_TAG); - _PyType_SetVersion(self, NEXT_GLOBAL_VERSION_TAG++); + if (self->tp_version_tag == 0) { + _PyType_SetVersion(self, NEXT_GLOBAL_VERSION_TAG++); + } } else { assert(!initial); From 54c63a32d06cb5f07a66245c375eac7d7efb964a Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Fri, 8 Nov 2024 16:47:51 +0000 Subject: [PATCH 113/219] GH-126212: Fix removal of slashes in file URIs on Windows (#126214) Adjust `urllib.request.pathname2url()` and `url2pathname()` so that they don't remove slashes from Windows DOS drive paths and URLs. There was no basis for this behaviour, and it conflicts with how UNC and POSIX paths are handled. --- Lib/nturl2path.py | 25 +++++-------------- Lib/test/test_urllib.py | 11 ++++++-- ...-10-30-23-59-36.gh-issue-126212._9uYjT.rst | 3 +++ 3 files changed, 18 insertions(+), 21 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-10-30-23-59-36.gh-issue-126212._9uYjT.rst diff --git a/Lib/nturl2path.py b/Lib/nturl2path.py index 6453f202c26d14..2f9fec7893afd1 100644 --- a/Lib/nturl2path.py +++ b/Lib/nturl2path.py @@ -24,23 +24,15 @@ def url2pathname(url): # convert this to \\host\path\on\remote\host # (notice halving of slashes at the start of the path) url = url[2:] - components = url.split('/') # make sure not to convert quoted slashes :-) - return urllib.parse.unquote('\\'.join(components)) + return urllib.parse.unquote(url.replace('/', '\\')) comp = url.split('|') if len(comp) != 2 or comp[0][-1] not in string.ascii_letters: error = 'Bad URL: ' + url raise OSError(error) drive = comp[0][-1].upper() - components = comp[1].split('/') - path = drive + ':' - for comp in components: - if comp: - path = path + '\\' + urllib.parse.unquote(comp) - # Issue #11474 - handing url such as |c/| - if path.endswith(':') and url.endswith('/'): - path += '\\' - return path + tail = urllib.parse.unquote(comp[1].replace('/', '\\')) + return drive + ':' + tail def pathname2url(p): """OS-specific conversion from a file system path to a relative URL @@ -60,17 +52,12 @@ def pathname2url(p): raise OSError('Bad path: ' + p) if not ':' in p: # No drive specifier, just convert slashes and quote the name - components = p.split('\\') - return urllib.parse.quote('/'.join(components)) + return urllib.parse.quote(p.replace('\\', '/')) comp = p.split(':', maxsplit=2) if len(comp) != 2 or len(comp[0]) > 1: error = 'Bad path: ' + p raise OSError(error) drive = urllib.parse.quote(comp[0].upper()) - components = comp[1].split('\\') - path = '///' + drive + ':' - for comp in components: - if comp: - path = path + '/' + urllib.parse.quote(comp) - return path + tail = urllib.parse.quote(comp[1].replace('\\', '/')) + return '///' + drive + ':' + tail diff --git a/Lib/test/test_urllib.py b/Lib/test/test_urllib.py index 3ee17f96b817e1..28369b21db06d4 100644 --- a/Lib/test/test_urllib.py +++ b/Lib/test/test_urllib.py @@ -1526,8 +1526,10 @@ def test_pathname2url_win(self): self.assertEqual(fn('\\\\?\\C:\\dir'), '///C:/dir') self.assertEqual(fn('\\\\?\\unc\\server\\share\\dir'), '//server/share/dir') self.assertEqual(fn("C:"), '///C:') - self.assertEqual(fn("C:\\"), '///C:') + self.assertEqual(fn("C:\\"), '///C:/') self.assertEqual(fn('C:\\a\\b.c'), '///C:/a/b.c') + self.assertEqual(fn('C:\\a\\b.c\\'), '///C:/a/b.c/') + self.assertEqual(fn('C:\\a\\\\b.c'), '///C:/a//b.c') self.assertEqual(fn('C:\\a\\b%#c'), '///C:/a/b%25%23c') self.assertEqual(fn('C:\\a\\b\xe9'), '///C:/a/b%C3%A9') self.assertEqual(fn('C:\\foo\\bar\\spam.foo'), "///C:/foo/bar/spam.foo") @@ -1563,13 +1565,15 @@ def test_url2pathname_win(self): self.assertEqual(fn("///C|"), 'C:') self.assertEqual(fn("///C:"), 'C:') self.assertEqual(fn('///C:/'), 'C:\\') - self.assertEqual(fn('/C|//'), 'C:\\') + self.assertEqual(fn('/C|//'), 'C:\\\\') self.assertEqual(fn('///C|/path'), 'C:\\path') # No DOS drive self.assertEqual(fn("///C/test/"), '\\\\\\C\\test\\') self.assertEqual(fn("////C/test/"), '\\\\C\\test\\') # DOS drive paths self.assertEqual(fn('C:/path/to/file'), 'C:\\path\\to\\file') + self.assertEqual(fn('C:/path/to/file/'), 'C:\\path\\to\\file\\') + self.assertEqual(fn('C:/path/to//file'), 'C:\\path\\to\\\\file') self.assertEqual(fn('C|/path/to/file'), 'C:\\path\\to\\file') self.assertEqual(fn('/C|/path/to/file'), 'C:\\path\\to\\file') self.assertEqual(fn('///C|/path/to/file'), 'C:\\path\\to\\file') @@ -1583,6 +1587,9 @@ def test_url2pathname_win(self): # Localhost paths self.assertEqual(fn('//localhost/C:/path/to/file'), 'C:\\path\\to\\file') self.assertEqual(fn('//localhost/C|/path/to/file'), 'C:\\path\\to\\file') + # Percent-encoded forward slashes are preserved for backwards compatibility + self.assertEqual(fn('C:/foo%2fbar'), 'C:\\foo/bar') + self.assertEqual(fn('//server/share/foo%2fbar'), '\\\\server\\share\\foo/bar') # Round-tripping paths = ['C:', r'\\\C\test\\', diff --git a/Misc/NEWS.d/next/Library/2024-10-30-23-59-36.gh-issue-126212._9uYjT.rst b/Misc/NEWS.d/next/Library/2024-10-30-23-59-36.gh-issue-126212._9uYjT.rst new file mode 100644 index 00000000000000..047fe0f68048b5 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-10-30-23-59-36.gh-issue-126212._9uYjT.rst @@ -0,0 +1,3 @@ +Fix issue where :func:`urllib.request.pathname2url` and +:func:`~urllib.request.url2pathname` removed slashes from Windows DOS drive +paths and URLs. From f8276bf5f37ef12aa0033634151fa33a6f7bd4f2 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Sat, 9 Nov 2024 03:12:55 +0100 Subject: [PATCH 114/219] gh-126187 Add emscripten.py script to automate emscripten build (#126190) Add emscripten.py script to automate emscripten build. This is modeled heavily on `Tools/wasm/wasi.py`. This will form the basis of an Emscripten build bot. --- ...-10-30-17-47-15.gh-issue-126187.0jFCZB.rst | 1 + Tools/wasm/README.md | 164 ++------- Tools/wasm/emscripten/__main__.py | 325 ++++++++++++++++++ 3 files changed, 354 insertions(+), 136 deletions(-) create mode 100644 Misc/NEWS.d/next/Build/2024-10-30-17-47-15.gh-issue-126187.0jFCZB.rst create mode 100644 Tools/wasm/emscripten/__main__.py diff --git a/Misc/NEWS.d/next/Build/2024-10-30-17-47-15.gh-issue-126187.0jFCZB.rst b/Misc/NEWS.d/next/Build/2024-10-30-17-47-15.gh-issue-126187.0jFCZB.rst new file mode 100644 index 00000000000000..c295a91c2225a3 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2024-10-30-17-47-15.gh-issue-126187.0jFCZB.rst @@ -0,0 +1 @@ +Introduced ``Tools/wasm/emscripten.py`` to simplify doing Emscripten builds. diff --git a/Tools/wasm/README.md b/Tools/wasm/README.md index bc3e4ba8bd5b76..4c9a643b0d9d74 100644 --- a/Tools/wasm/README.md +++ b/Tools/wasm/README.md @@ -1,7 +1,7 @@ # Python WebAssembly (WASM) build **WASI support is [tier 2](https://peps.python.org/pep-0011/#tier-2).** -**Emscripten is NOT officially supported as of Python 3.13.** +**Emscripten support is [tier 3](https://peps.python.org/pep-0011/#tier-3).** This directory contains configuration and helpers to facilitate cross compilation of CPython to WebAssembly (WASM). Python supports Emscripten @@ -27,154 +27,57 @@ It comes with a reduced and preloaded stdlib without tests and threading support. The ``Emscripten/node`` target has threading enabled and can access the file system directly. -Cross compiling to the wasm32-emscripten platform needs the -[Emscripten](https://emscripten.org/) SDK and a build Python interpreter. -Emscripten 3.1.19 or newer are recommended. All commands below are relative -to a repository checkout. +To cross compile to the ``wasm32-emscripten`` platform you need +[the Emscripten compiler toolchain](https://emscripten.org/), +a Python interpreter, and an installation of Node version 18 or newer. Emscripten +version 3.1.42 or newer is recommended. All commands below are relative to a checkout +of the Python repository. -#### Toolchain +#### Install [the Emscripten compiler toolchain](https://emscripten.org/docs/getting_started/downloads.html) -##### Container image - -Christian Heimes maintains a container image with Emscripten SDK, Python -build dependencies, WASI-SDK, wasmtime, and several additional tools. - -From within your local CPython repo clone, run one of the following commands: - -``` -# Fedora, RHEL, CentOS -podman run --rm -ti -v $(pwd):/python-wasm/cpython:Z -w /python-wasm/cpython quay.io/tiran/cpythonbuild:emsdk3 - -# other -docker run --rm -ti -v $(pwd):/python-wasm/cpython -w /python-wasm/cpython quay.io/tiran/cpythonbuild:emsdk3 +You can install the Emscripten toolchain as follows: +```shell +git clone https://github.com/emscripten-core/emsdk.git --depth 1 +./emsdk/emsdk install latest +./emsdk/emsdk activate latest ``` - -##### Manually - -###### Install [Emscripten SDK](https://emscripten.org/docs/getting_started/downloads.html) - -**NOTE**: Follow the on-screen instructions how to add the SDK to ``PATH``. - +To add the Emscripten compiler to your path: ```shell -git clone https://github.com/emscripten-core/emsdk.git /opt/emsdk -/opt/emsdk/emsdk install latest -/opt/emsdk/emsdk activate latest +source ./emsdk/emsdk_env.sh ``` +This adds `emcc` and `emconfigure` to your path. -###### Optionally: enable ccache for EMSDK +##### Optionally: enable ccache for EMSDK The ``EM_COMPILER_WRAPPER`` must be set after the EMSDK environment is sourced. Otherwise the source script removes the environment variable. -``` -. /opt/emsdk/emsdk_env.sh -EM_COMPILER_WRAPPER=ccache -``` - -###### Optionally: pre-build and cache static libraries - -Emscripten SDK provides static builds of core libraries without PIC -(position-independent code). Python builds with ``dlopen`` support require -PIC. To populate the build cache, run: - ```shell -. /opt/emsdk/emsdk_env.sh -embuilder build zlib bzip2 MINIMAL_PIC -embuilder --pic build zlib bzip2 MINIMAL_PIC +export EM_COMPILER_WRAPPER=ccache ``` - ### Compile and build Python interpreter -From within the container, run the following command: - -```shell -./Tools/wasm/wasm_build.py build -``` - -The command is roughly equivalent to: - -```shell -mkdir -p builddir/build -pushd builddir/build -../../configure -C -make -j$(nproc) -popd -``` - -#### Cross-compile to wasm32-emscripten for browser - -```shell -./Tools/wasm/wasm_build.py emscripten-browser -``` - -The command is roughly equivalent to: - +You can use `python Tools/wasm/emscripten` to compile and build targetting +Emscripten. You can do everything at once with: ```shell -mkdir -p builddir/emscripten-browser -pushd builddir/emscripten-browser - -CONFIG_SITE=../../Tools/wasm/config.site-wasm32-emscripten \ - emconfigure ../../configure -C \ - --host=wasm32-unknown-emscripten \ - --build=$(../../config.guess) \ - --with-emscripten-target=browser \ - --with-build-python=$(pwd)/../build/python - -emmake make -j$(nproc) -popd +python Tools/wasm/emscripten build ``` - -Serve `python.html` with a local webserver and open the file in a browser. -Python comes with a minimal web server script that sets necessary HTTP -headers like COOP, COEP, and mimetypes. Run the script outside the container -and from the root of the CPython checkout. - +or you can break it out into four separate steps: ```shell -./Tools/wasm/wasm_webserver.py +python Tools/wasm/emscripten configure-build-python +python Tools/wasm/emscripten make-build-python +python Tools/wasm/emscripten configure-host +python Tools/wasm/emscripten make-host ``` - -and open http://localhost:8000/builddir/emscripten-browser/python.html . This -directory structure enables the *C/C++ DevTools Support (DWARF)* to load C -and header files with debug builds. - - -#### Cross compile to wasm32-emscripten for node - +Extra arguments to the configure steps are passed along to configure. For +instance, to do a debug build, you can use: ```shell -./Tools/wasm/wasm_build.py emscripten-node-dl +python Tools/wasm/emscripten build --with-py-debug ``` -The command is roughly equivalent to: - -```shell -mkdir -p builddir/emscripten-node-dl -pushd builddir/emscripten-node-dl - -CONFIG_SITE=../../Tools/wasm/config.site-wasm32-emscripten \ - emconfigure ../../configure -C \ - --host=wasm32-unknown-emscripten \ - --build=$(../../config.guess) \ - --with-emscripten-target=node \ - --enable-wasm-dynamic-linking \ - --with-build-python=$(pwd)/../build/python - -emmake make -j$(nproc) -popd -``` - -```shell -node --experimental-wasm-threads --experimental-wasm-bulk-memory --experimental-wasm-bigint builddir/emscripten-node-dl/python.js -``` - -(``--experimental-wasm-bigint`` is not needed with recent NodeJS versions) - ### Limitations and issues -Emscripten before 3.1.8 has known bugs that can cause memory corruption and -resource leaks. 3.1.8 contains several fixes for bugs in date and time -functions. - #### Network stack - Python's socket module does not work with Emscripten's emulated POSIX @@ -241,8 +144,6 @@ functions. [gh-90548](https://github.com/python/cpython/issues/90548). - Python's object allocator ``obmalloc`` is disabled by default. - ``ensurepip`` is not available. -- Some ``ctypes`` features like ``c_longlong`` and ``c_longdouble`` may need - NodeJS option ``--experimental-wasm-bigint``. #### In the browser @@ -263,15 +164,6 @@ Node builds use ``NODERAWFS``. - Node RawFS allows direct access to the host file system without need to perform ``FS.mount()`` call. -### wasm64-emscripten - -- wasm64 requires recent NodeJS and ``--experimental-wasm-memory64``. -- ``EM_JS`` functions must return ``BigInt()``. -- ``Py_BuildValue()`` format strings must match size of types. Confusing 32 - and 64 bits types leads to memory corruption, see - [gh-95876](https://github.com/python/cpython/issues/95876) and - [gh-95878](https://github.com/python/cpython/issues/95878). - ### Hosting Python WASM builds The simple REPL terminal uses SharedArrayBuffer. For security reasons diff --git a/Tools/wasm/emscripten/__main__.py b/Tools/wasm/emscripten/__main__.py new file mode 100644 index 00000000000000..2015a3764ea8c8 --- /dev/null +++ b/Tools/wasm/emscripten/__main__.py @@ -0,0 +1,325 @@ +#!/usr/bin/env python3 + +import argparse +import contextlib +import functools +import os + +try: + from os import process_cpu_count as cpu_count +except ImportError: + from os import cpu_count +from pathlib import Path +import shutil +import subprocess +import sys +import sysconfig +import tempfile + +WASM_DIR = Path(__file__).parent.parent +CHECKOUT = WASM_DIR.parent.parent + +CROSS_BUILD_DIR = CHECKOUT / "cross-build" +BUILD_DIR = CROSS_BUILD_DIR / "build" +HOST_TRIPLE = "wasm32-emscripten" +HOST_DIR = CROSS_BUILD_DIR / HOST_TRIPLE + +LOCAL_SETUP = CHECKOUT / "Modules" / "Setup.local" +LOCAL_SETUP_MARKER = "# Generated by Tools/wasm/emscripten.py\n".encode("utf-8") + + +def updated_env(updates={}): + """Create a new dict representing the environment to use. + + The changes made to the execution environment are printed out. + """ + env_defaults = {} + # https://reproducible-builds.org/docs/source-date-epoch/ + git_epoch_cmd = ["git", "log", "-1", "--pretty=%ct"] + try: + epoch = subprocess.check_output(git_epoch_cmd, encoding="utf-8").strip() + env_defaults["SOURCE_DATE_EPOCH"] = epoch + except subprocess.CalledProcessError: + pass # Might be building from a tarball. + # This layering lets SOURCE_DATE_EPOCH from os.environ takes precedence. + environment = env_defaults | os.environ | updates + + env_diff = {} + for key, value in environment.items(): + if os.environ.get(key) != value: + env_diff[key] = value + + print("🌎 Environment changes:") + for key in sorted(env_diff.keys()): + print(f" {key}={env_diff[key]}") + + return environment + + +def subdir(working_dir, *, clean_ok=False): + """Decorator to change to a working directory.""" + + def decorator(func): + @functools.wraps(func) + def wrapper(context): + try: + tput_output = subprocess.check_output( + ["tput", "cols"], encoding="utf-8" + ) + terminal_width = int(tput_output.strip()) + except subprocess.CalledProcessError: + terminal_width = 80 + print("⎯" * terminal_width) + print("📁", working_dir) + if clean_ok and getattr(context, "clean", False) and working_dir.exists(): + print(f"🚮 Deleting directory (--clean)...") + shutil.rmtree(working_dir) + + working_dir.mkdir(parents=True, exist_ok=True) + + with contextlib.chdir(working_dir): + return func(context, working_dir) + + return wrapper + + return decorator + + +def call(command, *, quiet, **kwargs): + """Execute a command. + + If 'quiet' is true, then redirect stdout and stderr to a temporary file. + """ + print("❯", " ".join(map(str, command))) + if not quiet: + stdout = None + stderr = None + else: + stdout = tempfile.NamedTemporaryFile( + "w", + encoding="utf-8", + delete=False, + prefix="cpython-emscripten-", + suffix=".log", + ) + stderr = subprocess.STDOUT + print(f"📝 Logging output to {stdout.name} (--quiet)...") + + subprocess.check_call(command, **kwargs, stdout=stdout, stderr=stderr) + + +def build_platform(): + """The name of the build/host platform.""" + # Can also be found via `config.guess`.` + return sysconfig.get_config_var("BUILD_GNU_TYPE") + + +def build_python_path(): + """The path to the build Python binary.""" + binary = BUILD_DIR / "python" + if not binary.is_file(): + binary = binary.with_suffix(".exe") + if not binary.is_file(): + raise FileNotFoundError("Unable to find `python(.exe)` in " f"{BUILD_DIR}") + + return binary + + +@subdir(BUILD_DIR, clean_ok=True) +def configure_build_python(context, working_dir): + """Configure the build/host Python.""" + if LOCAL_SETUP.exists(): + print(f"👍 {LOCAL_SETUP} exists ...") + else: + print(f"📝 Touching {LOCAL_SETUP} ...") + LOCAL_SETUP.write_bytes(LOCAL_SETUP_MARKER) + + configure = [os.path.relpath(CHECKOUT / "configure", working_dir)] + if context.args: + configure.extend(context.args) + + call(configure, quiet=context.quiet) + + +@subdir(BUILD_DIR) +def make_build_python(context, working_dir): + """Make/build the build Python.""" + call(["make", "--jobs", str(cpu_count()), "all"], quiet=context.quiet) + + binary = build_python_path() + cmd = [ + binary, + "-c", + "import sys; " "print(f'{sys.version_info.major}.{sys.version_info.minor}')", + ] + version = subprocess.check_output(cmd, encoding="utf-8").strip() + + print(f"🎉 {binary} {version}") + + +@subdir(HOST_DIR, clean_ok=True) +def configure_emscripten_python(context, working_dir): + """Configure the emscripten/host build.""" + config_site = os.fsdecode( + CHECKOUT / "Tools" / "wasm" / "config.site-wasm32-emscripten" + ) + + emscripten_build_dir = working_dir.relative_to(CHECKOUT) + + python_build_dir = BUILD_DIR / "build" + lib_dirs = list(python_build_dir.glob("lib.*")) + assert ( + len(lib_dirs) == 1 + ), f"Expected a single lib.* directory in {python_build_dir}" + lib_dir = os.fsdecode(lib_dirs[0]) + pydebug = lib_dir.endswith("-pydebug") + python_version = lib_dir.removesuffix("-pydebug").rpartition("-")[-1] + sysconfig_data = ( + f"{emscripten_build_dir}/build/lib.emscripten-wasm32-{python_version}" + ) + if pydebug: + sysconfig_data += "-pydebug" + + host_runner = context.host_runner + env_additions = {"CONFIG_SITE": config_site, "HOSTRUNNER": host_runner} + build_python = os.fsdecode(build_python_path()) + configure = [ + "emconfigure", + os.path.relpath(CHECKOUT / "configure", working_dir), + "CFLAGS=-DPY_CALL_TRAMPOLINE -sUSE_BZIP2", + f"--host={HOST_TRIPLE}", + f"--build={build_platform()}", + f"--with-build-python={build_python}", + "--without-pymalloc", + "--disable-shared", + "--disable-ipv6", + "--enable-big-digits=30", + "--enable-wasm-dynamic-linking", + f"--prefix={HOST_DIR}", + ] + if pydebug: + configure.append("--with-pydebug") + if context.args: + configure.extend(context.args) + call( + configure, + env=updated_env(env_additions), + quiet=context.quiet, + ) + + python_js = working_dir / "python.js" + exec_script = working_dir / "python.sh" + exec_script.write_text(f'#!/bin/sh\nexec {host_runner} {python_js} "$@"\n') + exec_script.chmod(0o755) + print(f"🏃‍♀️ Created {exec_script} ... ") + sys.stdout.flush() + + +@subdir(HOST_DIR) +def make_emscripten_python(context, working_dir): + """Run `make` for the emscripten/host build.""" + call( + ["make", "--jobs", str(cpu_count()), "commoninstall"], + env=updated_env(), + quiet=context.quiet, + ) + + exec_script = working_dir / "python.sh" + subprocess.check_call([exec_script, "--version"]) + + +def build_all(context): + """Build everything.""" + steps = [ + configure_build_python, + make_build_python, + configure_emscripten_python, + make_emscripten_python, + ] + for step in steps: + step(context) + + +def clean_contents(context): + """Delete all files created by this script.""" + if CROSS_BUILD_DIR.exists(): + print(f"🧹 Deleting {CROSS_BUILD_DIR} ...") + shutil.rmtree(CROSS_BUILD_DIR) + + if LOCAL_SETUP.exists(): + with LOCAL_SETUP.open("rb") as file: + if file.read(len(LOCAL_SETUP_MARKER)) == LOCAL_SETUP_MARKER: + print(f"🧹 Deleting generated {LOCAL_SETUP} ...") + + +def main(): + default_host_runner = "node" + + parser = argparse.ArgumentParser() + subcommands = parser.add_subparsers(dest="subcommand") + build = subcommands.add_parser("build", help="Build everything") + configure_build = subcommands.add_parser( + "configure-build-python", help="Run `configure` for the " "build Python" + ) + make_build = subcommands.add_parser( + "make-build-python", help="Run `make` for the build Python" + ) + configure_host = subcommands.add_parser( + "configure-host", + help="Run `configure` for the host/emscripten (pydebug builds are inferred from the build Python)", + ) + make_host = subcommands.add_parser("make-host", help="Run `make` for the host/emscripten") + clean = subcommands.add_parser( + "clean", help="Delete files and directories created by this script" + ) + for subcommand in build, configure_build, make_build, configure_host, make_host: + subcommand.add_argument( + "--quiet", + action="store_true", + default=False, + dest="quiet", + help="Redirect output from subprocesses to a log file", + ) + for subcommand in configure_build, configure_host: + subcommand.add_argument( + "--clean", + action="store_true", + default=False, + dest="clean", + help="Delete any relevant directories before building", + ) + for subcommand in build, configure_build, configure_host: + subcommand.add_argument( + "args", nargs="*", help="Extra arguments to pass to `configure`" + ) + for subcommand in build, configure_host: + subcommand.add_argument( + "--host-runner", + action="store", + default=default_host_runner, + dest="host_runner", + help="Command template for running the emscripten host" + f"`{default_host_runner}`)", + ) + + context = parser.parse_args() + + dispatch = { + "configure-build-python": configure_build_python, + "make-build-python": make_build_python, + "configure-host": configure_emscripten_python, + "make-host": make_emscripten_python, + "build": build_all, + "clean": clean_contents, + } + + if not context.subcommand: + # No command provided, display help and exit + print("Expected one of", ", ".join(sorted(dispatch.keys())), file=sys.stderr) + parser.print_help(sys.stderr) + sys.exit(1) + dispatch[context.subcommand](context) + + +if __name__ == "__main__": + main() From 6293d00e7201f3f28b1f4512e57dc4f03855cabd Mon Sep 17 00:00:00 2001 From: Ken Jin Date: Sat, 9 Nov 2024 11:35:33 +0800 Subject: [PATCH 115/219] gh-120619: Strength reduce function guards, support 2-operand uop forms (GH-124846) Co-authored-by: Brandt Bucher --- Include/internal/pycore_optimizer.h | 3 +- Include/internal/pycore_uop_ids.h | 303 ++++++++++--------- Include/internal/pycore_uop_metadata.h | 4 + Lib/test/test_capi/test_opt.py | 24 ++ Python/bytecodes.c | 8 +- Python/ceval_macros.h | 3 +- Python/executor_cases.c.h | 108 ++++--- Python/generated_cases.c.h | 2 +- Python/optimizer.c | 22 +- Python/optimizer_analysis.c | 28 +- Python/optimizer_bytecodes.c | 23 +- Python/optimizer_cases.c.h | 69 +++-- Tools/cases_generator/analyzer.py | 2 +- Tools/cases_generator/optimizer_generator.py | 2 +- Tools/cases_generator/tier2_generator.py | 4 +- Tools/jit/_stencils.py | 24 +- Tools/jit/template.c | 20 +- 17 files changed, 379 insertions(+), 270 deletions(-) diff --git a/Include/internal/pycore_optimizer.h b/Include/internal/pycore_optimizer.h index f92c0a0cddf906..6d70b42f708854 100644 --- a/Include/internal/pycore_optimizer.h +++ b/Include/internal/pycore_optimizer.h @@ -58,7 +58,8 @@ typedef struct { uint16_t error_target; }; }; - uint64_t operand; // A cache entry + uint64_t operand0; // A cache entry + uint64_t operand1; } _PyUOpInstruction; typedef struct { diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h index 55416d2aae1e1a..fab4ce6a25b347 100644 --- a/Include/internal/pycore_uop_ids.h +++ b/Include/internal/pycore_uop_ids.h @@ -63,90 +63,91 @@ extern "C" { #define _CHECK_FUNCTION 333 #define _CHECK_FUNCTION_EXACT_ARGS 334 #define _CHECK_FUNCTION_VERSION 335 -#define _CHECK_FUNCTION_VERSION_KW 336 -#define _CHECK_IS_NOT_PY_CALLABLE 337 -#define _CHECK_IS_NOT_PY_CALLABLE_KW 338 -#define _CHECK_MANAGED_OBJECT_HAS_VALUES 339 -#define _CHECK_METHOD_VERSION 340 -#define _CHECK_METHOD_VERSION_KW 341 -#define _CHECK_PEP_523 342 -#define _CHECK_PERIODIC 343 -#define _CHECK_PERIODIC_IF_NOT_YIELD_FROM 344 -#define _CHECK_STACK_SPACE 345 -#define _CHECK_STACK_SPACE_OPERAND 346 -#define _CHECK_VALIDITY 347 -#define _CHECK_VALIDITY_AND_SET_IP 348 -#define _COMPARE_OP 349 -#define _COMPARE_OP_FLOAT 350 -#define _COMPARE_OP_INT 351 -#define _COMPARE_OP_STR 352 -#define _CONTAINS_OP 353 +#define _CHECK_FUNCTION_VERSION_INLINE 336 +#define _CHECK_FUNCTION_VERSION_KW 337 +#define _CHECK_IS_NOT_PY_CALLABLE 338 +#define _CHECK_IS_NOT_PY_CALLABLE_KW 339 +#define _CHECK_MANAGED_OBJECT_HAS_VALUES 340 +#define _CHECK_METHOD_VERSION 341 +#define _CHECK_METHOD_VERSION_KW 342 +#define _CHECK_PEP_523 343 +#define _CHECK_PERIODIC 344 +#define _CHECK_PERIODIC_IF_NOT_YIELD_FROM 345 +#define _CHECK_STACK_SPACE 346 +#define _CHECK_STACK_SPACE_OPERAND 347 +#define _CHECK_VALIDITY 348 +#define _CHECK_VALIDITY_AND_SET_IP 349 +#define _COMPARE_OP 350 +#define _COMPARE_OP_FLOAT 351 +#define _COMPARE_OP_INT 352 +#define _COMPARE_OP_STR 353 +#define _CONTAINS_OP 354 #define _CONTAINS_OP_DICT CONTAINS_OP_DICT #define _CONTAINS_OP_SET CONTAINS_OP_SET #define _CONVERT_VALUE CONVERT_VALUE #define _COPY COPY #define _COPY_FREE_VARS COPY_FREE_VARS -#define _CREATE_INIT_FRAME 354 +#define _CREATE_INIT_FRAME 355 #define _DELETE_ATTR DELETE_ATTR #define _DELETE_DEREF DELETE_DEREF #define _DELETE_FAST DELETE_FAST #define _DELETE_GLOBAL DELETE_GLOBAL #define _DELETE_NAME DELETE_NAME #define _DELETE_SUBSCR DELETE_SUBSCR -#define _DEOPT 355 +#define _DEOPT 356 #define _DICT_MERGE DICT_MERGE #define _DICT_UPDATE DICT_UPDATE -#define _DO_CALL 356 -#define _DO_CALL_FUNCTION_EX 357 -#define _DO_CALL_KW 358 -#define _DYNAMIC_EXIT 359 +#define _DO_CALL 357 +#define _DO_CALL_FUNCTION_EX 358 +#define _DO_CALL_KW 359 +#define _DYNAMIC_EXIT 360 #define _END_SEND END_SEND -#define _ERROR_POP_N 360 +#define _ERROR_POP_N 361 #define _EXIT_INIT_CHECK EXIT_INIT_CHECK -#define _EXPAND_METHOD 361 -#define _EXPAND_METHOD_KW 362 -#define _FATAL_ERROR 363 +#define _EXPAND_METHOD 362 +#define _EXPAND_METHOD_KW 363 +#define _FATAL_ERROR 364 #define _FORMAT_SIMPLE FORMAT_SIMPLE #define _FORMAT_WITH_SPEC FORMAT_WITH_SPEC -#define _FOR_ITER 364 -#define _FOR_ITER_GEN_FRAME 365 -#define _FOR_ITER_TIER_TWO 366 +#define _FOR_ITER 365 +#define _FOR_ITER_GEN_FRAME 366 +#define _FOR_ITER_TIER_TWO 367 #define _GET_AITER GET_AITER #define _GET_ANEXT GET_ANEXT #define _GET_AWAITABLE GET_AWAITABLE #define _GET_ITER GET_ITER #define _GET_LEN GET_LEN #define _GET_YIELD_FROM_ITER GET_YIELD_FROM_ITER -#define _GUARD_BOTH_FLOAT 367 -#define _GUARD_BOTH_INT 368 -#define _GUARD_BOTH_UNICODE 369 -#define _GUARD_BUILTINS_VERSION_PUSH_KEYS 370 -#define _GUARD_DORV_NO_DICT 371 -#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT 372 -#define _GUARD_GLOBALS_VERSION 373 -#define _GUARD_GLOBALS_VERSION_PUSH_KEYS 374 -#define _GUARD_IS_FALSE_POP 375 -#define _GUARD_IS_NONE_POP 376 -#define _GUARD_IS_NOT_NONE_POP 377 -#define _GUARD_IS_TRUE_POP 378 -#define _GUARD_KEYS_VERSION 379 -#define _GUARD_NOS_FLOAT 380 -#define _GUARD_NOS_INT 381 -#define _GUARD_NOT_EXHAUSTED_LIST 382 -#define _GUARD_NOT_EXHAUSTED_RANGE 383 -#define _GUARD_NOT_EXHAUSTED_TUPLE 384 -#define _GUARD_TOS_FLOAT 385 -#define _GUARD_TOS_INT 386 -#define _GUARD_TYPE_VERSION 387 +#define _GUARD_BOTH_FLOAT 368 +#define _GUARD_BOTH_INT 369 +#define _GUARD_BOTH_UNICODE 370 +#define _GUARD_BUILTINS_VERSION_PUSH_KEYS 371 +#define _GUARD_DORV_NO_DICT 372 +#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT 373 +#define _GUARD_GLOBALS_VERSION 374 +#define _GUARD_GLOBALS_VERSION_PUSH_KEYS 375 +#define _GUARD_IS_FALSE_POP 376 +#define _GUARD_IS_NONE_POP 377 +#define _GUARD_IS_NOT_NONE_POP 378 +#define _GUARD_IS_TRUE_POP 379 +#define _GUARD_KEYS_VERSION 380 +#define _GUARD_NOS_FLOAT 381 +#define _GUARD_NOS_INT 382 +#define _GUARD_NOT_EXHAUSTED_LIST 383 +#define _GUARD_NOT_EXHAUSTED_RANGE 384 +#define _GUARD_NOT_EXHAUSTED_TUPLE 385 +#define _GUARD_TOS_FLOAT 386 +#define _GUARD_TOS_INT 387 +#define _GUARD_TYPE_VERSION 388 #define _IMPORT_FROM IMPORT_FROM #define _IMPORT_NAME IMPORT_NAME -#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 388 -#define _INIT_CALL_PY_EXACT_ARGS 389 -#define _INIT_CALL_PY_EXACT_ARGS_0 390 -#define _INIT_CALL_PY_EXACT_ARGS_1 391 -#define _INIT_CALL_PY_EXACT_ARGS_2 392 -#define _INIT_CALL_PY_EXACT_ARGS_3 393 -#define _INIT_CALL_PY_EXACT_ARGS_4 394 +#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 389 +#define _INIT_CALL_PY_EXACT_ARGS 390 +#define _INIT_CALL_PY_EXACT_ARGS_0 391 +#define _INIT_CALL_PY_EXACT_ARGS_1 392 +#define _INIT_CALL_PY_EXACT_ARGS_2 393 +#define _INIT_CALL_PY_EXACT_ARGS_3 394 +#define _INIT_CALL_PY_EXACT_ARGS_4 395 #define _INSTRUMENTED_CALL_FUNCTION_EX INSTRUMENTED_CALL_FUNCTION_EX #define _INSTRUMENTED_CALL_KW INSTRUMENTED_CALL_KW #define _INSTRUMENTED_FOR_ITER INSTRUMENTED_FOR_ITER @@ -158,142 +159,142 @@ extern "C" { #define _INSTRUMENTED_POP_JUMP_IF_NONE INSTRUMENTED_POP_JUMP_IF_NONE #define _INSTRUMENTED_POP_JUMP_IF_NOT_NONE INSTRUMENTED_POP_JUMP_IF_NOT_NONE #define _INSTRUMENTED_POP_JUMP_IF_TRUE INSTRUMENTED_POP_JUMP_IF_TRUE -#define _INTERNAL_INCREMENT_OPT_COUNTER 395 -#define _IS_NONE 396 +#define _INTERNAL_INCREMENT_OPT_COUNTER 396 +#define _IS_NONE 397 #define _IS_OP IS_OP -#define _ITER_CHECK_LIST 397 -#define _ITER_CHECK_RANGE 398 -#define _ITER_CHECK_TUPLE 399 -#define _ITER_JUMP_LIST 400 -#define _ITER_JUMP_RANGE 401 -#define _ITER_JUMP_TUPLE 402 -#define _ITER_NEXT_LIST 403 -#define _ITER_NEXT_RANGE 404 -#define _ITER_NEXT_TUPLE 405 -#define _JUMP_TO_TOP 406 +#define _ITER_CHECK_LIST 398 +#define _ITER_CHECK_RANGE 399 +#define _ITER_CHECK_TUPLE 400 +#define _ITER_JUMP_LIST 401 +#define _ITER_JUMP_RANGE 402 +#define _ITER_JUMP_TUPLE 403 +#define _ITER_NEXT_LIST 404 +#define _ITER_NEXT_RANGE 405 +#define _ITER_NEXT_TUPLE 406 +#define _JUMP_TO_TOP 407 #define _LIST_APPEND LIST_APPEND #define _LIST_EXTEND LIST_EXTEND -#define _LOAD_ATTR 407 -#define _LOAD_ATTR_CLASS 408 -#define _LOAD_ATTR_CLASS_0 409 -#define _LOAD_ATTR_CLASS_1 410 +#define _LOAD_ATTR 408 +#define _LOAD_ATTR_CLASS 409 +#define _LOAD_ATTR_CLASS_0 410 +#define _LOAD_ATTR_CLASS_1 411 #define _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN -#define _LOAD_ATTR_INSTANCE_VALUE 411 -#define _LOAD_ATTR_INSTANCE_VALUE_0 412 -#define _LOAD_ATTR_INSTANCE_VALUE_1 413 -#define _LOAD_ATTR_METHOD_LAZY_DICT 414 -#define _LOAD_ATTR_METHOD_NO_DICT 415 -#define _LOAD_ATTR_METHOD_WITH_VALUES 416 -#define _LOAD_ATTR_MODULE 417 -#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT 418 -#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 419 -#define _LOAD_ATTR_PROPERTY_FRAME 420 -#define _LOAD_ATTR_SLOT 421 -#define _LOAD_ATTR_SLOT_0 422 -#define _LOAD_ATTR_SLOT_1 423 -#define _LOAD_ATTR_WITH_HINT 424 +#define _LOAD_ATTR_INSTANCE_VALUE 412 +#define _LOAD_ATTR_INSTANCE_VALUE_0 413 +#define _LOAD_ATTR_INSTANCE_VALUE_1 414 +#define _LOAD_ATTR_METHOD_LAZY_DICT 415 +#define _LOAD_ATTR_METHOD_NO_DICT 416 +#define _LOAD_ATTR_METHOD_WITH_VALUES 417 +#define _LOAD_ATTR_MODULE 418 +#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT 419 +#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 420 +#define _LOAD_ATTR_PROPERTY_FRAME 421 +#define _LOAD_ATTR_SLOT 422 +#define _LOAD_ATTR_SLOT_0 423 +#define _LOAD_ATTR_SLOT_1 424 +#define _LOAD_ATTR_WITH_HINT 425 #define _LOAD_BUILD_CLASS LOAD_BUILD_CLASS -#define _LOAD_BYTECODE 425 +#define _LOAD_BYTECODE 426 #define _LOAD_COMMON_CONSTANT LOAD_COMMON_CONSTANT #define _LOAD_CONST LOAD_CONST #define _LOAD_CONST_IMMORTAL LOAD_CONST_IMMORTAL -#define _LOAD_CONST_INLINE 426 -#define _LOAD_CONST_INLINE_BORROW 427 -#define _LOAD_CONST_INLINE_BORROW_WITH_NULL 428 -#define _LOAD_CONST_INLINE_WITH_NULL 429 +#define _LOAD_CONST_INLINE 427 +#define _LOAD_CONST_INLINE_BORROW 428 +#define _LOAD_CONST_INLINE_BORROW_WITH_NULL 429 +#define _LOAD_CONST_INLINE_WITH_NULL 430 #define _LOAD_DEREF LOAD_DEREF -#define _LOAD_FAST 430 -#define _LOAD_FAST_0 431 -#define _LOAD_FAST_1 432 -#define _LOAD_FAST_2 433 -#define _LOAD_FAST_3 434 -#define _LOAD_FAST_4 435 -#define _LOAD_FAST_5 436 -#define _LOAD_FAST_6 437 -#define _LOAD_FAST_7 438 +#define _LOAD_FAST 431 +#define _LOAD_FAST_0 432 +#define _LOAD_FAST_1 433 +#define _LOAD_FAST_2 434 +#define _LOAD_FAST_3 435 +#define _LOAD_FAST_4 436 +#define _LOAD_FAST_5 437 +#define _LOAD_FAST_6 438 +#define _LOAD_FAST_7 439 #define _LOAD_FAST_AND_CLEAR LOAD_FAST_AND_CLEAR #define _LOAD_FAST_CHECK LOAD_FAST_CHECK #define _LOAD_FAST_LOAD_FAST LOAD_FAST_LOAD_FAST #define _LOAD_FROM_DICT_OR_DEREF LOAD_FROM_DICT_OR_DEREF #define _LOAD_FROM_DICT_OR_GLOBALS LOAD_FROM_DICT_OR_GLOBALS -#define _LOAD_GLOBAL 439 -#define _LOAD_GLOBAL_BUILTINS 440 -#define _LOAD_GLOBAL_BUILTINS_FROM_KEYS 441 -#define _LOAD_GLOBAL_MODULE 442 -#define _LOAD_GLOBAL_MODULE_FROM_KEYS 443 +#define _LOAD_GLOBAL 440 +#define _LOAD_GLOBAL_BUILTINS 441 +#define _LOAD_GLOBAL_BUILTINS_FROM_KEYS 442 +#define _LOAD_GLOBAL_MODULE 443 +#define _LOAD_GLOBAL_MODULE_FROM_KEYS 444 #define _LOAD_LOCALS LOAD_LOCALS #define _LOAD_NAME LOAD_NAME -#define _LOAD_SMALL_INT 444 -#define _LOAD_SMALL_INT_0 445 -#define _LOAD_SMALL_INT_1 446 -#define _LOAD_SMALL_INT_2 447 -#define _LOAD_SMALL_INT_3 448 +#define _LOAD_SMALL_INT 445 +#define _LOAD_SMALL_INT_0 446 +#define _LOAD_SMALL_INT_1 447 +#define _LOAD_SMALL_INT_2 448 +#define _LOAD_SMALL_INT_3 449 #define _LOAD_SPECIAL LOAD_SPECIAL #define _LOAD_SUPER_ATTR_ATTR LOAD_SUPER_ATTR_ATTR #define _LOAD_SUPER_ATTR_METHOD LOAD_SUPER_ATTR_METHOD -#define _MAKE_CALLARGS_A_TUPLE 449 +#define _MAKE_CALLARGS_A_TUPLE 450 #define _MAKE_CELL MAKE_CELL #define _MAKE_FUNCTION MAKE_FUNCTION -#define _MAKE_WARM 450 +#define _MAKE_WARM 451 #define _MAP_ADD MAP_ADD #define _MATCH_CLASS MATCH_CLASS #define _MATCH_KEYS MATCH_KEYS #define _MATCH_MAPPING MATCH_MAPPING #define _MATCH_SEQUENCE MATCH_SEQUENCE -#define _MAYBE_EXPAND_METHOD 451 -#define _MAYBE_EXPAND_METHOD_KW 452 -#define _MONITOR_CALL 453 -#define _MONITOR_JUMP_BACKWARD 454 -#define _MONITOR_RESUME 455 +#define _MAYBE_EXPAND_METHOD 452 +#define _MAYBE_EXPAND_METHOD_KW 453 +#define _MONITOR_CALL 454 +#define _MONITOR_JUMP_BACKWARD 455 +#define _MONITOR_RESUME 456 #define _NOP NOP #define _POP_EXCEPT POP_EXCEPT -#define _POP_JUMP_IF_FALSE 456 -#define _POP_JUMP_IF_TRUE 457 +#define _POP_JUMP_IF_FALSE 457 +#define _POP_JUMP_IF_TRUE 458 #define _POP_TOP POP_TOP -#define _POP_TOP_LOAD_CONST_INLINE_BORROW 458 +#define _POP_TOP_LOAD_CONST_INLINE_BORROW 459 #define _PUSH_EXC_INFO PUSH_EXC_INFO -#define _PUSH_FRAME 459 +#define _PUSH_FRAME 460 #define _PUSH_NULL PUSH_NULL -#define _PY_FRAME_GENERAL 460 -#define _PY_FRAME_KW 461 -#define _QUICKEN_RESUME 462 -#define _REPLACE_WITH_TRUE 463 +#define _PY_FRAME_GENERAL 461 +#define _PY_FRAME_KW 462 +#define _QUICKEN_RESUME 463 +#define _REPLACE_WITH_TRUE 464 #define _RESUME_CHECK RESUME_CHECK #define _RETURN_GENERATOR RETURN_GENERATOR #define _RETURN_VALUE RETURN_VALUE -#define _SAVE_RETURN_OFFSET 464 -#define _SEND 465 -#define _SEND_GEN_FRAME 466 +#define _SAVE_RETURN_OFFSET 465 +#define _SEND 466 +#define _SEND_GEN_FRAME 467 #define _SETUP_ANNOTATIONS SETUP_ANNOTATIONS #define _SET_ADD SET_ADD #define _SET_FUNCTION_ATTRIBUTE SET_FUNCTION_ATTRIBUTE #define _SET_UPDATE SET_UPDATE -#define _START_EXECUTOR 467 -#define _STORE_ATTR 468 -#define _STORE_ATTR_INSTANCE_VALUE 469 -#define _STORE_ATTR_SLOT 470 -#define _STORE_ATTR_WITH_HINT 471 +#define _START_EXECUTOR 468 +#define _STORE_ATTR 469 +#define _STORE_ATTR_INSTANCE_VALUE 470 +#define _STORE_ATTR_SLOT 471 +#define _STORE_ATTR_WITH_HINT 472 #define _STORE_DEREF STORE_DEREF -#define _STORE_FAST 472 -#define _STORE_FAST_0 473 -#define _STORE_FAST_1 474 -#define _STORE_FAST_2 475 -#define _STORE_FAST_3 476 -#define _STORE_FAST_4 477 -#define _STORE_FAST_5 478 -#define _STORE_FAST_6 479 -#define _STORE_FAST_7 480 +#define _STORE_FAST 473 +#define _STORE_FAST_0 474 +#define _STORE_FAST_1 475 +#define _STORE_FAST_2 476 +#define _STORE_FAST_3 477 +#define _STORE_FAST_4 478 +#define _STORE_FAST_5 479 +#define _STORE_FAST_6 480 +#define _STORE_FAST_7 481 #define _STORE_FAST_LOAD_FAST STORE_FAST_LOAD_FAST #define _STORE_FAST_STORE_FAST STORE_FAST_STORE_FAST #define _STORE_GLOBAL STORE_GLOBAL #define _STORE_NAME STORE_NAME -#define _STORE_SLICE 481 -#define _STORE_SUBSCR 482 +#define _STORE_SLICE 482 +#define _STORE_SUBSCR 483 #define _STORE_SUBSCR_DICT STORE_SUBSCR_DICT #define _STORE_SUBSCR_LIST_INT STORE_SUBSCR_LIST_INT #define _SWAP SWAP -#define _TIER2_RESUME_CHECK 483 -#define _TO_BOOL 484 +#define _TIER2_RESUME_CHECK 484 +#define _TO_BOOL 485 #define _TO_BOOL_BOOL TO_BOOL_BOOL #define _TO_BOOL_INT TO_BOOL_INT #define _TO_BOOL_LIST TO_BOOL_LIST @@ -303,13 +304,13 @@ extern "C" { #define _UNARY_NEGATIVE UNARY_NEGATIVE #define _UNARY_NOT UNARY_NOT #define _UNPACK_EX UNPACK_EX -#define _UNPACK_SEQUENCE 485 +#define _UNPACK_SEQUENCE 486 #define _UNPACK_SEQUENCE_LIST UNPACK_SEQUENCE_LIST #define _UNPACK_SEQUENCE_TUPLE UNPACK_SEQUENCE_TUPLE #define _UNPACK_SEQUENCE_TWO_TUPLE UNPACK_SEQUENCE_TWO_TUPLE #define _WITH_EXCEPT_START WITH_EXCEPT_START #define _YIELD_VALUE YIELD_VALUE -#define MAX_UOP_ID 485 +#define MAX_UOP_ID 486 #ifdef __cplusplus } diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index 98a41d1f23f569..1b2880cb6bb67e 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -213,6 +213,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_MAYBE_EXPAND_METHOD] = HAS_ARG_FLAG, [_PY_FRAME_GENERAL] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG, [_CHECK_FUNCTION_VERSION] = HAS_ARG_FLAG | HAS_EXIT_FLAG, + [_CHECK_FUNCTION_VERSION_INLINE] = HAS_EXIT_FLAG, [_CHECK_METHOD_VERSION] = HAS_ARG_FLAG | HAS_EXIT_FLAG, [_EXPAND_METHOD] = HAS_ARG_FLAG, [_CHECK_IS_NOT_PY_CALLABLE] = HAS_ARG_FLAG | HAS_EXIT_FLAG, @@ -353,6 +354,7 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = { [_CHECK_FUNCTION] = "_CHECK_FUNCTION", [_CHECK_FUNCTION_EXACT_ARGS] = "_CHECK_FUNCTION_EXACT_ARGS", [_CHECK_FUNCTION_VERSION] = "_CHECK_FUNCTION_VERSION", + [_CHECK_FUNCTION_VERSION_INLINE] = "_CHECK_FUNCTION_VERSION_INLINE", [_CHECK_FUNCTION_VERSION_KW] = "_CHECK_FUNCTION_VERSION_KW", [_CHECK_IS_NOT_PY_CALLABLE] = "_CHECK_IS_NOT_PY_CALLABLE", [_CHECK_IS_NOT_PY_CALLABLE_KW] = "_CHECK_IS_NOT_PY_CALLABLE_KW", @@ -965,6 +967,8 @@ int _PyUop_num_popped(int opcode, int oparg) return 2 + oparg; case _CHECK_FUNCTION_VERSION: return 0; + case _CHECK_FUNCTION_VERSION_INLINE: + return 0; case _CHECK_METHOD_VERSION: return 0; case _EXPAND_METHOD: diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index 7b3d9e4fd1126f..9726353bcd6a6d 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -1486,6 +1486,26 @@ def fn(a): fn(A()) + def test_func_guards_removed_or_reduced(self): + def testfunc(n): + for i in range(n): + # Only works on functions promoted to constants + global_identity(i) + + opt = _testinternalcapi.new_uop_optimizer() + with temporary_optimizer(opt): + testfunc(20) + + ex = get_first_executor(testfunc) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_PUSH_FRAME", uops) + # Strength reduced version + self.assertIn("_CHECK_FUNCTION_VERSION_INLINE", uops) + self.assertNotIn("_CHECK_FUNCTION_VERSION", uops) + # Removed guard + self.assertNotIn("_CHECK_FUNCTION_EXACT_ARGS", uops) + def test_jit_error_pops(self): """ Tests that the correct number of pops are inserted into the @@ -1495,5 +1515,9 @@ def test_jit_error_pops(self): with self.assertRaises(TypeError): {item for item in items} + +def global_identity(x): + return x + if __name__ == "__main__": unittest.main() diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 7ae0f20369641a..04983fd861ec59 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -661,7 +661,7 @@ dummy_func( assert(next_instr->op.code == STORE_FAST); next_oparg = next_instr->op.arg; #else - next_oparg = CURRENT_OPERAND(); + next_oparg = CURRENT_OPERAND0(); #endif _PyStackRef *target_local = &GETLOCAL(next_oparg); DEOPT_IF(!PyStackRef_Is(*target_local, left)); @@ -3463,6 +3463,12 @@ dummy_func( EXIT_IF(func->func_version != func_version); } + tier2 op(_CHECK_FUNCTION_VERSION_INLINE, (func_version/2, callable_o/4 --)) { + assert(PyFunction_Check(callable_o)); + PyFunctionObject *func = (PyFunctionObject *)callable_o; + EXIT_IF(func->func_version != func_version); + } + macro(CALL_PY_GENERAL) = unused/1 + // Skip over the counter _CHECK_PEP_523 + diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h index 5df55813a0ddeb..603b71ea938cde 100644 --- a/Python/ceval_macros.h +++ b/Python/ceval_macros.h @@ -413,7 +413,8 @@ do { \ #define CURRENT_OPARG() (next_uop[-1].oparg) -#define CURRENT_OPERAND() (next_uop[-1].operand) +#define CURRENT_OPERAND0() (next_uop[-1].operand0) +#define CURRENT_OPERAND1() (next_uop[-1].operand1) #define JUMP_TO_JUMP_TARGET() goto jump_to_jump_target #define JUMP_TO_ERROR() goto jump_to_error_target diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 1d63402214db5d..494ace1bd85822 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -831,7 +831,7 @@ assert(next_instr->op.code == STORE_FAST); next_oparg = next_instr->op.arg; #else - next_oparg = CURRENT_OPERAND(); + next_oparg = CURRENT_OPERAND0(); #endif _PyStackRef *target_local = &GETLOCAL(next_oparg); if (!PyStackRef_Is(*target_local, left)) { @@ -1864,7 +1864,7 @@ } case _GUARD_GLOBALS_VERSION: { - uint16_t version = (uint16_t)CURRENT_OPERAND(); + uint16_t version = (uint16_t)CURRENT_OPERAND0(); PyDictObject *dict = (PyDictObject *)GLOBALS(); if (!PyDict_CheckExact(dict)) { UOP_STAT_INC(uopcode, miss); @@ -1880,7 +1880,7 @@ case _GUARD_GLOBALS_VERSION_PUSH_KEYS: { PyDictKeysObject *globals_keys; - uint16_t version = (uint16_t)CURRENT_OPERAND(); + uint16_t version = (uint16_t)CURRENT_OPERAND0(); PyDictObject *dict = (PyDictObject *)GLOBALS(); if (!PyDict_CheckExact(dict)) { UOP_STAT_INC(uopcode, miss); @@ -1900,7 +1900,7 @@ case _GUARD_BUILTINS_VERSION_PUSH_KEYS: { PyDictKeysObject *builtins_keys; - uint16_t version = (uint16_t)CURRENT_OPERAND(); + uint16_t version = (uint16_t)CURRENT_OPERAND0(); PyDictObject *dict = (PyDictObject *)BUILTINS(); if (!PyDict_CheckExact(dict)) { UOP_STAT_INC(uopcode, miss); @@ -1924,7 +1924,7 @@ _PyStackRef null = PyStackRef_NULL; oparg = CURRENT_OPARG(); globals_keys = (PyDictKeysObject *)stack_pointer[-1].bits; - uint16_t index = (uint16_t)CURRENT_OPERAND(); + uint16_t index = (uint16_t)CURRENT_OPERAND0(); PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(globals_keys); PyObject *res_o = entries[index].me_value; stack_pointer += -1; @@ -1950,7 +1950,7 @@ _PyStackRef null = PyStackRef_NULL; oparg = CURRENT_OPARG(); builtins_keys = (PyDictKeysObject *)stack_pointer[-1].bits; - uint16_t index = (uint16_t)CURRENT_OPERAND(); + uint16_t index = (uint16_t)CURRENT_OPERAND0(); PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(builtins_keys); PyObject *res_o = entries[index].me_value; stack_pointer += -1; @@ -2523,7 +2523,7 @@ case _GUARD_TYPE_VERSION: { _PyStackRef owner; owner = stack_pointer[-1]; - uint32_t type_version = (uint32_t)CURRENT_OPERAND(); + uint32_t type_version = (uint32_t)CURRENT_OPERAND0(); PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); assert(type_version != 0); if (tp->tp_version_tag != type_version) { @@ -2552,7 +2552,7 @@ _PyStackRef null = PyStackRef_NULL; (void)null; owner = stack_pointer[-1]; - uint16_t offset = (uint16_t)CURRENT_OPERAND(); + uint16_t offset = (uint16_t)CURRENT_OPERAND0(); PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); PyObject **value_ptr = (PyObject**)(((char *)owner_o) + offset); PyObject *attr_o = *value_ptr; @@ -2575,7 +2575,7 @@ _PyStackRef null = PyStackRef_NULL; (void)null; owner = stack_pointer[-1]; - uint16_t offset = (uint16_t)CURRENT_OPERAND(); + uint16_t offset = (uint16_t)CURRENT_OPERAND0(); PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); PyObject **value_ptr = (PyObject**)(((char *)owner_o) + offset); PyObject *attr_o = *value_ptr; @@ -2600,7 +2600,7 @@ case _CHECK_ATTR_MODULE: { _PyStackRef owner; owner = stack_pointer[-1]; - uint32_t dict_version = (uint32_t)CURRENT_OPERAND(); + uint32_t dict_version = (uint32_t)CURRENT_OPERAND0(); PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); if (!PyModule_CheckExact(owner_o)) { UOP_STAT_INC(uopcode, miss); @@ -2621,7 +2621,7 @@ _PyStackRef null = PyStackRef_NULL; oparg = CURRENT_OPARG(); owner = stack_pointer[-1]; - uint16_t index = (uint16_t)CURRENT_OPERAND(); + uint16_t index = (uint16_t)CURRENT_OPERAND0(); PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner_o)->md_dict; assert(dict->ma_keys->dk_kind == DICT_KEYS_UNICODE); @@ -2664,7 +2664,7 @@ _PyStackRef null = PyStackRef_NULL; oparg = CURRENT_OPARG(); owner = stack_pointer[-1]; - uint16_t hint = (uint16_t)CURRENT_OPERAND(); + uint16_t hint = (uint16_t)CURRENT_OPERAND0(); PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); PyObject *attr_o; PyDictObject *dict = _PyObject_GetManagedDict(owner_o); @@ -2705,7 +2705,7 @@ _PyStackRef null = PyStackRef_NULL; (void)null; owner = stack_pointer[-1]; - uint16_t index = (uint16_t)CURRENT_OPERAND(); + uint16_t index = (uint16_t)CURRENT_OPERAND0(); PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); char *addr = (char *)owner_o + index; PyObject *attr_o = *(PyObject **)addr; @@ -2727,7 +2727,7 @@ _PyStackRef null = PyStackRef_NULL; (void)null; owner = stack_pointer[-1]; - uint16_t index = (uint16_t)CURRENT_OPERAND(); + uint16_t index = (uint16_t)CURRENT_OPERAND0(); PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); char *addr = (char *)owner_o + index; PyObject *attr_o = *(PyObject **)addr; @@ -2751,7 +2751,7 @@ case _CHECK_ATTR_CLASS: { _PyStackRef owner; owner = stack_pointer[-1]; - uint32_t type_version = (uint32_t)CURRENT_OPERAND(); + uint32_t type_version = (uint32_t)CURRENT_OPERAND0(); PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); if (!PyType_Check(owner_o)) { UOP_STAT_INC(uopcode, miss); @@ -2771,7 +2771,7 @@ _PyStackRef null = PyStackRef_NULL; (void)null; owner = stack_pointer[-1]; - PyObject *descr = (PyObject *)CURRENT_OPERAND(); + PyObject *descr = (PyObject *)CURRENT_OPERAND0(); STAT_INC(LOAD_ATTR, hit); assert(descr != NULL); attr = PyStackRef_FromPyObjectNew(descr); @@ -2787,7 +2787,7 @@ _PyStackRef null = PyStackRef_NULL; (void)null; owner = stack_pointer[-1]; - PyObject *descr = (PyObject *)CURRENT_OPERAND(); + PyObject *descr = (PyObject *)CURRENT_OPERAND0(); STAT_INC(LOAD_ATTR, hit); assert(descr != NULL); attr = PyStackRef_FromPyObjectNew(descr); @@ -2807,7 +2807,7 @@ _PyInterpreterFrame *new_frame; oparg = CURRENT_OPARG(); owner = stack_pointer[-1]; - PyObject *fget = (PyObject *)CURRENT_OPERAND(); + PyObject *fget = (PyObject *)CURRENT_OPERAND0(); assert((oparg & 1) == 0); assert(Py_IS_TYPE(fget, &PyFunction_Type)); PyFunctionObject *f = (PyFunctionObject *)fget; @@ -2859,7 +2859,7 @@ _PyStackRef value; owner = stack_pointer[-1]; value = stack_pointer[-2]; - uint16_t offset = (uint16_t)CURRENT_OPERAND(); + uint16_t offset = (uint16_t)CURRENT_OPERAND0(); PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); STAT_INC(STORE_ATTR, hit); assert(_PyObject_GetManagedDict(owner_o) == NULL); @@ -2886,7 +2886,7 @@ oparg = CURRENT_OPARG(); owner = stack_pointer[-1]; value = stack_pointer[-2]; - uint16_t hint = (uint16_t)CURRENT_OPERAND(); + uint16_t hint = (uint16_t)CURRENT_OPERAND0(); PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_MANAGED_DICT); PyDictObject *dict = _PyObject_GetManagedDict(owner_o); @@ -2937,7 +2937,7 @@ _PyStackRef value; owner = stack_pointer[-1]; value = stack_pointer[-2]; - uint16_t index = (uint16_t)CURRENT_OPERAND(); + uint16_t index = (uint16_t)CURRENT_OPERAND0(); PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); char *addr = (char *)owner_o + index; STAT_INC(STORE_ATTR, hit); @@ -3780,7 +3780,7 @@ case _GUARD_KEYS_VERSION: { _PyStackRef owner; owner = stack_pointer[-1]; - uint32_t keys_version = (uint32_t)CURRENT_OPERAND(); + uint32_t keys_version = (uint32_t)CURRENT_OPERAND0(); PyTypeObject *owner_cls = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls; if (owner_heap_type->ht_cached_keys->dk_version != keys_version) { @@ -3796,7 +3796,7 @@ _PyStackRef self = PyStackRef_NULL; oparg = CURRENT_OPARG(); owner = stack_pointer[-1]; - PyObject *descr = (PyObject *)CURRENT_OPERAND(); + PyObject *descr = (PyObject *)CURRENT_OPERAND0(); assert(oparg & 1); /* Cached method object */ STAT_INC(LOAD_ATTR, hit); @@ -3817,7 +3817,7 @@ _PyStackRef self = PyStackRef_NULL; oparg = CURRENT_OPARG(); owner = stack_pointer[-1]; - PyObject *descr = (PyObject *)CURRENT_OPERAND(); + PyObject *descr = (PyObject *)CURRENT_OPERAND0(); assert(oparg & 1); assert(Py_TYPE(PyStackRef_AsPyObjectBorrow(owner))->tp_dictoffset == 0); STAT_INC(LOAD_ATTR, hit); @@ -3837,7 +3837,7 @@ _PyStackRef attr; oparg = CURRENT_OPARG(); owner = stack_pointer[-1]; - PyObject *descr = (PyObject *)CURRENT_OPERAND(); + PyObject *descr = (PyObject *)CURRENT_OPERAND0(); assert((oparg & 1) == 0); STAT_INC(LOAD_ATTR, hit); assert(descr != NULL); @@ -3852,7 +3852,7 @@ _PyStackRef attr; oparg = CURRENT_OPARG(); owner = stack_pointer[-1]; - PyObject *descr = (PyObject *)CURRENT_OPERAND(); + PyObject *descr = (PyObject *)CURRENT_OPERAND0(); assert((oparg & 1) == 0); assert(Py_TYPE(PyStackRef_AsPyObjectBorrow(owner))->tp_dictoffset == 0); STAT_INC(LOAD_ATTR, hit); @@ -3866,7 +3866,7 @@ case _CHECK_ATTR_METHOD_LAZY_DICT: { _PyStackRef owner; owner = stack_pointer[-1]; - uint16_t dictoffset = (uint16_t)CURRENT_OPERAND(); + uint16_t dictoffset = (uint16_t)CURRENT_OPERAND0(); char *ptr = ((char *)PyStackRef_AsPyObjectBorrow(owner)) + MANAGED_DICT_OFFSET + dictoffset; PyObject *dict = *(PyObject **)ptr; /* This object has a __dict__, just not yet created */ @@ -3883,7 +3883,7 @@ _PyStackRef self = PyStackRef_NULL; oparg = CURRENT_OPARG(); owner = stack_pointer[-1]; - PyObject *descr = (PyObject *)CURRENT_OPERAND(); + PyObject *descr = (PyObject *)CURRENT_OPERAND0(); assert(oparg & 1); STAT_INC(LOAD_ATTR, hit); assert(descr != NULL); @@ -3967,7 +3967,7 @@ _PyStackRef *callable; oparg = CURRENT_OPARG(); callable = &stack_pointer[-2 - oparg]; - uint32_t func_version = (uint32_t)CURRENT_OPERAND(); + uint32_t func_version = (uint32_t)CURRENT_OPERAND0(); PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); if (!PyFunction_Check(callable_o)) { UOP_STAT_INC(uopcode, miss); @@ -3981,13 +3981,25 @@ break; } + case _CHECK_FUNCTION_VERSION_INLINE: { + uint32_t func_version = (uint32_t)CURRENT_OPERAND0(); + PyObject *callable_o = (PyObject *)CURRENT_OPERAND1(); + assert(PyFunction_Check(callable_o)); + PyFunctionObject *func = (PyFunctionObject *)callable_o; + if (func->func_version != func_version) { + UOP_STAT_INC(uopcode, miss); + JUMP_TO_JUMP_TARGET(); + } + break; + } + case _CHECK_METHOD_VERSION: { _PyStackRef *null; _PyStackRef *callable; oparg = CURRENT_OPARG(); null = &stack_pointer[-1 - oparg]; callable = &stack_pointer[-2 - oparg]; - uint32_t func_version = (uint32_t)CURRENT_OPERAND(); + uint32_t func_version = (uint32_t)CURRENT_OPERAND0(); PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); if (Py_TYPE(callable_o) != &PyMethod_Type) { UOP_STAT_INC(uopcode, miss); @@ -4443,7 +4455,7 @@ callable = &stack_pointer[-2 - oparg]; init = &stack_pointer[-2 - oparg]; self = &stack_pointer[-1 - oparg]; - uint32_t type_version = (uint32_t)CURRENT_OPERAND(); + uint32_t type_version = (uint32_t)CURRENT_OPERAND0(); PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); if (!PyStackRef_IsNull(null[0])) { UOP_STAT_INC(uopcode, miss); @@ -5201,7 +5213,7 @@ _PyStackRef *callable; oparg = CURRENT_OPARG(); callable = &stack_pointer[-3 - oparg]; - uint32_t func_version = (uint32_t)CURRENT_OPERAND(); + uint32_t func_version = (uint32_t)CURRENT_OPERAND0(); PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); if (!PyFunction_Check(callable_o)) { UOP_STAT_INC(uopcode, miss); @@ -5221,7 +5233,7 @@ oparg = CURRENT_OPARG(); null = &stack_pointer[-2 - oparg]; callable = &stack_pointer[-3 - oparg]; - uint32_t func_version = (uint32_t)CURRENT_OPERAND(); + uint32_t func_version = (uint32_t)CURRENT_OPERAND0(); PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); if (Py_TYPE(callable_o) != &PyMethod_Type) { UOP_STAT_INC(uopcode, miss); @@ -5659,13 +5671,13 @@ } case _SET_IP: { - PyObject *instr_ptr = (PyObject *)CURRENT_OPERAND(); + PyObject *instr_ptr = (PyObject *)CURRENT_OPERAND0(); frame->instr_ptr = (_Py_CODEUNIT *)instr_ptr; break; } case _CHECK_STACK_SPACE_OPERAND: { - uint32_t framesize = (uint32_t)CURRENT_OPERAND(); + uint32_t framesize = (uint32_t)CURRENT_OPERAND0(); assert(framesize <= INT_MAX); if (!_PyThreadState_HasStackSpace(tstate, framesize)) { UOP_STAT_INC(uopcode, miss); @@ -5690,7 +5702,7 @@ } case _EXIT_TRACE: { - PyObject *exit_p = (PyObject *)CURRENT_OPERAND(); + PyObject *exit_p = (PyObject *)CURRENT_OPERAND0(); _PyExitData *exit = (_PyExitData *)exit_p; PyCodeObject *code = _PyFrame_GetCode(frame); _PyFrame_SetStackPointer(frame, stack_pointer); @@ -5757,7 +5769,7 @@ case _LOAD_CONST_INLINE: { _PyStackRef value; - PyObject *ptr = (PyObject *)CURRENT_OPERAND(); + PyObject *ptr = (PyObject *)CURRENT_OPERAND0(); value = PyStackRef_FromPyObjectNew(ptr); stack_pointer[0] = value; stack_pointer += 1; @@ -5767,7 +5779,7 @@ case _LOAD_CONST_INLINE_BORROW: { _PyStackRef value; - PyObject *ptr = (PyObject *)CURRENT_OPERAND(); + PyObject *ptr = (PyObject *)CURRENT_OPERAND0(); value = PyStackRef_FromPyObjectImmortal(ptr); stack_pointer[0] = value; stack_pointer += 1; @@ -5779,7 +5791,7 @@ _PyStackRef pop; _PyStackRef value; pop = stack_pointer[-1]; - PyObject *ptr = (PyObject *)CURRENT_OPERAND(); + PyObject *ptr = (PyObject *)CURRENT_OPERAND0(); PyStackRef_CLOSE(pop); value = PyStackRef_FromPyObjectImmortal(ptr); stack_pointer[-1] = value; @@ -5789,7 +5801,7 @@ case _LOAD_CONST_INLINE_WITH_NULL: { _PyStackRef value; _PyStackRef null; - PyObject *ptr = (PyObject *)CURRENT_OPERAND(); + PyObject *ptr = (PyObject *)CURRENT_OPERAND0(); value = PyStackRef_FromPyObjectNew(ptr); null = PyStackRef_NULL; stack_pointer[0] = value; @@ -5802,7 +5814,7 @@ case _LOAD_CONST_INLINE_BORROW_WITH_NULL: { _PyStackRef value; _PyStackRef null; - PyObject *ptr = (PyObject *)CURRENT_OPERAND(); + PyObject *ptr = (PyObject *)CURRENT_OPERAND0(); value = PyStackRef_FromPyObjectImmortal(ptr); null = PyStackRef_NULL; stack_pointer[0] = value; @@ -5813,7 +5825,7 @@ } case _CHECK_FUNCTION: { - uint32_t func_version = (uint32_t)CURRENT_OPERAND(); + uint32_t func_version = (uint32_t)CURRENT_OPERAND0(); assert(PyStackRef_FunctionCheck(frame->f_funcobj)); PyFunctionObject *func = (PyFunctionObject *)PyStackRef_AsPyObjectBorrow(frame->f_funcobj); if (func->func_version != func_version) { @@ -5827,7 +5839,7 @@ _PyStackRef res; _PyStackRef null = PyStackRef_NULL; oparg = CURRENT_OPARG(); - uint16_t index = (uint16_t)CURRENT_OPERAND(); + uint16_t index = (uint16_t)CURRENT_OPERAND0(); PyDictObject *dict = (PyDictObject *)GLOBALS(); PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(dict->ma_keys); PyObject *res_o = entries[index].me_value; @@ -5849,7 +5861,7 @@ _PyStackRef res; _PyStackRef null = PyStackRef_NULL; oparg = CURRENT_OPARG(); - uint16_t index = (uint16_t)CURRENT_OPERAND(); + uint16_t index = (uint16_t)CURRENT_OPERAND0(); PyDictObject *dict = (PyDictObject *)BUILTINS(); PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(dict->ma_keys); PyObject *res_o = entries[index].me_value; @@ -5878,7 +5890,7 @@ } case _DYNAMIC_EXIT: { - PyObject *exit_p = (PyObject *)CURRENT_OPERAND(); + PyObject *exit_p = (PyObject *)CURRENT_OPERAND0(); tstate->previous_executor = (PyObject *)current_executor; _PyExitData *exit = (_PyExitData *)exit_p; _Py_CODEUNIT *target = frame->instr_ptr; @@ -5925,7 +5937,7 @@ } case _START_EXECUTOR: { - PyObject *executor = (PyObject *)CURRENT_OPERAND(); + PyObject *executor = (PyObject *)CURRENT_OPERAND0(); Py_DECREF(tstate->previous_executor); tstate->previous_executor = NULL; #ifndef _Py_JIT @@ -5951,7 +5963,7 @@ } case _CHECK_VALIDITY_AND_SET_IP: { - PyObject *instr_ptr = (PyObject *)CURRENT_OPERAND(); + PyObject *instr_ptr = (PyObject *)CURRENT_OPERAND0(); if (!current_executor->vm_data.valid) { UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); @@ -5967,7 +5979,7 @@ case _ERROR_POP_N: { oparg = CURRENT_OPARG(); - uint32_t target = (uint32_t)CURRENT_OPERAND(); + uint32_t target = (uint32_t)CURRENT_OPERAND0(); stack_pointer += -oparg; assert(WITHIN_STACK_BOUNDS()); _PyFrame_SetStackPointer(frame, stack_pointer); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 03b4d2224922f0..77bf6ad3781f17 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -192,7 +192,7 @@ assert(next_instr->op.code == STORE_FAST); next_oparg = next_instr->op.arg; #else - next_oparg = CURRENT_OPERAND(); + next_oparg = CURRENT_OPERAND0(); #endif _PyStackRef *target_local = &GETLOCAL(next_oparg); DEOPT_IF(!PyStackRef_Is(*target_local, left), BINARY_OP); diff --git a/Python/optimizer.c b/Python/optimizer.c index b876b6c2bd72fd..bc2ecc098b0e15 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -288,13 +288,13 @@ _PyUOpPrint(const _PyUOpInstruction *uop) printf(" (%d, target=%d, operand=%#" PRIx64, uop->oparg, uop->target, - (uint64_t)uop->operand); + (uint64_t)uop->operand0); break; case UOP_FORMAT_JUMP: printf(" (%d, jump_target=%d, operand=%#" PRIx64, uop->oparg, uop->jump_target, - (uint64_t)uop->operand); + (uint64_t)uop->operand0); break; default: printf(" (%d, Unknown format)", uop->oparg); @@ -340,7 +340,7 @@ uop_item(_PyExecutorObject *self, Py_ssize_t index) Py_DECREF(oname); return NULL; } - PyObject *operand = PyLong_FromUnsignedLongLong(self->trace[index].operand); + PyObject *operand = PyLong_FromUnsignedLongLong(self->trace[index].operand0); if (operand == NULL) { Py_DECREF(target); Py_DECREF(oparg); @@ -463,7 +463,7 @@ add_to_trace( trace[trace_length].format = UOP_FORMAT_TARGET; trace[trace_length].target = target; trace[trace_length].oparg = oparg; - trace[trace_length].operand = operand; + trace[trace_length].operand0 = operand; return trace_length + 1; } @@ -970,7 +970,7 @@ static void make_exit(_PyUOpInstruction *inst, int opcode, int target) { inst->opcode = opcode; inst->oparg = 0; - inst->operand = 0; + inst->operand0 = 0; inst->format = UOP_FORMAT_TARGET; inst->target = target; } @@ -1033,7 +1033,7 @@ prepare_for_execution(_PyUOpInstruction *buffer, int length) current_error_target = target; make_exit(&buffer[next_spare], _ERROR_POP_N, 0); buffer[next_spare].oparg = popped; - buffer[next_spare].operand = target; + buffer[next_spare].operand0 = target; next_spare++; } buffer[i].error_target = current_error; @@ -1150,7 +1150,7 @@ make_executor_from_uops(_PyUOpInstruction *buffer, int length, const _PyBloomFil int next_exit = exit_count-1; _PyUOpInstruction *dest = (_PyUOpInstruction *)&executor->trace[length]; assert(buffer[0].opcode == _START_EXECUTOR); - buffer[0].operand = (uint64_t)executor; + buffer[0].operand0 = (uint64_t)executor; for (int i = length-1; i >= 0; i--) { int opcode = buffer[i].opcode; dest--; @@ -1159,13 +1159,13 @@ make_executor_from_uops(_PyUOpInstruction *buffer, int length, const _PyBloomFil if (opcode == _EXIT_TRACE) { _PyExitData *exit = &executor->exits[next_exit]; exit->target = buffer[i].target; - dest->operand = (uint64_t)exit; + dest->operand0 = (uint64_t)exit; next_exit--; } if (opcode == _DYNAMIC_EXIT) { _PyExitData *exit = &executor->exits[next_exit]; exit->target = 0; - dest->operand = (uint64_t)exit; + dest->operand0 = (uint64_t)exit; next_exit--; } } @@ -1312,7 +1312,7 @@ _PyOptimizer_NewUOpOptimizer(void) static void counter_dealloc(_PyExecutorObject *self) { /* The optimizer is the operand of the second uop. */ - PyObject *opt = (PyObject *)self->trace[1].operand; + PyObject *opt = (PyObject *)self->trace[1].operand0; Py_DECREF(opt); uop_dealloc(self); } @@ -1352,7 +1352,7 @@ counter_optimize( _Py_CODEUNIT *target = instr + 1 + _PyOpcode_Caches[JUMP_BACKWARD] - oparg; _PyUOpInstruction buffer[4] = { { .opcode = _START_EXECUTOR, .jump_target = 3, .format=UOP_FORMAT_JUMP }, - { .opcode = _LOAD_CONST_INLINE, .operand = (uintptr_t)self }, + { .opcode = _LOAD_CONST_INLINE, .operand0 = (uintptr_t)self }, { .opcode = _INTERNAL_INCREMENT_OPT_COUNTER }, { .opcode = _EXIT_TRACE, .target = (uint32_t)(target - _PyCode_CODE(code)), .format=UOP_FORMAT_TARGET } }; diff --git a/Python/optimizer_analysis.c b/Python/optimizer_analysis.c index 25166bc2dc5c02..a4a0472b64e57c 100644 --- a/Python/optimizer_analysis.c +++ b/Python/optimizer_analysis.c @@ -100,11 +100,11 @@ convert_global_to_const(_PyUOpInstruction *inst, PyObject *obj) PyDictObject *dict = (PyDictObject *)obj; assert(dict->ma_keys->dk_kind == DICT_KEYS_UNICODE); PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(dict->ma_keys); - assert(inst->operand <= UINT16_MAX); - if ((int)inst->operand >= dict->ma_keys->dk_nentries) { + assert(inst->operand0 <= UINT16_MAX); + if ((int)inst->operand0 >= dict->ma_keys->dk_nentries) { return NULL; } - PyObject *res = entries[inst->operand].me_value; + PyObject *res = entries[inst->operand0].me_value; if (res == NULL) { return NULL; } @@ -114,7 +114,7 @@ convert_global_to_const(_PyUOpInstruction *inst, PyObject *obj) else { inst->opcode = (inst->oparg & 1) ? _LOAD_CONST_INLINE_WITH_NULL : _LOAD_CONST_INLINE; } - inst->operand = (uint64_t)res; + inst->operand0 = (uint64_t)res; return res; } @@ -125,7 +125,7 @@ incorrect_keys(_PyUOpInstruction *inst, PyObject *obj) return 1; } PyDictObject *dict = (PyDictObject *)obj; - if (dict->ma_keys->dk_version != inst->operand) { + if (dict->ma_keys->dk_version != inst->operand0) { return 1; } return 0; @@ -215,7 +215,7 @@ remove_globals(_PyInterpreterFrame *frame, _PyUOpInstruction *buffer, } else { buffer[pc].opcode = _CHECK_FUNCTION; - buffer[pc].operand = function_version; + buffer[pc].operand0 = function_version; function_checked |= 1; } // We're no longer pushing the builtins keys; rewrite the @@ -248,7 +248,7 @@ remove_globals(_PyInterpreterFrame *frame, _PyUOpInstruction *buffer, } else { buffer[pc].opcode = _CHECK_FUNCTION; - buffer[pc].operand = function_version; + buffer[pc].operand0 = function_version; function_checked |= 1; } if (opcode == _GUARD_GLOBALS_VERSION_PUSH_KEYS) { @@ -273,7 +273,7 @@ remove_globals(_PyInterpreterFrame *frame, _PyUOpInstruction *buffer, builtins_watched <<= 1; globals_watched <<= 1; function_checked <<= 1; - uint64_t operand = buffer[pc].operand; + uint64_t operand = buffer[pc].operand0; if (operand == 0 || (operand & 1)) { // It's either a code object or NULL, so bail return 1; @@ -301,7 +301,7 @@ remove_globals(_PyInterpreterFrame *frame, _PyUOpInstruction *buffer, builtins_watched >>= 1; globals_watched >>= 1; function_checked >>= 1; - uint64_t operand = buffer[pc].operand; + uint64_t operand = buffer[pc].operand0; if (operand == 0 || (operand & 1)) { // It's either a code object or NULL, so bail return 1; @@ -317,7 +317,7 @@ remove_globals(_PyInterpreterFrame *frame, _PyUOpInstruction *buffer, break; } case _CHECK_FUNCTION_EXACT_ARGS: - prechecked_function_version = (uint32_t)buffer[pc].operand; + prechecked_function_version = (uint32_t)buffer[pc].operand0; break; default: if (is_terminator(inst)) { @@ -343,7 +343,7 @@ remove_globals(_PyInterpreterFrame *frame, _PyUOpInstruction *buffer, #define REPLACE_OP(INST, OP, ARG, OPERAND) \ INST->opcode = OP; \ INST->oparg = ARG; \ - INST->operand = OPERAND; + INST->operand0 = OPERAND; /* Shortened forms for convenience, used in optimizer_bytecodes.c */ #define sym_is_not_null _Py_uop_sym_is_not_null @@ -409,7 +409,7 @@ get_code(_PyUOpInstruction *op) { assert(op->opcode == _PUSH_FRAME || op->opcode == _RETURN_VALUE || op->opcode == _RETURN_GENERATOR); PyCodeObject *co = NULL; - uint64_t operand = op->operand; + uint64_t operand = op->operand0; if (operand == 0) { return NULL; } @@ -429,7 +429,7 @@ static PyCodeObject * get_code_with_logging(_PyUOpInstruction *op) { PyCodeObject *co = NULL; - uint64_t push_operand = op->operand; + uint64_t push_operand = op->operand0; if (push_operand & 1) { co = (PyCodeObject *)(push_operand & ~1); DPRINTF(3, "code=%p ", co); @@ -534,7 +534,7 @@ optimize_uops( assert(max_space <= INT_MAX); assert(max_space <= INT32_MAX); first_valid_check_stack->opcode = _CHECK_STACK_SPACE_OPERAND; - first_valid_check_stack->operand = max_space; + first_valid_check_stack->operand0 = max_space; } return trace_len; diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index 71904c1bc73f88..42bdbd9ca8d0cd 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -346,7 +346,7 @@ dummy_func(void) { res = sym_new_type(ctx, &PyUnicode_Type); } // _STORE_FAST: - GETLOCAL(this_instr->operand) = res; + GETLOCAL(this_instr->operand0) = res; } op(_BINARY_SUBSCR_INIT_CALL, (container, sub -- new_frame: _Py_UOpsAbstractFrame *)) { @@ -589,8 +589,27 @@ dummy_func(void) { self = sym_new_not_null(ctx); } - op(_CHECK_FUNCTION_EXACT_ARGS, (callable, self_or_null, unused[oparg] -- callable, self_or_null, unused[oparg])) { + op(_CHECK_FUNCTION_VERSION, (func_version/2, callable, self_or_null, unused[oparg] -- callable, self_or_null, unused[oparg])) { + (void)self_or_null; + if (sym_is_const(callable) && sym_matches_type(callable, &PyFunction_Type)) { + assert(PyFunction_Check(sym_get_const(callable))); + REPLACE_OP(this_instr, _CHECK_FUNCTION_VERSION_INLINE, 0, func_version); + this_instr->operand1 = (uintptr_t)sym_get_const(callable); + } sym_set_type(callable, &PyFunction_Type); + } + + op(_CHECK_FUNCTION_EXACT_ARGS, (callable, self_or_null, unused[oparg] -- callable, self_or_null, unused[oparg])) { + assert(sym_matches_type(callable, &PyFunction_Type)); + if (sym_is_const(callable)) { + if (sym_is_null(self_or_null) || sym_is_not_null(self_or_null)) { + PyFunctionObject *func = (PyFunctionObject *)sym_get_const(callable); + PyCodeObject *co = (PyCodeObject *)func->func_code; + if (co->co_argcount == oparg + !sym_is_null(self_or_null)) { + REPLACE_OP(this_instr, _NOP, 0 ,0); + } + } + } (void)self_or_null; } diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 54821b23716eeb..f77a5aa35bdf82 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -525,7 +525,7 @@ res = sym_new_type(ctx, &PyUnicode_Type); } // _STORE_FAST: - GETLOCAL(this_instr->operand) = res; + GETLOCAL(this_instr->operand0) = res; stack_pointer += -2; assert(WITHIN_STACK_BOUNDS()); break; @@ -888,7 +888,7 @@ case _GUARD_GLOBALS_VERSION_PUSH_KEYS: { _Py_UopsSymbol *globals_keys; - uint16_t version = (uint16_t)this_instr->operand; + uint16_t version = (uint16_t)this_instr->operand0; globals_keys = sym_new_unknown(ctx); (void)version; stack_pointer[0] = globals_keys; @@ -899,7 +899,7 @@ case _GUARD_BUILTINS_VERSION_PUSH_KEYS: { _Py_UopsSymbol *builtins_keys; - uint16_t version = (uint16_t)this_instr->operand; + uint16_t version = (uint16_t)this_instr->operand0; builtins_keys = sym_new_unknown(ctx); (void)version; stack_pointer[0] = builtins_keys; @@ -1090,7 +1090,7 @@ case _GUARD_TYPE_VERSION: { _Py_UopsSymbol *owner; owner = stack_pointer[-1]; - uint32_t type_version = (uint32_t)this_instr->operand; + uint32_t type_version = (uint32_t)this_instr->operand0; assert(type_version); if (sym_matches_type_version(owner, type_version)) { REPLACE_OP(this_instr, _NOP, 0, 0); @@ -1122,7 +1122,7 @@ _Py_UopsSymbol *attr; _Py_UopsSymbol *null = NULL; owner = stack_pointer[-1]; - uint16_t offset = (uint16_t)this_instr->operand; + uint16_t offset = (uint16_t)this_instr->operand0; attr = sym_new_not_null(ctx); null = sym_new_null(ctx); (void)offset; @@ -1137,7 +1137,7 @@ case _CHECK_ATTR_MODULE: { _Py_UopsSymbol *owner; owner = stack_pointer[-1]; - uint32_t dict_version = (uint32_t)this_instr->operand; + uint32_t dict_version = (uint32_t)this_instr->operand0; (void)dict_version; if (sym_is_const(owner)) { PyObject *cnst = sym_get_const(owner); @@ -1160,7 +1160,7 @@ _Py_UopsSymbol *attr; _Py_UopsSymbol *null = NULL; owner = stack_pointer[-1]; - uint16_t index = (uint16_t)this_instr->operand; + uint16_t index = (uint16_t)this_instr->operand0; (void)index; null = sym_new_null(ctx); attr = NULL; @@ -1202,7 +1202,7 @@ _Py_UopsSymbol *attr; _Py_UopsSymbol *null = NULL; owner = stack_pointer[-1]; - uint16_t hint = (uint16_t)this_instr->operand; + uint16_t hint = (uint16_t)this_instr->operand0; attr = sym_new_not_null(ctx); null = sym_new_null(ctx); (void)hint; @@ -1219,7 +1219,7 @@ _Py_UopsSymbol *attr; _Py_UopsSymbol *null = NULL; owner = stack_pointer[-1]; - uint16_t index = (uint16_t)this_instr->operand; + uint16_t index = (uint16_t)this_instr->operand0; attr = sym_new_not_null(ctx); null = sym_new_null(ctx); (void)index; @@ -1240,7 +1240,7 @@ _Py_UopsSymbol *attr; _Py_UopsSymbol *null = NULL; owner = stack_pointer[-1]; - PyObject *descr = (PyObject *)this_instr->operand; + PyObject *descr = (PyObject *)this_instr->operand0; attr = sym_new_not_null(ctx); null = sym_new_null(ctx); (void)descr; @@ -1256,7 +1256,7 @@ _Py_UopsSymbol *owner; _Py_UOpsAbstractFrame *new_frame; owner = stack_pointer[-1]; - PyObject *fget = (PyObject *)this_instr->operand; + PyObject *fget = (PyObject *)this_instr->operand0; (void)fget; (void)owner; new_frame = NULL; @@ -1639,7 +1639,7 @@ _Py_UopsSymbol *attr; _Py_UopsSymbol *self = NULL; owner = stack_pointer[-1]; - PyObject *descr = (PyObject *)this_instr->operand; + PyObject *descr = (PyObject *)this_instr->operand0; (void)descr; attr = sym_new_not_null(ctx); self = owner; @@ -1655,7 +1655,7 @@ _Py_UopsSymbol *attr; _Py_UopsSymbol *self = NULL; owner = stack_pointer[-1]; - PyObject *descr = (PyObject *)this_instr->operand; + PyObject *descr = (PyObject *)this_instr->operand0; (void)descr; attr = sym_new_not_null(ctx); self = owner; @@ -1689,7 +1689,7 @@ _Py_UopsSymbol *attr; _Py_UopsSymbol *self = NULL; owner = stack_pointer[-1]; - PyObject *descr = (PyObject *)this_instr->operand; + PyObject *descr = (PyObject *)this_instr->operand0; (void)descr; attr = sym_new_not_null(ctx); self = owner; @@ -1749,6 +1749,22 @@ } case _CHECK_FUNCTION_VERSION: { + _Py_UopsSymbol *self_or_null; + _Py_UopsSymbol *callable; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + uint32_t func_version = (uint32_t)this_instr->operand0; + (void)self_or_null; + if (sym_is_const(callable) && sym_matches_type(callable, &PyFunction_Type)) { + assert(PyFunction_Check(sym_get_const(callable))); + REPLACE_OP(this_instr, _CHECK_FUNCTION_VERSION_INLINE, 0, func_version); + this_instr->operand1 = (uintptr_t)sym_get_const(callable); + } + sym_set_type(callable, &PyFunction_Type); + break; + } + + case _CHECK_FUNCTION_VERSION_INLINE: { break; } @@ -1816,7 +1832,16 @@ _Py_UopsSymbol *callable; self_or_null = stack_pointer[-1 - oparg]; callable = stack_pointer[-2 - oparg]; - sym_set_type(callable, &PyFunction_Type); + assert(sym_matches_type(callable, &PyFunction_Type)); + if (sym_is_const(callable)) { + if (sym_is_null(self_or_null) || sym_is_not_null(self_or_null)) { + PyFunctionObject *func = (PyFunctionObject *)sym_get_const(callable); + PyCodeObject *co = (PyCodeObject *)func->func_code; + if (co->co_argcount == oparg + !sym_is_null(self_or_null)) { + REPLACE_OP(this_instr, _NOP, 0 ,0); + } + } + } (void)self_or_null; break; } @@ -1939,7 +1964,7 @@ null = stack_pointer[-1 - oparg]; callable = stack_pointer[-2 - oparg]; args = &stack_pointer[-oparg]; - uint32_t type_version = (uint32_t)this_instr->operand; + uint32_t type_version = (uint32_t)this_instr->operand0; (void)type_version; (void)callable; (void)null; @@ -2399,7 +2424,7 @@ } case _CHECK_STACK_SPACE_OPERAND: { - uint32_t framesize = (uint32_t)this_instr->operand; + uint32_t framesize = (uint32_t)this_instr->operand0; (void)framesize; /* We should never see _CHECK_STACK_SPACE_OPERANDs. * They are only created at the end of this pass. */ @@ -2412,7 +2437,7 @@ } case _EXIT_TRACE: { - PyObject *exit_p = (PyObject *)this_instr->operand; + PyObject *exit_p = (PyObject *)this_instr->operand0; (void)exit_p; ctx->done = true; break; @@ -2424,7 +2449,7 @@ case _LOAD_CONST_INLINE: { _Py_UopsSymbol *value; - PyObject *ptr = (PyObject *)this_instr->operand; + PyObject *ptr = (PyObject *)this_instr->operand0; value = sym_new_const(ctx, ptr); stack_pointer[0] = value; stack_pointer += 1; @@ -2434,7 +2459,7 @@ case _LOAD_CONST_INLINE_BORROW: { _Py_UopsSymbol *value; - PyObject *ptr = (PyObject *)this_instr->operand; + PyObject *ptr = (PyObject *)this_instr->operand0; value = sym_new_const(ctx, ptr); stack_pointer[0] = value; stack_pointer += 1; @@ -2452,7 +2477,7 @@ case _LOAD_CONST_INLINE_WITH_NULL: { _Py_UopsSymbol *value; _Py_UopsSymbol *null; - PyObject *ptr = (PyObject *)this_instr->operand; + PyObject *ptr = (PyObject *)this_instr->operand0; value = sym_new_const(ctx, ptr); null = sym_new_null(ctx); stack_pointer[0] = value; @@ -2465,7 +2490,7 @@ case _LOAD_CONST_INLINE_BORROW_WITH_NULL: { _Py_UopsSymbol *value; _Py_UopsSymbol *null; - PyObject *ptr = (PyObject *)this_instr->operand; + PyObject *ptr = (PyObject *)this_instr->operand0; value = sym_new_const(ctx, ptr); null = sym_new_null(ctx); stack_pointer[0] = value; diff --git a/Tools/cases_generator/analyzer.py b/Tools/cases_generator/analyzer.py index a725ec10d4e52a..96b32445fb62e2 100644 --- a/Tools/cases_generator/analyzer.py +++ b/Tools/cases_generator/analyzer.py @@ -200,7 +200,7 @@ def why_not_viable(self) -> str | None: return "has tier 1 control flow" if self.properties.needs_this: return "uses the 'this_instr' variable" - if len([c for c in self.caches if c.name != "unused"]) > 1: + if len([c for c in self.caches if c.name != "unused"]) > 2: return "has unused cache entries" if self.properties.error_with_pop and self.properties.error_without_pop: return "has both popping and not-popping errors" diff --git a/Tools/cases_generator/optimizer_generator.py b/Tools/cases_generator/optimizer_generator.py index 7a1dfe1b85bf1a..d08b621aed552b 100644 --- a/Tools/cases_generator/optimizer_generator.py +++ b/Tools/cases_generator/optimizer_generator.py @@ -143,7 +143,7 @@ def write_uop( else: type = f"uint{cache.size*16}_t " cast = f"uint{cache.size*16}_t" - out.emit(f"{type}{cache.name} = ({cast})this_instr->operand;\n") + out.emit(f"{type}{cache.name} = ({cast})this_instr->operand0;\n") if override: emitter = OptimizerEmitter(out) # No reference management of inputs needed. diff --git a/Tools/cases_generator/tier2_generator.py b/Tools/cases_generator/tier2_generator.py index ce761495cca435..dd16a1a7eb28b5 100644 --- a/Tools/cases_generator/tier2_generator.py +++ b/Tools/cases_generator/tier2_generator.py @@ -181,14 +181,14 @@ def write_uop(uop: Uop, emitter: Emitter, stack: Stack) -> Stack: code_list, storage = Storage.for_uop(stack, uop) for code in code_list: emitter.emit(code) - for cache in uop.caches: + for idx, cache in enumerate(uop.caches): if cache.name != "unused": if cache.size == 4: type = cast = "PyObject *" else: type = f"uint{cache.size*16}_t " cast = f"uint{cache.size*16}_t" - emitter.emit(f"{type}{cache.name} = ({cast})CURRENT_OPERAND();\n") + emitter.emit(f"{type}{cache.name} = ({cast})CURRENT_OPERAND{idx}();\n") storage = emitter.emit_tokens(uop, storage, None) except StackError as ex: raise analysis_error(ex.args[0], uop.body[0]) from None diff --git a/Tools/jit/_stencils.py b/Tools/jit/_stencils.py index 2cd051b0a77b8d..61be8fd3bbdf55 100644 --- a/Tools/jit/_stencils.py +++ b/Tools/jit/_stencils.py @@ -29,11 +29,16 @@ class HoleValue(enum.Enum): GOT = enum.auto() # The current uop's oparg (exposed as _JIT_OPARG): OPARG = enum.auto() - # The current uop's operand on 64-bit platforms (exposed as _JIT_OPERAND): - OPERAND = enum.auto() - # The current uop's operand on 32-bit platforms (exposed as _JIT_OPERAND_HI/LO): - OPERAND_HI = enum.auto() - OPERAND_LO = enum.auto() + # The current uop's operand0 on 64-bit platforms (exposed as _JIT_OPERAND0): + OPERAND0 = enum.auto() + # The current uop's operand0 on 32-bit platforms (exposed as _JIT_OPERAND0_HI/LO): + OPERAND0_HI = enum.auto() + OPERAND0_LO = enum.auto() + # The current uop's operand1 on 64-bit platforms (exposed as _JIT_OPERAND1): + OPERAND1 = enum.auto() + # The current uop's operand1 on 32-bit platforms (exposed as _JIT_OPERAND1_HI/LO): + OPERAND1_HI = enum.auto() + OPERAND1_LO = enum.auto() # The current uop's target (exposed as _JIT_TARGET): TARGET = enum.auto() # The base address of the machine code for the jump target (exposed as _JIT_JUMP_TARGET): @@ -99,9 +104,12 @@ class HoleValue(enum.Enum): # These should all have been turned into DATA values by process_relocations: # HoleValue.GOT: "", HoleValue.OPARG: "instruction->oparg", - HoleValue.OPERAND: "instruction->operand", - HoleValue.OPERAND_HI: "(instruction->operand >> 32)", - HoleValue.OPERAND_LO: "(instruction->operand & UINT32_MAX)", + HoleValue.OPERAND0: "instruction->operand0", + HoleValue.OPERAND0_HI: "(instruction->operand0 >> 32)", + HoleValue.OPERAND0_LO: "(instruction->operand0 & UINT32_MAX)", + HoleValue.OPERAND1: "instruction->operand1", + HoleValue.OPERAND1_HI: "(instruction->operand1 >> 32)", + HoleValue.OPERAND1_LO: "(instruction->operand1 & UINT32_MAX)", HoleValue.TARGET: "instruction->target", HoleValue.JUMP_TARGET: "state->instruction_starts[instruction->jump_target]", HoleValue.ERROR_TARGET: "state->instruction_starts[instruction->error_target]", diff --git a/Tools/jit/template.c b/Tools/jit/template.c index 57c1006ab423e9..95c90bda70f352 100644 --- a/Tools/jit/template.c +++ b/Tools/jit/template.c @@ -26,8 +26,11 @@ #undef CURRENT_OPARG #define CURRENT_OPARG() (_oparg) -#undef CURRENT_OPERAND -#define CURRENT_OPERAND() (_operand) +#undef CURRENT_OPERAND0 +#define CURRENT_OPERAND0() (_operand0) + +#undef CURRENT_OPERAND1 +#define CURRENT_OPERAND1() (_operand1) #undef DEOPT_IF #define DEOPT_IF(COND, INSTNAME) \ @@ -99,12 +102,17 @@ _JIT_ENTRY(_PyInterpreterFrame *frame, _PyStackRef *stack_pointer, PyThreadState // Other stuff we need handy: PATCH_VALUE(uint16_t, _oparg, _JIT_OPARG) #if SIZEOF_VOID_P == 8 - PATCH_VALUE(uint64_t, _operand, _JIT_OPERAND) + PATCH_VALUE(uint64_t, _operand0, _JIT_OPERAND0) + PATCH_VALUE(uint64_t, _operand1, _JIT_OPERAND1) #else assert(SIZEOF_VOID_P == 4); - PATCH_VALUE(uint32_t, _operand_hi, _JIT_OPERAND_HI) - PATCH_VALUE(uint32_t, _operand_lo, _JIT_OPERAND_LO) - uint64_t _operand = ((uint64_t)_operand_hi << 32) | _operand_lo; + PATCH_VALUE(uint32_t, _operand0_hi, _JIT_OPERAND0_HI) + PATCH_VALUE(uint32_t, _operand0_lo, _JIT_OPERAND0_LO) + uint64_t _operand0 = ((uint64_t)_operand0_hi << 32) | _operand0_lo; + + PATCH_VALUE(uint32_t, _operand1_hi, _JIT_OPERAND1_HI) + PATCH_VALUE(uint32_t, _operand1_lo, _JIT_OPERAND1_LO) + uint64_t _operand1 = ((uint64_t)_operand1_hi << 32) | _operand1_lo; #endif PATCH_VALUE(uint32_t, _target, _JIT_TARGET) From 0f47a3199c51ba7c49e72b4c645dbf599aa17be4 Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Sat, 9 Nov 2024 18:21:53 +0000 Subject: [PATCH 116/219] pathlib ABCs: support initializing paths with no arguments (#126608) In the past I've equivocated about whether to require at least one argument in the `PurePathBase` (and `PathBase`) initializer, and what the default should be if we make it optional. I now have a local use case that has persuaded me to make it optional and default to the empty string (a `zipp.Path`-like class that treats relative and absolute paths similarly.) Happily this brings the base class more in line with `PurePath` and `Path`. --- Lib/pathlib/_abc.py | 16 +++++++--------- Lib/test/test_pathlib/test_pathlib_abc.py | 1 + 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py index 43e6624934b045..e9e46e511bddf1 100644 --- a/Lib/pathlib/_abc.py +++ b/Lib/pathlib/_abc.py @@ -124,14 +124,12 @@ class PurePathBase: parser = ParserBase() _globber = PathGlobber - def __init__(self, arg, *args): - paths = [arg] - paths.extend(args) - for path in paths: - if not isinstance(path, str): + def __init__(self, *args): + for arg in args: + if not isinstance(arg, str): raise TypeError( - f"path should be a str, not {type(path).__name__!r}") - self._raw_paths = paths + f"argument should be a str, not {type(arg).__name__!r}") + self._raw_paths = list(args) self._resolving = False def with_segments(self, *pathsegments): @@ -270,7 +268,7 @@ def relative_to(self, other, *, walk_up=False): raise ValueError(f"'..' segment in {str(other)!r} cannot be walked") else: parts0.append('..') - return self.with_segments('', *reversed(parts0)) + return self.with_segments(*reversed(parts0)) def is_relative_to(self, other): """Return True if the path is relative to another path or False. @@ -746,7 +744,7 @@ def cwd(cls): # enable users to replace the implementation of 'absolute()' in a # subclass and benefit from the new behaviour here. This works because # os.path.abspath('.') == os.getcwd(). - return cls('').absolute() + return cls().absolute() def expanduser(self): """ Return a new path with expanded ~ and ~user constructs diff --git a/Lib/test/test_pathlib/test_pathlib_abc.py b/Lib/test/test_pathlib/test_pathlib_abc.py index d155e7c5bb9935..bb2e4187ef9574 100644 --- a/Lib/test/test_pathlib/test_pathlib_abc.py +++ b/Lib/test/test_pathlib/test_pathlib_abc.py @@ -148,6 +148,7 @@ def test_constructor_common(self): P = self.cls p = P('a') self.assertIsInstance(p, P) + P() P('a', 'b', 'c') P('/a', 'b', 'c') P('a/b/c') From 266328552e922fd9030cd699e10a25f03a67c8ba Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Sat, 9 Nov 2024 18:47:49 +0000 Subject: [PATCH 117/219] pathlib ABCs: tighten up `resolve()` and `absolute()` (#126611) In `PathBase.resolve()`, raise `UnsupportedOperation` if a non-POSIX path parser is used (our implementation uses `posixpath._realpath()`, which produces incorrect results for non-POSIX path flavours.) Also tweak code to call `self.absolute()` upfront rather than supplying an emulated `getcwd()` function. Adjust `PathBase.absolute()` to work somewhat like `resolve()`. If a POSIX path parser is used, we treat the root directory as the current directory. This is the simplest useful behaviour for concrete path types without a current directory cursor. --- Lib/pathlib/_abc.py | 23 ++++++++----- Lib/test/test_pathlib/test_pathlib.py | 22 +++++++++++++ Lib/test/test_pathlib/test_pathlib_abc.py | 40 ++++++++++------------- 3 files changed, 53 insertions(+), 32 deletions(-) diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py index e9e46e511bddf1..2c243d470d4eda 100644 --- a/Lib/pathlib/_abc.py +++ b/Lib/pathlib/_abc.py @@ -735,7 +735,13 @@ def absolute(self): Use resolve() to resolve symlinks and remove '..' segments. """ - raise UnsupportedOperation(self._unsupported_msg('absolute()')) + if self.is_absolute(): + return self + elif self.parser is not posixpath: + raise UnsupportedOperation(self._unsupported_msg('absolute()')) + else: + # Treat the root directory as the current working directory. + return self.with_segments('/', *self._raw_paths) @classmethod def cwd(cls): @@ -772,10 +778,13 @@ def resolve(self, strict=False): """ if self._resolving: return self + elif self.parser is not posixpath: + raise UnsupportedOperation(self._unsupported_msg('resolve()')) - def getcwd(): - return str(self.with_segments().absolute()) + def raise_error(*args): + raise OSError("Unsupported operation.") + getcwd = raise_error if strict or getattr(self.readlink, '_supported', True): def lstat(path_str): path = self.with_segments(path_str) @@ -790,14 +799,10 @@ def readlink(path_str): # If the user has *not* overridden the `readlink()` method, then # symlinks are unsupported and (in non-strict mode) we can improve # performance by not calling `path.lstat()`. - def skip(path_str): - # This exception will be internally consumed by `_realpath()`. - raise OSError("Operation skipped.") - - lstat = readlink = skip + lstat = readlink = raise_error return self.with_segments(posixpath._realpath( - str(self), strict, self.parser.sep, + str(self.absolute()), strict, self.parser.sep, getcwd=getcwd, lstat=lstat, readlink=readlink, maxlinks=self._max_symlinks)) diff --git a/Lib/test/test_pathlib/test_pathlib.py b/Lib/test/test_pathlib/test_pathlib.py index c7104bfda90f6c..46966b6df2d7b0 100644 --- a/Lib/test/test_pathlib/test_pathlib.py +++ b/Lib/test/test_pathlib/test_pathlib.py @@ -861,6 +861,28 @@ def test_move_into_other_os(self): def test_move_into_empty_name_other_os(self): self.test_move_into_empty_name() + def _check_complex_symlinks(self, link0_target): + super()._check_complex_symlinks(link0_target) + P = self.cls(self.base) + # Resolve relative paths. + old_path = os.getcwd() + os.chdir(self.base) + try: + p = self.cls('link0').resolve() + self.assertEqual(p, P) + self.assertEqualNormCase(str(p), self.base) + p = self.cls('link1').resolve() + self.assertEqual(p, P) + self.assertEqualNormCase(str(p), self.base) + p = self.cls('link2').resolve() + self.assertEqual(p, P) + self.assertEqualNormCase(str(p), self.base) + p = self.cls('link3').resolve() + self.assertEqual(p, P) + self.assertEqualNormCase(str(p), self.base) + finally: + os.chdir(old_path) + def test_resolve_nonexist_relative_issue38671(self): p = self.cls('non', 'exist') diff --git a/Lib/test/test_pathlib/test_pathlib_abc.py b/Lib/test/test_pathlib/test_pathlib_abc.py index bb2e4187ef9574..b69d674e1cf1ed 100644 --- a/Lib/test/test_pathlib/test_pathlib_abc.py +++ b/Lib/test/test_pathlib/test_pathlib_abc.py @@ -2493,6 +2493,23 @@ def test_glob_long_symlink(self): bad_link.symlink_to("bad" * 200) self.assertEqual(sorted(base.glob('**/*')), [bad_link]) + @needs_posix + def test_absolute_posix(self): + P = self.cls + # The default implementation uses '/' as the current directory + self.assertEqual(str(P('').absolute()), '/') + self.assertEqual(str(P('a').absolute()), '/a') + self.assertEqual(str(P('a/b').absolute()), '/a/b') + + self.assertEqual(str(P('/').absolute()), '/') + self.assertEqual(str(P('/a').absolute()), '/a') + self.assertEqual(str(P('/a/b').absolute()), '/a/b') + + # '//'-prefixed absolute path (supported by POSIX). + self.assertEqual(str(P('//').absolute()), '//') + self.assertEqual(str(P('//a').absolute()), '//a') + self.assertEqual(str(P('//a/b').absolute()), '//a/b') + @needs_symlinks def test_readlink(self): P = self.cls(self.base) @@ -2810,29 +2827,6 @@ def _check_complex_symlinks(self, link0_target): self.assertEqual(p, P) self.assertEqualNormCase(str(p), self.base) - # Resolve relative paths. - try: - self.cls('').absolute() - except UnsupportedOperation: - return - old_path = os.getcwd() - os.chdir(self.base) - try: - p = self.cls('link0').resolve() - self.assertEqual(p, P) - self.assertEqualNormCase(str(p), self.base) - p = self.cls('link1').resolve() - self.assertEqual(p, P) - self.assertEqualNormCase(str(p), self.base) - p = self.cls('link2').resolve() - self.assertEqual(p, P) - self.assertEqualNormCase(str(p), self.base) - p = self.cls('link3').resolve() - self.assertEqual(p, P) - self.assertEqualNormCase(str(p), self.base) - finally: - os.chdir(old_path) - @needs_symlinks def test_complex_symlinks_absolute(self): self._check_complex_symlinks(self.base) From 9d08423b6e0fa89ce9cfea08e580ed72e5db8c70 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 9 Nov 2024 15:01:32 -0800 Subject: [PATCH 118/219] gh-117378: Fix multiprocessing forkserver preload sys.path inheritance. (GH-126538) gh-117378: Fix multiprocessing forkserver preload sys.path inheritance. `sys.path` was not properly being sent from the parent process when launching the multiprocessing forkserver process to preload imports. This bug has been there since the forkserver start method was introduced in Python 3.4. It was always _supposed_ to inherit `sys.path` the same way the spawn method does. Observable behavior change: A `''` value in `sys.path` will now be replaced in the forkserver's `sys.path` with an absolute pathname `os.path.abspath(os.getcwd())` saved at the time that `multiprocessing` was imported in the parent process as it already was when using the spawn start method. **This will only be observable during forkserver preload imports**. The code invoked before calling things in another process already correctly sets `sys.path`. Which is likely why this went unnoticed for so long as a mere performance issue in some configurations. A workaround for the bug on impacted Pythons is to set PYTHONPATH in the environment before multiprocessing's forkserver process was started. Not perfect as that is then inherited by other children, etc, but likely good enough for many people's purposes. Co-authored-by: Serhiy Storchaka --- Lib/multiprocessing/forkserver.py | 2 + Lib/test/_test_multiprocessing.py | 78 +++++++++++++++++++ ...-11-07-01-40-11.gh-issue-117378.o9O5uM.rst | 17 ++++ 3 files changed, 97 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2024-11-07-01-40-11.gh-issue-117378.o9O5uM.rst diff --git a/Lib/multiprocessing/forkserver.py b/Lib/multiprocessing/forkserver.py index 53b8c492675878..bff7fb91d974b3 100644 --- a/Lib/multiprocessing/forkserver.py +++ b/Lib/multiprocessing/forkserver.py @@ -168,6 +168,8 @@ def ensure_running(self): def main(listener_fd, alive_r, preload, main_path=None, sys_path=None): '''Run forkserver.''' if preload: + if sys_path is not None: + sys.path[:] = sys_path if '__main__' in preload and main_path is not None: process.current_process()._inheriting = True try: diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index 38ddb62c693fc0..328cd5112cab7a 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -12,6 +12,7 @@ import sys import os import gc +import importlib import errno import functools import signal @@ -20,8 +21,10 @@ import socket import random import logging +import shutil import subprocess import struct +import tempfile import operator import pickle import weakref @@ -6397,6 +6400,81 @@ def test_atexit(self): self.assertEqual(f.read(), 'deadbeef') +class _TestSpawnedSysPath(BaseTestCase): + """Test that sys.path is setup in forkserver and spawn processes.""" + + ALLOWED_TYPES = ('processes',) + + def setUp(self): + self._orig_sys_path = list(sys.path) + self._temp_dir = tempfile.mkdtemp(prefix="test_sys_path-") + self._mod_name = "unique_test_mod" + module_path = os.path.join(self._temp_dir, f"{self._mod_name}.py") + with open(module_path, "w", encoding="utf-8") as mod: + mod.write("# A simple test module\n") + sys.path[:] = [p for p in sys.path if p] # remove any existing ""s + sys.path.insert(0, self._temp_dir) + sys.path.insert(0, "") # Replaced with an abspath in child. + try: + self._ctx_forkserver = multiprocessing.get_context("forkserver") + except ValueError: + self._ctx_forkserver = None + self._ctx_spawn = multiprocessing.get_context("spawn") + + def tearDown(self): + sys.path[:] = self._orig_sys_path + shutil.rmtree(self._temp_dir, ignore_errors=True) + + @staticmethod + def enq_imported_module_names(queue): + queue.put(tuple(sys.modules)) + + def test_forkserver_preload_imports_sys_path(self): + ctx = self._ctx_forkserver + if not ctx: + self.skipTest("requires forkserver start method.") + self.assertNotIn(self._mod_name, sys.modules) + multiprocessing.forkserver._forkserver._stop() # Must be fresh. + ctx.set_forkserver_preload( + ["test.test_multiprocessing_forkserver", self._mod_name]) + q = ctx.Queue() + proc = ctx.Process(target=self.enq_imported_module_names, args=(q,)) + proc.start() + proc.join() + child_imported_modules = q.get() + q.close() + self.assertIn(self._mod_name, child_imported_modules) + + @staticmethod + def enq_sys_path_and_import(queue, mod_name): + queue.put(sys.path) + try: + importlib.import_module(mod_name) + except ImportError as exc: + queue.put(exc) + else: + queue.put(None) + + def test_child_sys_path(self): + for ctx in (self._ctx_spawn, self._ctx_forkserver): + if not ctx: + continue + with self.subTest(f"{ctx.get_start_method()} start method"): + q = ctx.Queue() + proc = ctx.Process(target=self.enq_sys_path_and_import, + args=(q, self._mod_name)) + proc.start() + proc.join() + child_sys_path = q.get() + import_error = q.get() + q.close() + self.assertNotIn("", child_sys_path) # replaced by an abspath + self.assertIn(self._temp_dir, child_sys_path) # our addition + # ignore the first element, it is the absolute "" replacement + self.assertEqual(child_sys_path[1:], sys.path[1:]) + self.assertIsNone(import_error, msg=f"child could not import {self._mod_name}") + + class MiscTestCase(unittest.TestCase): def test__all__(self): # Just make sure names in not_exported are excluded diff --git a/Misc/NEWS.d/next/Library/2024-11-07-01-40-11.gh-issue-117378.o9O5uM.rst b/Misc/NEWS.d/next/Library/2024-11-07-01-40-11.gh-issue-117378.o9O5uM.rst new file mode 100644 index 00000000000000..cdbe21f9f9a663 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-11-07-01-40-11.gh-issue-117378.o9O5uM.rst @@ -0,0 +1,17 @@ +Fixed the :mod:`multiprocessing` ``"forkserver"`` start method forkserver +process to correctly inherit the parent's :data:`sys.path` during the importing +of :func:`multiprocessing.set_forkserver_preload` modules in the same manner as +:data:`sys.path` is configured in workers before executing work items. + +This bug caused some forkserver module preloading to silently fail to preload. +This manifested as a performance degration in child processes when the +``sys.path`` was required due to additional repeated work in every worker. + +It could also have a side effect of ``""`` remaining in :data:`sys.path` during +forkserver preload imports instead of the absolute path from :func:`os.getcwd` +at multiprocessing import time used in the worker ``sys.path``. + +Potentially leading to incorrect imports from the wrong location during +preload. We are unaware of that actually happening. The issue was discovered +by someone observing unexpected preload performance gains. + From 450db61a78989c5a1f1106be01e071798c783cf9 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Sun, 10 Nov 2024 02:48:33 +0200 Subject: [PATCH 119/219] Postpone `module.__loader__` deprecation to Python 3.16 (#126482) --- Doc/deprecations/pending-removal-in-3.14.rst | 7 ------- Doc/deprecations/pending-removal-in-3.16.rst | 7 +++++++ Doc/reference/datamodel.rst | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Doc/deprecations/pending-removal-in-3.14.rst b/Doc/deprecations/pending-removal-in-3.14.rst index 1904465b856506..6159fa48848285 100644 --- a/Doc/deprecations/pending-removal-in-3.14.rst +++ b/Doc/deprecations/pending-removal-in-3.14.rst @@ -1,13 +1,6 @@ Pending removal in Python 3.14 ------------------------------ -* The import system: - - * Setting :attr:`~module.__loader__` on a module while - failing to set :attr:`__spec__.loader ` - is deprecated. In Python 3.14, :attr:`!__loader__` will cease to be set or - taken into consideration by the import system or the standard library. - * :mod:`argparse`: The *type*, *choices*, and *metavar* parameters of :class:`!argparse.BooleanOptionalAction` are deprecated and will be removed in 3.14. diff --git a/Doc/deprecations/pending-removal-in-3.16.rst b/Doc/deprecations/pending-removal-in-3.16.rst index fac500d34742ca..6f6954b783a1ae 100644 --- a/Doc/deprecations/pending-removal-in-3.16.rst +++ b/Doc/deprecations/pending-removal-in-3.16.rst @@ -1,6 +1,13 @@ Pending removal in Python 3.16 ------------------------------ +* The import system: + + * Setting :attr:`~module.__loader__` on a module while + failing to set :attr:`__spec__.loader ` + is deprecated. In Python 3.16, :attr:`!__loader__` will cease to be set or + taken into consideration by the import system or the standard library. + * :mod:`array`: * The ``'u'`` format code (:c:type:`wchar_t`) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 41133b92ed88ec..66b836eaf0008a 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -1028,9 +1028,9 @@ this approach. using the :class:`types.ModuleType` constructor. Previously the attribute was optional. - .. deprecated-removed:: 3.12 3.14 + .. deprecated-removed:: 3.12 3.16 Setting :attr:`!__loader__` on a module while failing to set - :attr:`!__spec__.loader` is deprecated. In Python 3.14, + :attr:`!__spec__.loader` is deprecated. In Python 3.16, :attr:`!__loader__` will cease to be set or taken into consideration by the import system or the standard library. From 160758a574d12bf0d965d8206136e7da4f4fd6c3 Mon Sep 17 00:00:00 2001 From: Jan Hicken Date: Sun, 10 Nov 2024 15:57:24 +0100 Subject: [PATCH 120/219] gh-126565: Skip `zipfile.Path.exists` check in write mode (#126576) When `zipfile.Path.open` is called, the implementation will check whether the path already exists in the ZIP file. However, this check is only required when the ZIP file is in read mode. By swapping arguments of the `and` operator, the short-circuiting will prevent the check from being run in write mode. This change will improve the performance of `open()`, because checking whether a file exists is slow in write mode, especially when the archive has many members. --- Lib/zipfile/_path/__init__.py | 2 +- .../next/Library/2024-11-08-11-06-14.gh-issue-126565.dFFO22.rst | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2024-11-08-11-06-14.gh-issue-126565.dFFO22.rst diff --git a/Lib/zipfile/_path/__init__.py b/Lib/zipfile/_path/__init__.py index c0e53e273cfaac..5ae16ec970dda4 100644 --- a/Lib/zipfile/_path/__init__.py +++ b/Lib/zipfile/_path/__init__.py @@ -339,7 +339,7 @@ def open(self, mode='r', *args, pwd=None, **kwargs): if self.is_dir(): raise IsADirectoryError(self) zip_mode = mode[0] - if not self.exists() and zip_mode == 'r': + if zip_mode == 'r' and not self.exists(): raise FileNotFoundError(self) stream = self.root.open(self.at, zip_mode, pwd=pwd) if 'b' in mode: diff --git a/Misc/NEWS.d/next/Library/2024-11-08-11-06-14.gh-issue-126565.dFFO22.rst b/Misc/NEWS.d/next/Library/2024-11-08-11-06-14.gh-issue-126565.dFFO22.rst new file mode 100644 index 00000000000000..22858570bbe03c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-11-08-11-06-14.gh-issue-126565.dFFO22.rst @@ -0,0 +1 @@ +Improve performances of :meth:`zipfile.Path.open` for non-reading modes. From 0f6bb28ff3ba152faf7523ea9aaf0094cc39bdda Mon Sep 17 00:00:00 2001 From: CF Bolz-Tereick Date: Sun, 10 Nov 2024 16:16:36 +0100 Subject: [PATCH 121/219] Skip test in test_socket.py if `sys.getrefcount` isn't available (#126640) Skip `testMakefileCloseSocketDestroy` test if `sys.getrefcount` isn't available. This is necessary for PyPy and other Python implementations that do not have `sys.getrefcount`. --- Lib/test/test_socket.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index f2bc52ba6e8701..dc8a4a44f37e65 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -5334,6 +5334,8 @@ def _testMakefileClose(self): self.write_file.write(self.write_msg) self.write_file.flush() + @unittest.skipUnless(hasattr(sys, 'getrefcount'), + 'test needs sys.getrefcount()') def testMakefileCloseSocketDestroy(self): refcount_before = sys.getrefcount(self.cli_conn) self.read_file.close() From ca878b6e45f9c7934842f7bb94274e671b155e09 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sun, 10 Nov 2024 13:17:05 -0800 Subject: [PATCH 122/219] gh-117378: Only run the new multiprocessing SysPath test when appropriate (GH-126635) The first version had it running two forkserver and one spawn tests underneath each of the _fork, _forkserver, and _spawn test suites that build off the generic one. This adds to the existing complexity of the multiprocessing test suite by offering BaseTestCase classes another attribute to control which suites they are invoked under. Practicality vs purity here. :/ Net result: we don't over-run the new test and their internal logic is simplified. --- Lib/test/_test_multiprocessing.py | 59 ++++++++++++++++--------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index 328cd5112cab7a..bcb024d8386fd1 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -258,6 +258,9 @@ def __call__(self, *args, **kwds): class BaseTestCase(object): ALLOWED_TYPES = ('processes', 'manager', 'threads') + # If not empty, limit which start method suites run this class. + START_METHODS: set[str] = set() + start_method = None # set by install_tests_in_module_dict() def assertTimingAlmostEqual(self, a, b): if CHECK_TIMINGS: @@ -6403,7 +6406,9 @@ def test_atexit(self): class _TestSpawnedSysPath(BaseTestCase): """Test that sys.path is setup in forkserver and spawn processes.""" - ALLOWED_TYPES = ('processes',) + ALLOWED_TYPES = {'processes'} + # Not applicable to fork which inherits everything from the process as is. + START_METHODS = {"forkserver", "spawn"} def setUp(self): self._orig_sys_path = list(sys.path) @@ -6415,11 +6420,8 @@ def setUp(self): sys.path[:] = [p for p in sys.path if p] # remove any existing ""s sys.path.insert(0, self._temp_dir) sys.path.insert(0, "") # Replaced with an abspath in child. - try: - self._ctx_forkserver = multiprocessing.get_context("forkserver") - except ValueError: - self._ctx_forkserver = None - self._ctx_spawn = multiprocessing.get_context("spawn") + self.assertIn(self.start_method, self.START_METHODS) + self._ctx = multiprocessing.get_context(self.start_method) def tearDown(self): sys.path[:] = self._orig_sys_path @@ -6430,15 +6432,15 @@ def enq_imported_module_names(queue): queue.put(tuple(sys.modules)) def test_forkserver_preload_imports_sys_path(self): - ctx = self._ctx_forkserver - if not ctx: - self.skipTest("requires forkserver start method.") + if self._ctx.get_start_method() != "forkserver": + self.skipTest("forkserver specific test.") self.assertNotIn(self._mod_name, sys.modules) multiprocessing.forkserver._forkserver._stop() # Must be fresh. - ctx.set_forkserver_preload( + self._ctx.set_forkserver_preload( ["test.test_multiprocessing_forkserver", self._mod_name]) - q = ctx.Queue() - proc = ctx.Process(target=self.enq_imported_module_names, args=(q,)) + q = self._ctx.Queue() + proc = self._ctx.Process( + target=self.enq_imported_module_names, args=(q,)) proc.start() proc.join() child_imported_modules = q.get() @@ -6456,23 +6458,19 @@ def enq_sys_path_and_import(queue, mod_name): queue.put(None) def test_child_sys_path(self): - for ctx in (self._ctx_spawn, self._ctx_forkserver): - if not ctx: - continue - with self.subTest(f"{ctx.get_start_method()} start method"): - q = ctx.Queue() - proc = ctx.Process(target=self.enq_sys_path_and_import, - args=(q, self._mod_name)) - proc.start() - proc.join() - child_sys_path = q.get() - import_error = q.get() - q.close() - self.assertNotIn("", child_sys_path) # replaced by an abspath - self.assertIn(self._temp_dir, child_sys_path) # our addition - # ignore the first element, it is the absolute "" replacement - self.assertEqual(child_sys_path[1:], sys.path[1:]) - self.assertIsNone(import_error, msg=f"child could not import {self._mod_name}") + q = self._ctx.Queue() + proc = self._ctx.Process( + target=self.enq_sys_path_and_import, args=(q, self._mod_name)) + proc.start() + proc.join() + child_sys_path = q.get() + import_error = q.get() + q.close() + self.assertNotIn("", child_sys_path) # replaced by an abspath + self.assertIn(self._temp_dir, child_sys_path) # our addition + # ignore the first element, it is the absolute "" replacement + self.assertEqual(child_sys_path[1:], sys.path[1:]) + self.assertIsNone(import_error, msg=f"child could not import {self._mod_name}") class MiscTestCase(unittest.TestCase): @@ -6669,6 +6667,8 @@ def install_tests_in_module_dict(remote_globs, start_method, if base is BaseTestCase: continue assert set(base.ALLOWED_TYPES) <= ALL_TYPES, base.ALLOWED_TYPES + if base.START_METHODS and start_method not in base.START_METHODS: + continue # class not intended for this start method. for type_ in base.ALLOWED_TYPES: if only_type and type_ != only_type: continue @@ -6682,6 +6682,7 @@ class Temp(base, Mixin, unittest.TestCase): Temp = hashlib_helper.requires_hashdigest('sha256')(Temp) Temp.__name__ = Temp.__qualname__ = newname Temp.__module__ = __module__ + Temp.start_method = start_method remote_globs[newname] = Temp elif issubclass(base, unittest.TestCase): if only_type: From f435de6765e0327995850d719534be38c9b5ec49 Mon Sep 17 00:00:00 2001 From: Kirill Podoprigora Date: Sun, 10 Nov 2024 23:44:56 +0200 Subject: [PATCH 123/219] gh-126647: `Doc/using/configure.rst`: Add an entry for ``--enable-experimental-jit`` option (#126648) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an entry for the ``--enable-experimental-jit`` option in ``Doc/using/configure.rst``. This was added as an experimental option in CPython 3.13. Possible values for it: * `no` - don't build the JIT. * `yes` - build the JIT. * `yes-off` - build the JIT but disable it by default. * `interpreter` - don't build the JIT but enable tier 2 interpreter instead. Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Doc/using/configure.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Doc/using/configure.rst b/Doc/using/configure.rst index 83994af795e3fc..5f1ee0c2a2e657 100644 --- a/Doc/using/configure.rst +++ b/Doc/using/configure.rst @@ -297,6 +297,19 @@ General Options .. versionadded:: 3.13 +.. option:: --enable-experimental-jit=[no|yes|yes-off|interpreter] + + Indicate how to integrate the :ref:`JIT compiler `. + + * ``no`` - build the interpreter without the JIT. + * ``yes`` - build the interpreter with the JIT. + * ``yes-off`` - build the interpreter with the JIT but disable it by default. + * ``interpreter`` - build the interpreter without the JIT, but with the tier 2 enabled interpreter. + + By convention, ``--enable-experimental-jit`` is a shorthand for ``--enable-experimental-jit=yes``. + + .. versionadded:: 3.13 + .. option:: PKG_CONFIG Path to ``pkg-config`` utility. From 434b29767f2fdef9f35c8e93303cf6aca4a66a80 Mon Sep 17 00:00:00 2001 From: Pedro Fonini Date: Sun, 10 Nov 2024 21:40:25 -0300 Subject: [PATCH 124/219] gh-126543: Docs: change "bound type var" to "bounded" when used in the context of the 'bound' kw argument to TypeVar (#126584) --- Doc/library/typing.rst | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index cd8b90854b0e94..0fee782121b0af 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -1726,11 +1726,11 @@ without the dedicated syntax, as documented below. class Sequence[T]: # T is a TypeVar ... - This syntax can also be used to create bound and constrained type + This syntax can also be used to create bounded and constrained type variables:: - class StrSequence[S: str]: # S is a TypeVar bound to str - ... + class StrSequence[S: str]: # S is a TypeVar with a `str` upper bound; + ... # we can say that S is "bounded by `str`" class StrOrBytesSequence[A: (str, bytes)]: # A is a TypeVar constrained to str or bytes @@ -1763,8 +1763,8 @@ without the dedicated syntax, as documented below. """Add two strings or bytes objects together.""" return x + y - Note that type variables can be *bound*, *constrained*, or neither, but - cannot be both bound *and* constrained. + Note that type variables can be *bounded*, *constrained*, or neither, but + cannot be both bounded *and* constrained. The variance of type variables is inferred by type checkers when they are created through the :ref:`type parameter syntax ` or when @@ -1774,8 +1774,8 @@ without the dedicated syntax, as documented below. By default, manually created type variables are invariant. See :pep:`484` and :pep:`695` for more details. - Bound type variables and constrained type variables have different - semantics in several important ways. Using a *bound* type variable means + Bounded type variables and constrained type variables have different + semantics in several important ways. Using a *bounded* type variable means that the ``TypeVar`` will be solved using the most specific type possible:: x = print_capitalized('a string') @@ -1789,8 +1789,8 @@ without the dedicated syntax, as documented below. z = print_capitalized(45) # error: int is not a subtype of str - Type variables can be bound to concrete types, abstract types (ABCs or - protocols), and even unions of types:: + The upper bound of a type variable can be a concrete type, abstract type + (ABC or Protocol), or even a union of types:: # Can be anything with an __abs__ method def print_abs[T: SupportsAbs](arg: T) -> None: @@ -1834,7 +1834,7 @@ without the dedicated syntax, as documented below. .. attribute:: __bound__ - The bound of the type variable, if any. + The upper bound of the type variable, if any. .. versionchanged:: 3.12 @@ -2100,7 +2100,7 @@ without the dedicated syntax, as documented below. return x + y Without ``ParamSpec``, the simplest way to annotate this previously was to - use a :class:`TypeVar` with bound ``Callable[..., Any]``. However this + use a :class:`TypeVar` with upper bound ``Callable[..., Any]``. However this causes two problems: 1. The type checker can't type check the ``inner`` function because From 5c488caeb858690a696bc9f74fc74a274a3aa51c Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sun, 10 Nov 2024 22:39:58 -0800 Subject: [PATCH 125/219] gh-117378: Clear up the NEWS entry wording (GH-126634) gh-117378: Clear up the NEWS entry wording. Docs are hard. Lets go shopping! --- .../Library/2024-11-07-01-40-11.gh-issue-117378.o9O5uM.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Misc/NEWS.d/next/Library/2024-11-07-01-40-11.gh-issue-117378.o9O5uM.rst b/Misc/NEWS.d/next/Library/2024-11-07-01-40-11.gh-issue-117378.o9O5uM.rst index cdbe21f9f9a663..d7d4477ec17814 100644 --- a/Misc/NEWS.d/next/Library/2024-11-07-01-40-11.gh-issue-117378.o9O5uM.rst +++ b/Misc/NEWS.d/next/Library/2024-11-07-01-40-11.gh-issue-117378.o9O5uM.rst @@ -11,7 +11,7 @@ It could also have a side effect of ``""`` remaining in :data:`sys.path` during forkserver preload imports instead of the absolute path from :func:`os.getcwd` at multiprocessing import time used in the worker ``sys.path``. -Potentially leading to incorrect imports from the wrong location during -preload. We are unaware of that actually happening. The issue was discovered -by someone observing unexpected preload performance gains. +The ``sys.path`` differences between phases in the child process could +potentially have caused preload to import incorrect things from the wrong +location. We are unaware of that actually having happened in practice. From 25257d61cfccc3b4189f96390a5c4db73fd5302c Mon Sep 17 00:00:00 2001 From: vivodi <103735539+vivodi@users.noreply.github.com> Date: Mon, 11 Nov 2024 14:47:56 +0800 Subject: [PATCH 126/219] gh-126664: Use `else` instead of `finally` in "The with statement" documentation. (GH-126665) --- Doc/reference/compound_stmts.rst | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index 1b1e9f479cbe08..e73ce44270b082 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -534,18 +534,15 @@ is semantically equivalent to:: enter = type(manager).__enter__ exit = type(manager).__exit__ value = enter(manager) - hit_except = False try: TARGET = value SUITE except: - hit_except = True if not exit(manager, *sys.exc_info()): raise - finally: - if not hit_except: - exit(manager, None, None, None) + else: + exit(manager, None, None, None) With more than one item, the context managers are processed as if multiple :keyword:`with` statements were nested:: From 82269c7d580e1aad71ff11fe891cf7f97eb45703 Mon Sep 17 00:00:00 2001 From: Rafael Fontenelle Date: Mon, 11 Nov 2024 03:59:23 -0300 Subject: [PATCH 127/219] Add missing fullstop `.` to whatsnew/3.8.rst (GH-126553) --- Doc/whatsnew/3.8.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst index fc9f49e65af847..bdc4ca5cab5245 100644 --- a/Doc/whatsnew/3.8.rst +++ b/Doc/whatsnew/3.8.rst @@ -936,7 +936,7 @@ Add option ``--json-lines`` to parse every input line as a separate JSON object. logging ------- -Added a *force* keyword argument to :func:`logging.basicConfig` +Added a *force* keyword argument to :func:`logging.basicConfig`. When set to true, any existing handlers attached to the root logger are removed and closed before carrying out the configuration specified by the other arguments. From 6ee542d491589b470ec7cdd353463ff9ff52d098 Mon Sep 17 00:00:00 2001 From: Stephen Morton Date: Sun, 10 Nov 2024 23:08:58 -0800 Subject: [PATCH 128/219] gh-126417: validate ABC methods on multiprocessing proxy types (#126454) Checks that appropriate dunder __ methods exist on the dict and list proxy types. Co-authored-by: Alex Waygood --- Lib/test/_test_multiprocessing.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index bcb024d8386fd1..8329a848a90088 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -2464,6 +2464,19 @@ def test_list_isinstance(self): a = self.list() self.assertIsInstance(a, collections.abc.MutableSequence) + # MutableSequence also has __iter__, but we can iterate over + # ListProxy using __getitem__ instead. Adding __iter__ to ListProxy + # would change the behavior of a list modified during iteration. + mutable_sequence_methods = ( + '__contains__', '__delitem__', '__getitem__', '__iadd__', + '__len__', '__reversed__', '__setitem__', 'append', + 'clear', 'count', 'extend', 'index', 'insert', 'pop', 'remove', + 'reverse', + ) + for name in mutable_sequence_methods: + with self.subTest(name=name): + self.assertTrue(callable(getattr(a, name))) + def test_list_iter(self): a = self.list(list(range(10))) it = iter(a) @@ -2508,6 +2521,15 @@ def test_dict_isinstance(self): a = self.dict() self.assertIsInstance(a, collections.abc.MutableMapping) + mutable_mapping_methods = ( + '__contains__', '__delitem__', '__eq__', '__getitem__', '__iter__', + '__len__', '__ne__', '__setitem__', 'clear', 'get', 'items', + 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values', + ) + for name in mutable_mapping_methods: + with self.subTest(name=name): + self.assertTrue(callable(getattr(a, name))) + def test_dict_iter(self): d = self.dict() indices = list(range(65, 70)) From 9fc2808eaf4e74a9f52f44d20a7d1110bd949d41 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Mon, 11 Nov 2024 14:35:56 +0300 Subject: [PATCH 129/219] gh-126654: Fix crash in several functions in `_interpreters` module (#126678) --- Lib/test/test__interpreters.py | 18 ++++++++++++++++++ ...4-11-11-13-00-21.gh-issue-126654.4gfP2y.rst | 2 ++ Modules/_interpretersmodule.c | 5 +++++ 3 files changed, 25 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2024-11-11-13-00-21.gh-issue-126654.4gfP2y.rst diff --git a/Lib/test/test__interpreters.py b/Lib/test/test__interpreters.py index 14cd50bd30502c..bf3165e2341949 100644 --- a/Lib/test/test__interpreters.py +++ b/Lib/test/test__interpreters.py @@ -551,6 +551,24 @@ def test_still_running(self): self.assertTrue(_interpreters.is_running(interp)) +class CommonTests(TestBase): + def setUp(self): + super().setUp() + self.id = _interpreters.create() + + def test_signatures(self): + # for method in ['exec', 'run_string', 'run_func']: + msg = "expected 'shared' to be a dict" + with self.assertRaisesRegex(TypeError, msg): + _interpreters.exec(self.id, 'a', 1) + with self.assertRaisesRegex(TypeError, msg): + _interpreters.exec(self.id, 'a', shared=1) + with self.assertRaisesRegex(TypeError, msg): + _interpreters.run_string(self.id, 'a', shared=1) + with self.assertRaisesRegex(TypeError, msg): + _interpreters.run_func(self.id, lambda: None, shared=1) + + class RunStringTests(TestBase): def setUp(self): diff --git a/Misc/NEWS.d/next/Library/2024-11-11-13-00-21.gh-issue-126654.4gfP2y.rst b/Misc/NEWS.d/next/Library/2024-11-11-13-00-21.gh-issue-126654.4gfP2y.rst new file mode 100644 index 00000000000000..750158e6d4d3ae --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-11-11-13-00-21.gh-issue-126654.4gfP2y.rst @@ -0,0 +1,2 @@ +Fix crash when non-dict was passed to several functions in ``_interpreters`` +module. diff --git a/Modules/_interpretersmodule.c b/Modules/_interpretersmodule.c index 95acdd69e53260..a9a966e79e0920 100644 --- a/Modules/_interpretersmodule.c +++ b/Modules/_interpretersmodule.c @@ -936,6 +936,11 @@ static int _interp_exec(PyObject *self, PyInterpreterState *interp, PyObject *code_arg, PyObject *shared_arg, PyObject **p_excinfo) { + if (shared_arg != NULL && !PyDict_CheckExact(shared_arg)) { + PyErr_SetString(PyExc_TypeError, "expected 'shared' to be a dict"); + return -1; + } + // Extract code. Py_ssize_t codestrlen = -1; PyObject *bytes_obj = NULL; From 819830f34a11ecaa3aada174ca8eedeb3f260630 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 11 Nov 2024 18:27:26 +0200 Subject: [PATCH 130/219] gh-126505: Fix bugs in compiling case-insensitive character classes (GH-126557) * upper-case non-BMP character was ignored * the ASCII flag was ignored when matching a character range whose upper bound is beyond the BMP region --- Lib/re/_compiler.py | 23 +++++--- Lib/test/test_re.py | 55 +++++++++++++++++++ ...-11-07-22-41-47.gh-issue-126505.iztYE1.rst | 4 ++ 3 files changed, 73 insertions(+), 9 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-11-07-22-41-47.gh-issue-126505.iztYE1.rst diff --git a/Lib/re/_compiler.py b/Lib/re/_compiler.py index 29109f8812ee7b..20dd561d1c1520 100644 --- a/Lib/re/_compiler.py +++ b/Lib/re/_compiler.py @@ -255,11 +255,11 @@ def _optimize_charset(charset, iscased=None, fixup=None, fixes=None): while True: try: if op is LITERAL: - if fixup: - lo = fixup(av) - charmap[lo] = 1 - if fixes and lo in fixes: - for k in fixes[lo]: + if fixup: # IGNORECASE and not LOCALE + av = fixup(av) + charmap[av] = 1 + if fixes and av in fixes: + for k in fixes[av]: charmap[k] = 1 if not hascased and iscased(av): hascased = True @@ -267,7 +267,7 @@ def _optimize_charset(charset, iscased=None, fixup=None, fixes=None): charmap[av] = 1 elif op is RANGE: r = range(av[0], av[1]+1) - if fixup: + if fixup: # IGNORECASE and not LOCALE if fixes: for i in map(fixup, r): charmap[i] = 1 @@ -298,8 +298,7 @@ def _optimize_charset(charset, iscased=None, fixup=None, fixes=None): # Character set contains non-BMP character codes. # For range, all BMP characters in the range are already # proceeded. - if fixup: - hascased = True + if fixup: # IGNORECASE and not LOCALE # For now, IN_UNI_IGNORE+LITERAL and # IN_UNI_IGNORE+RANGE_UNI_IGNORE work for all non-BMP # characters, because two characters (at least one of @@ -310,7 +309,13 @@ def _optimize_charset(charset, iscased=None, fixup=None, fixes=None): # Also, both c.lower() and c.lower().upper() are single # characters for every non-BMP character. if op is RANGE: - op = RANGE_UNI_IGNORE + if fixes: # not ASCII + op = RANGE_UNI_IGNORE + hascased = True + else: + assert op is LITERAL + if not hascased and iscased(av): + hascased = True tail.append((op, av)) break diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py index ff95f54026e172..7bc702ec89a4a7 100644 --- a/Lib/test/test_re.py +++ b/Lib/test/test_re.py @@ -1136,6 +1136,39 @@ def test_ignore_case_set(self): self.assertTrue(re.match(br'[19a]', b'a', re.I)) self.assertTrue(re.match(br'[19a]', b'A', re.I)) self.assertTrue(re.match(br'[19A]', b'a', re.I)) + self.assertTrue(re.match(r'[19\xc7]', '\xc7', re.I)) + self.assertTrue(re.match(r'[19\xc7]', '\xe7', re.I)) + self.assertTrue(re.match(r'[19\xe7]', '\xc7', re.I)) + self.assertTrue(re.match(r'[19\xe7]', '\xe7', re.I)) + self.assertTrue(re.match(r'[19\u0400]', '\u0400', re.I)) + self.assertTrue(re.match(r'[19\u0400]', '\u0450', re.I)) + self.assertTrue(re.match(r'[19\u0450]', '\u0400', re.I)) + self.assertTrue(re.match(r'[19\u0450]', '\u0450', re.I)) + self.assertTrue(re.match(r'[19\U00010400]', '\U00010400', re.I)) + self.assertTrue(re.match(r'[19\U00010400]', '\U00010428', re.I)) + self.assertTrue(re.match(r'[19\U00010428]', '\U00010400', re.I)) + self.assertTrue(re.match(r'[19\U00010428]', '\U00010428', re.I)) + + self.assertTrue(re.match(br'[19A]', b'A', re.I)) + self.assertTrue(re.match(br'[19a]', b'a', re.I)) + self.assertTrue(re.match(br'[19a]', b'A', re.I)) + self.assertTrue(re.match(br'[19A]', b'a', re.I)) + self.assertTrue(re.match(r'[19A]', 'A', re.I|re.A)) + self.assertTrue(re.match(r'[19a]', 'a', re.I|re.A)) + self.assertTrue(re.match(r'[19a]', 'A', re.I|re.A)) + self.assertTrue(re.match(r'[19A]', 'a', re.I|re.A)) + self.assertTrue(re.match(r'[19\xc7]', '\xc7', re.I|re.A)) + self.assertIsNone(re.match(r'[19\xc7]', '\xe7', re.I|re.A)) + self.assertIsNone(re.match(r'[19\xe7]', '\xc7', re.I|re.A)) + self.assertTrue(re.match(r'[19\xe7]', '\xe7', re.I|re.A)) + self.assertTrue(re.match(r'[19\u0400]', '\u0400', re.I|re.A)) + self.assertIsNone(re.match(r'[19\u0400]', '\u0450', re.I|re.A)) + self.assertIsNone(re.match(r'[19\u0450]', '\u0400', re.I|re.A)) + self.assertTrue(re.match(r'[19\u0450]', '\u0450', re.I|re.A)) + self.assertTrue(re.match(r'[19\U00010400]', '\U00010400', re.I|re.A)) + self.assertIsNone(re.match(r'[19\U00010400]', '\U00010428', re.I|re.A)) + self.assertIsNone(re.match(r'[19\U00010428]', '\U00010400', re.I|re.A)) + self.assertTrue(re.match(r'[19\U00010428]', '\U00010428', re.I|re.A)) # Two different characters have the same lowercase. assert 'K'.lower() == '\u212a'.lower() == 'k' # 'K' @@ -1172,8 +1205,10 @@ def test_ignore_case_range(self): self.assertTrue(re.match(br'[9-a]', b'_', re.I)) self.assertIsNone(re.match(br'[9-A]', b'_', re.I)) self.assertTrue(re.match(r'[\xc0-\xde]', '\xd7', re.I)) + self.assertTrue(re.match(r'[\xc0-\xde]', '\xe7', re.I)) self.assertIsNone(re.match(r'[\xc0-\xde]', '\xf7', re.I)) self.assertTrue(re.match(r'[\xe0-\xfe]', '\xf7', re.I)) + self.assertTrue(re.match(r'[\xe0-\xfe]', '\xc7', re.I)) self.assertIsNone(re.match(r'[\xe0-\xfe]', '\xd7', re.I)) self.assertTrue(re.match(r'[\u0430-\u045f]', '\u0450', re.I)) self.assertTrue(re.match(r'[\u0430-\u045f]', '\u0400', re.I)) @@ -1184,6 +1219,26 @@ def test_ignore_case_range(self): self.assertTrue(re.match(r'[\U00010400-\U00010427]', '\U00010428', re.I)) self.assertTrue(re.match(r'[\U00010400-\U00010427]', '\U00010400', re.I)) + self.assertTrue(re.match(r'[\xc0-\xde]', '\xd7', re.I|re.A)) + self.assertIsNone(re.match(r'[\xc0-\xde]', '\xe7', re.I|re.A)) + self.assertTrue(re.match(r'[\xe0-\xfe]', '\xf7', re.I|re.A)) + self.assertIsNone(re.match(r'[\xe0-\xfe]', '\xc7', re.I|re.A)) + self.assertTrue(re.match(r'[\u0430-\u045f]', '\u0450', re.I|re.A)) + self.assertIsNone(re.match(r'[\u0430-\u045f]', '\u0400', re.I|re.A)) + self.assertIsNone(re.match(r'[\u0400-\u042f]', '\u0450', re.I|re.A)) + self.assertTrue(re.match(r'[\u0400-\u042f]', '\u0400', re.I|re.A)) + self.assertTrue(re.match(r'[\U00010428-\U0001044f]', '\U00010428', re.I|re.A)) + self.assertIsNone(re.match(r'[\U00010428-\U0001044f]', '\U00010400', re.I|re.A)) + self.assertIsNone(re.match(r'[\U00010400-\U00010427]', '\U00010428', re.I|re.A)) + self.assertTrue(re.match(r'[\U00010400-\U00010427]', '\U00010400', re.I|re.A)) + + self.assertTrue(re.match(r'[N-\x7f]', 'A', re.I|re.A)) + self.assertTrue(re.match(r'[n-\x7f]', 'Z', re.I|re.A)) + self.assertTrue(re.match(r'[N-\uffff]', 'A', re.I|re.A)) + self.assertTrue(re.match(r'[n-\uffff]', 'Z', re.I|re.A)) + self.assertTrue(re.match(r'[N-\U00010000]', 'A', re.I|re.A)) + self.assertTrue(re.match(r'[n-\U00010000]', 'Z', re.I|re.A)) + # Two different characters have the same lowercase. assert 'K'.lower() == '\u212a'.lower() == 'k' # 'K' self.assertTrue(re.match(r'[J-M]', '\u212a', re.I)) diff --git a/Misc/NEWS.d/next/Library/2024-11-07-22-41-47.gh-issue-126505.iztYE1.rst b/Misc/NEWS.d/next/Library/2024-11-07-22-41-47.gh-issue-126505.iztYE1.rst new file mode 100644 index 00000000000000..0a0f893a2688a0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-11-07-22-41-47.gh-issue-126505.iztYE1.rst @@ -0,0 +1,4 @@ +Fix bugs in compiling case-insensitive :mod:`regular expressions ` with +character classes containing non-BMP characters: upper-case non-BMP +character did was ignored and the ASCII flag was ignored when +matching a character range whose upper bound is beyond the BMP region. From 79805d228440814c0674ab5190ef17f235503d2e Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 11 Nov 2024 18:28:30 +0200 Subject: [PATCH 131/219] gh-117941: Reject option names starting with "--no-" in argparse.BooleanOptionalAction (GH-125894) They never worked correctly. --- Lib/argparse.py | 3 +++ Lib/test/test_argparse.py | 7 +++++++ .../Library/2024-10-23-20-44-30.gh-issue-117941.Y9jdlW.rst | 2 ++ 3 files changed, 12 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2024-10-23-20-44-30.gh-issue-117941.Y9jdlW.rst diff --git a/Lib/argparse.py b/Lib/argparse.py index 072cd5e7dc0d06..5ecfdca17175e3 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -863,6 +863,9 @@ def __init__(self, _option_strings.append(option_string) if option_string.startswith('--'): + if option_string.startswith('--no-'): + raise ValueError(f'invalid option name {option_string!r} ' + f'for BooleanOptionalAction') option_string = '--no-' + option_string[2:] _option_strings.append(option_string) diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index ba9876570385d3..cbf119ed2dabbb 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -789,6 +789,13 @@ def test_const(self): self.assertIn("got an unexpected keyword argument 'const'", str(cm.exception)) + def test_invalid_name(self): + parser = argparse.ArgumentParser() + with self.assertRaises(ValueError) as cm: + parser.add_argument('--no-foo', action=argparse.BooleanOptionalAction) + self.assertEqual(str(cm.exception), + "invalid option name '--no-foo' for BooleanOptionalAction") + class TestBooleanOptionalActionRequired(ParserTestCase): """Tests BooleanOptionalAction required""" diff --git a/Misc/NEWS.d/next/Library/2024-10-23-20-44-30.gh-issue-117941.Y9jdlW.rst b/Misc/NEWS.d/next/Library/2024-10-23-20-44-30.gh-issue-117941.Y9jdlW.rst new file mode 100644 index 00000000000000..9c2553f0f0e8cd --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-10-23-20-44-30.gh-issue-117941.Y9jdlW.rst @@ -0,0 +1,2 @@ +:class:`!argparse.BooleanOptionalAction` now rejects option names starting +with ``--no-``. From 25aee21aa84061b5d8e08247c8581da1459f37e8 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 11 Nov 2024 18:29:28 +0200 Subject: [PATCH 132/219] gh-126374: Add support of options with optional arguments in the getopt module (GH-126375) --- Doc/library/getopt.rst | 26 +++++- Doc/whatsnew/3.14.rst | 5 ++ Lib/getopt.py | 24 ++++-- Lib/test/test_getopt.py | 81 +++++++++++++++---- ...-11-03-23-25-07.gh-issue-126374.Xu_THP.rst | 1 + 5 files changed, 112 insertions(+), 25 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-11-03-23-25-07.gh-issue-126374.Xu_THP.rst diff --git a/Doc/library/getopt.rst b/Doc/library/getopt.rst index 3ab44b9fc56108..def0ea357bceb2 100644 --- a/Doc/library/getopt.rst +++ b/Doc/library/getopt.rst @@ -38,7 +38,8 @@ exception: be parsed, without the leading reference to the running program. Typically, this means ``sys.argv[1:]``. *shortopts* is the string of option letters that the script wants to recognize, with options that require an argument followed by a - colon (``':'``; i.e., the same format that Unix :c:func:`!getopt` uses). + colon (``':'``) and options that accept an optional argument followed by + two colons (``'::'``); i.e., the same format that Unix :c:func:`!getopt` uses. .. note:: @@ -49,8 +50,10 @@ exception: *longopts*, if specified, must be a list of strings with the names of the long options which should be supported. The leading ``'--'`` characters should not be included in the option name. Long options which require an - argument should be followed by an equal sign (``'='``). Optional arguments - are not supported. To accept only long options, *shortopts* should be an + argument should be followed by an equal sign (``'='``). + Long options which accept an optional argument should be followed by + an equal sign and question mark (``'=?'``). + To accept only long options, *shortopts* should be an empty string. Long options on the command line can be recognized so long as they provide a prefix of the option name that matches exactly one of the accepted options. For example, if *longopts* is ``['foo', 'frob']``, the @@ -67,6 +70,9 @@ exception: options occur in the list in the same order in which they were found, thus allowing multiple occurrences. Long and short options may be mixed. + .. versionchanged:: 3.14 + Optional arguments are supported. + .. function:: gnu_getopt(args, shortopts, longopts=[]) @@ -124,6 +130,20 @@ Using long option names is equally easy: >>> args ['a1', 'a2'] +Optional arguments should be specified explicitly: + +.. doctest:: + + >>> s = '-Con -C --color=off --color a1 a2' + >>> args = s.split() + >>> args + ['-Con', '-C', '--color=off', '--color', 'a1', 'a2'] + >>> optlist, args = getopt.getopt(args, 'C::', ['color=?']) + >>> optlist + [('-C', 'on'), ('-C', ''), ('--color', 'off'), ('--color', '')] + >>> args + ['a1', 'a2'] + In a script, typical usage is something like this: .. testcode:: diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index b9d2c27eb9a321..4b2a64fd0fab9c 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -314,6 +314,11 @@ functools to reserve a place for positional arguments. (Contributed by Dominykas Grigonis in :gh:`119127`.) +getopt +------ + +* Add support for options with optional arguments. + (Contributed by Serhiy Storchaka in :gh:`126374`.) http ---- diff --git a/Lib/getopt.py b/Lib/getopt.py index 1df5b96472a45c..c12c08b29c79c4 100644 --- a/Lib/getopt.py +++ b/Lib/getopt.py @@ -27,7 +27,6 @@ # - allow the caller to specify ordering # - RETURN_IN_ORDER option # - GNU extension with '-' as first character of option string -# - optional arguments, specified by double colons # - an option string with a W followed by semicolon should # treat "-W foo" as "--foo" @@ -58,12 +57,14 @@ def getopt(args, shortopts, longopts = []): running program. Typically, this means "sys.argv[1:]". shortopts is the string of option letters that the script wants to recognize, with options that require an argument followed by a - colon (i.e., the same format that Unix getopt() uses). If + colon and options that accept an optional argument followed by + two colons (i.e., the same format that Unix getopt() uses). If specified, longopts is a list of strings with the names of the long options which should be supported. The leading '--' characters should not be included in the option name. Options which require an argument should be followed by an equal sign - ('='). + ('='). Options which acept an optional argument should be + followed by an equal sign and question mark ('=?'). The return value consists of two elements: the first is a list of (option, value) pairs; the second is the list of program arguments @@ -153,7 +154,7 @@ def do_longs(opts, opt, longopts, args): has_arg, opt = long_has_args(opt, longopts) if has_arg: - if optarg is None: + if optarg is None and has_arg != '?': if not args: raise GetoptError(_('option --%s requires argument') % opt, opt) optarg, args = args[0], args[1:] @@ -174,6 +175,8 @@ def long_has_args(opt, longopts): return False, opt elif opt + '=' in possibilities: return True, opt + elif opt + '=?' in possibilities: + return '?', opt # No exact match, so better be unique. if len(possibilities) > 1: # XXX since possibilities contains all valid continuations, might be @@ -181,6 +184,8 @@ def long_has_args(opt, longopts): raise GetoptError(_('option --%s not a unique prefix') % opt, opt) assert len(possibilities) == 1 unique_match = possibilities[0] + if unique_match.endswith('=?'): + return '?', unique_match[:-2] has_arg = unique_match.endswith('=') if has_arg: unique_match = unique_match[:-1] @@ -189,8 +194,9 @@ def long_has_args(opt, longopts): def do_shorts(opts, optstring, shortopts, args): while optstring != '': opt, optstring = optstring[0], optstring[1:] - if short_has_arg(opt, shortopts): - if optstring == '': + has_arg = short_has_arg(opt, shortopts) + if has_arg: + if optstring == '' and has_arg != '?': if not args: raise GetoptError(_('option -%s requires argument') % opt, opt) @@ -204,7 +210,11 @@ def do_shorts(opts, optstring, shortopts, args): def short_has_arg(opt, shortopts): for i in range(len(shortopts)): if opt == shortopts[i] != ':': - return shortopts.startswith(':', i+1) + if not shortopts.startswith(':', i+1): + return False + if shortopts.startswith('::', i+1): + return '?' + return True raise GetoptError(_('option -%s not recognized') % opt, opt) if __name__ == '__main__': diff --git a/Lib/test/test_getopt.py b/Lib/test/test_getopt.py index c8b3442de4aa77..984bdb73f34aa4 100644 --- a/Lib/test/test_getopt.py +++ b/Lib/test/test_getopt.py @@ -19,21 +19,34 @@ def assertError(self, *args, **kwargs): self.assertRaises(getopt.GetoptError, *args, **kwargs) def test_short_has_arg(self): - self.assertTrue(getopt.short_has_arg('a', 'a:')) - self.assertFalse(getopt.short_has_arg('a', 'a')) + self.assertIs(getopt.short_has_arg('a', 'a:'), True) + self.assertIs(getopt.short_has_arg('a', 'a'), False) + self.assertEqual(getopt.short_has_arg('a', 'a::'), '?') self.assertError(getopt.short_has_arg, 'a', 'b') def test_long_has_args(self): has_arg, option = getopt.long_has_args('abc', ['abc=']) - self.assertTrue(has_arg) + self.assertIs(has_arg, True) self.assertEqual(option, 'abc') has_arg, option = getopt.long_has_args('abc', ['abc']) - self.assertFalse(has_arg) + self.assertIs(has_arg, False) self.assertEqual(option, 'abc') + has_arg, option = getopt.long_has_args('abc', ['abc=?']) + self.assertEqual(has_arg, '?') + self.assertEqual(option, 'abc') + + has_arg, option = getopt.long_has_args('abc', ['abcd=']) + self.assertIs(has_arg, True) + self.assertEqual(option, 'abcd') + has_arg, option = getopt.long_has_args('abc', ['abcd']) - self.assertFalse(has_arg) + self.assertIs(has_arg, False) + self.assertEqual(option, 'abcd') + + has_arg, option = getopt.long_has_args('abc', ['abcd=?']) + self.assertEqual(has_arg, '?') self.assertEqual(option, 'abcd') self.assertError(getopt.long_has_args, 'abc', ['def']) @@ -49,9 +62,9 @@ def test_do_shorts(self): self.assertEqual(opts, [('-a', '1')]) self.assertEqual(args, []) - #opts, args = getopt.do_shorts([], 'a=1', 'a:', []) - #self.assertEqual(opts, [('-a', '1')]) - #self.assertEqual(args, []) + opts, args = getopt.do_shorts([], 'a=1', 'a:', []) + self.assertEqual(opts, [('-a', '=1')]) + self.assertEqual(args, []) opts, args = getopt.do_shorts([], 'a', 'a:', ['1']) self.assertEqual(opts, [('-a', '1')]) @@ -61,6 +74,14 @@ def test_do_shorts(self): self.assertEqual(opts, [('-a', '1')]) self.assertEqual(args, ['2']) + opts, args = getopt.do_shorts([], 'a', 'a::', ['1']) + self.assertEqual(opts, [('-a', '')]) + self.assertEqual(args, ['1']) + + opts, args = getopt.do_shorts([], 'a1', 'a::', []) + self.assertEqual(opts, [('-a', '1')]) + self.assertEqual(args, []) + self.assertError(getopt.do_shorts, [], 'a1', 'a', []) self.assertError(getopt.do_shorts, [], 'a', 'a:', []) @@ -77,6 +98,22 @@ def test_do_longs(self): self.assertEqual(opts, [('--abcd', '1')]) self.assertEqual(args, []) + opts, args = getopt.do_longs([], 'abc', ['abc=?'], ['1']) + self.assertEqual(opts, [('--abc', '')]) + self.assertEqual(args, ['1']) + + opts, args = getopt.do_longs([], 'abc', ['abcd=?'], ['1']) + self.assertEqual(opts, [('--abcd', '')]) + self.assertEqual(args, ['1']) + + opts, args = getopt.do_longs([], 'abc=1', ['abc=?'], []) + self.assertEqual(opts, [('--abc', '1')]) + self.assertEqual(args, []) + + opts, args = getopt.do_longs([], 'abc=1', ['abcd=?'], []) + self.assertEqual(opts, [('--abcd', '1')]) + self.assertEqual(args, []) + opts, args = getopt.do_longs([], 'abc', ['ab', 'abc', 'abcd'], []) self.assertEqual(opts, [('--abc', '')]) self.assertEqual(args, []) @@ -95,7 +132,7 @@ def test_getopt(self): # note: the empty string between '-a' and '--beta' is significant: # it simulates an empty string option argument ('-a ""') on the # command line. - cmdline = ['-a', '1', '-b', '--alpha=2', '--beta', '-a', '3', '-a', + cmdline = ['-a1', '-b', '--alpha=2', '--beta', '-a', '3', '-a', '', '--beta', 'arg1', 'arg2'] opts, args = getopt.getopt(cmdline, 'a:b', ['alpha=', 'beta']) @@ -106,17 +143,29 @@ def test_getopt(self): # accounted for in the code that calls getopt(). self.assertEqual(args, ['arg1', 'arg2']) + cmdline = ['-a1', '--alpha=2', '--alpha=', '-a', '--alpha', 'arg1', 'arg2'] + opts, args = getopt.getopt(cmdline, 'a::', ['alpha=?']) + self.assertEqual(opts, [('-a', '1'), ('--alpha', '2'), ('--alpha', ''), + ('-a', ''), ('--alpha', '')]) + self.assertEqual(args, ['arg1', 'arg2']) + self.assertError(getopt.getopt, cmdline, 'a:b', ['alpha', 'beta']) def test_gnu_getopt(self): # Test handling of GNU style scanning mode. - cmdline = ['-a', 'arg1', '-b', '1', '--alpha', '--beta=2'] + cmdline = ['-a', 'arg1', '-b', '1', '--alpha', '--beta=2', '--beta', + '3', 'arg2'] # GNU style opts, args = getopt.gnu_getopt(cmdline, 'ab:', ['alpha', 'beta=']) - self.assertEqual(args, ['arg1']) - self.assertEqual(opts, [('-a', ''), ('-b', '1'), - ('--alpha', ''), ('--beta', '2')]) + self.assertEqual(args, ['arg1', 'arg2']) + self.assertEqual(opts, [('-a', ''), ('-b', '1'), ('--alpha', ''), + ('--beta', '2'), ('--beta', '3')]) + + opts, args = getopt.gnu_getopt(cmdline, 'ab::', ['alpha', 'beta=?']) + self.assertEqual(args, ['arg1', '1', '3', 'arg2']) + self.assertEqual(opts, [('-a', ''), ('-b', ''), ('--alpha', ''), + ('--beta', '2'), ('--beta', '')]) # recognize "-" as an argument opts, args = getopt.gnu_getopt(['-a', '-', '-b', '-'], 'ab:', []) @@ -126,13 +175,15 @@ def test_gnu_getopt(self): # Posix style via + opts, args = getopt.gnu_getopt(cmdline, '+ab:', ['alpha', 'beta=']) self.assertEqual(opts, [('-a', '')]) - self.assertEqual(args, ['arg1', '-b', '1', '--alpha', '--beta=2']) + self.assertEqual(args, ['arg1', '-b', '1', '--alpha', '--beta=2', + '--beta', '3', 'arg2']) # Posix style via POSIXLY_CORRECT self.env["POSIXLY_CORRECT"] = "1" opts, args = getopt.gnu_getopt(cmdline, 'ab:', ['alpha', 'beta=']) self.assertEqual(opts, [('-a', '')]) - self.assertEqual(args, ['arg1', '-b', '1', '--alpha', '--beta=2']) + self.assertEqual(args, ['arg1', '-b', '1', '--alpha', '--beta=2', + '--beta', '3', 'arg2']) def test_issue4629(self): longopts, shortopts = getopt.getopt(['--help='], '', ['help=']) diff --git a/Misc/NEWS.d/next/Library/2024-11-03-23-25-07.gh-issue-126374.Xu_THP.rst b/Misc/NEWS.d/next/Library/2024-11-03-23-25-07.gh-issue-126374.Xu_THP.rst new file mode 100644 index 00000000000000..ad7ecfb6af9ec8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-11-03-23-25-07.gh-issue-126374.Xu_THP.rst @@ -0,0 +1 @@ +Add support for options with optional arguments in the :mod:`getopt` module. From 6e25eb15410f781f632d536d555f38879432522c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=AD=E4=B9=9D=E9=BC=8E?= <109224573@qq.com> Date: Tue, 12 Nov 2024 01:10:49 +0800 Subject: [PATCH 133/219] Update documentation links to Microsoft's documentation pages (GH-126379) --- Doc/library/asyncio-eventloop.rst | 2 +- Doc/library/time.rst | 2 +- Doc/using/windows.rst | 14 +++++++------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst index 3ace6eda4d7f29..9f1aec148f8750 100644 --- a/Doc/library/asyncio-eventloop.rst +++ b/Doc/library/asyncio-eventloop.rst @@ -1797,7 +1797,7 @@ By default asyncio is configured to use :class:`EventLoop`. .. seealso:: `MSDN documentation on I/O Completion Ports - `_. + `_. .. class:: EventLoop diff --git a/Doc/library/time.rst b/Doc/library/time.rst index 9cd5db768e9853..6265c2214eaa0d 100644 --- a/Doc/library/time.rst +++ b/Doc/library/time.rst @@ -390,7 +390,7 @@ Functions threads ready to run, the function returns immediately, and the thread continues execution. On Windows 8.1 and newer the implementation uses a `high-resolution timer - `_ + `_ which provides resolution of 100 nanoseconds. If *secs* is zero, ``Sleep(0)`` is used. Unix implementation: diff --git a/Doc/using/windows.rst b/Doc/using/windows.rst index daaf8822af1161..1a6322d72341ff 100644 --- a/Doc/using/windows.rst +++ b/Doc/using/windows.rst @@ -435,7 +435,7 @@ When writing to the Windows Registry, the following behaviors exist: For more detail on the technical basis for these limitations, please consult Microsoft's documentation on packaged full-trust apps, currently available at `docs.microsoft.com/en-us/windows/msix/desktop/desktop-to-uwp-behind-the-scenes -`_ +`_ .. _windows-nuget: @@ -536,7 +536,7 @@ dependents, such as Idle), pip and the Python documentation are not included. .. note:: The embedded distribution does not include the `Microsoft C Runtime - `_ and it is + `_ and it is the responsibility of the application installer to provide this. The runtime may have already been installed on a user's system previously or automatically via Windows Update, and can be detected by finding @@ -679,13 +679,13 @@ System variables, you need non-restricted access to your machine .. seealso:: - https://docs.microsoft.com/en-us/windows/win32/procthread/environment-variables + https://learn.microsoft.com/windows/win32/procthread/environment-variables Overview of environment variables on Windows - https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/set_1 + https://learn.microsoft.com/windows-server/administration/windows-commands/set_1 The ``set`` command, for temporarily modifying environment variables - https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/setx + https://learn.microsoft.com/windows-server/administration/windows-commands/setx The ``setx`` command, for permanently modifying environment variables @@ -1291,13 +1291,13 @@ is a collection of modules for advanced Windows-specific support. This includes utilities for: * `Component Object Model - `_ + `_ (COM) * Win32 API calls * Registry * Event log * `Microsoft Foundation Classes - `_ + `_ (MFC) user interfaces `PythonWin Date: Mon, 11 Nov 2024 11:19:08 -0800 Subject: [PATCH 134/219] gh-84559: gh-103134: Whats new 3.14 entries for multiprocessing. (GH-126697) --- Doc/whatsnew/3.14.rst | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 4b2a64fd0fab9c..c8f7dd162f1137 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -250,6 +250,12 @@ concurrent.futures same process) to Python code. This is separate from the proposed API in :pep:`734`. (Contributed by Eric Snow in :gh:`124548`.) +* The default ``ProcessPoolExecutor`` start method (see + :ref:`multiprocessing-start-methods`) changed from *fork* to *forkserver* on + platforms other than macOS & Windows. If you require the threading + incompatible *fork* start method you must explicitly request it by + supplying a *mp_context* to :class:`concurrent.futures.ProcessPoolExecutor`. + (Contributed by Gregory P. Smith in :gh:`84559`.) ctypes ------ @@ -357,6 +363,25 @@ json (Contributed by Trey Hunner in :gh:`122873`.) +multiprocessing +--------------- + +* The default start method (see :ref:`multiprocessing-start-methods`) changed + from *fork* to *forkserver* on platforms other than macOS & Windows where + it was already *spawn*. If you require the threading incompatible *fork* + start method you must explicitly request it using a context from + :func:`multiprocessing.get_context` (preferred) or change the default via + :func:`multiprocessing.set_start_method`. + (Contributed by Gregory P. Smith in :gh:`84559`.) +* The :ref:`multiprocessing proxy objects ` + for *list* and *dict* types gain previously overlooked missing methods: + + * :meth:`!clear` and :meth:`!copy` for proxies of :class:`list`. + * :meth:`~dict.fromkeys`, ``reversed(d)``, ``d | {}``, ``{} | d``, + ``d |= {'b': 2}`` for proxies of :class:`dict`. + + (Contributed by Roy Hyunjin Han for :gh:`103134`) + operator -------- @@ -511,14 +536,6 @@ Deprecated as a single positional argument. (Contributed by Serhiy Storchaka in :gh:`109218`.) -* :mod:`multiprocessing` and :mod:`concurrent.futures`: - The default start method (see :ref:`multiprocessing-start-methods`) changed - away from *fork* to *forkserver* on platforms where it was not already - *spawn* (Windows & macOS). If you require the threading incompatible *fork* - start method you must explicitly specify it when using :mod:`multiprocessing` - or :mod:`concurrent.futures` APIs. - (Contributed by Gregory P. Smith in :gh:`84559`.) - * :mod:`os`: :term:`Soft deprecate ` :func:`os.popen` and :func:`os.spawn* ` functions. They should no longer be used to From 3c6d2d123004d8e37a2111083e22e2a2c96dffd0 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 11 Nov 2024 23:08:54 +0200 Subject: [PATCH 135/219] gh-89416: Add RFC 9559 MIME types for Matroska formats (#126412) Co-authored-by: Zachary Ware Co-authored-by: Jelle Zijlstra --- Doc/whatsnew/3.14.rst | 14 ++++++ Lib/mimetypes.py | 3 ++ Lib/test/test_mimetypes.py | 47 +++++++++++-------- ...4-11-04-22-53-09.gh-issue-89416.YVQaas.rst | 2 + 4 files changed, 46 insertions(+), 20 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-11-04-22-53-09.gh-issue-89416.YVQaas.rst diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index c8f7dd162f1137..f9b219828d3d94 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -363,6 +363,19 @@ json (Contributed by Trey Hunner in :gh:`122873`.) +mimetypes +--------- + +* Add :rfc:`9559` MIME types for Matroska audiovisual data container + structures, containing: + + * audio with no video: ``audio/matroska`` (``.mka``) + * video: ``video/matroska`` (``.mkv``) + * stereoscopic video: ``video/matroska-3d`` (``.mk3d``) + + (Contributed by Hugo van Kemenade in :gh:`89416`.) + + multiprocessing --------------- @@ -382,6 +395,7 @@ multiprocessing (Contributed by Roy Hyunjin Han for :gh:`103134`) + operator -------- diff --git a/Lib/mimetypes.py b/Lib/mimetypes.py index d7c4e8444f8dec..fd343a78c98ae1 100644 --- a/Lib/mimetypes.py +++ b/Lib/mimetypes.py @@ -534,6 +534,7 @@ def _default_mime_types(): '.ass' : 'audio/aac', '.au' : 'audio/basic', '.snd' : 'audio/basic', + '.mka' : 'audio/matroska', '.mp3' : 'audio/mpeg', '.mp2' : 'audio/mpeg', '.opus' : 'audio/opus', @@ -595,6 +596,8 @@ def _default_mime_types(): '.sgml' : 'text/x-sgml', '.vcf' : 'text/x-vcard', '.xml' : 'text/xml', + '.mkv' : 'video/matroska', + '.mk3d' : 'video/matroska-3d', '.mp4' : 'video/mp4', '.mpeg' : 'video/mpeg', '.m1v' : 'video/mpeg', diff --git a/Lib/test/test_mimetypes.py b/Lib/test/test_mimetypes.py index 58f6a4dfae08ba..8d3e8fcafb6740 100644 --- a/Lib/test/test_mimetypes.py +++ b/Lib/test/test_mimetypes.py @@ -223,26 +223,33 @@ def test_guess_known_extensions(self): def test_preferred_extension(self): def check_extensions(): - self.assertEqual(mimetypes.guess_extension('application/octet-stream'), '.bin') - self.assertEqual(mimetypes.guess_extension('application/postscript'), '.ps') - self.assertEqual(mimetypes.guess_extension('application/vnd.apple.mpegurl'), '.m3u') - self.assertEqual(mimetypes.guess_extension('application/vnd.ms-excel'), '.xls') - self.assertEqual(mimetypes.guess_extension('application/vnd.ms-powerpoint'), '.ppt') - self.assertEqual(mimetypes.guess_extension('application/x-texinfo'), '.texi') - self.assertEqual(mimetypes.guess_extension('application/x-troff'), '.roff') - self.assertEqual(mimetypes.guess_extension('application/xml'), '.xsl') - self.assertEqual(mimetypes.guess_extension('audio/mpeg'), '.mp3') - self.assertEqual(mimetypes.guess_extension('image/avif'), '.avif') - self.assertEqual(mimetypes.guess_extension('image/webp'), '.webp') - self.assertEqual(mimetypes.guess_extension('image/jpeg'), '.jpg') - self.assertEqual(mimetypes.guess_extension('image/tiff'), '.tiff') - self.assertEqual(mimetypes.guess_extension('message/rfc822'), '.eml') - self.assertEqual(mimetypes.guess_extension('text/html'), '.html') - self.assertEqual(mimetypes.guess_extension('text/plain'), '.txt') - self.assertEqual(mimetypes.guess_extension('text/rtf'), '.rtf') - self.assertEqual(mimetypes.guess_extension('text/x-rst'), '.rst') - self.assertEqual(mimetypes.guess_extension('video/mpeg'), '.mpeg') - self.assertEqual(mimetypes.guess_extension('video/quicktime'), '.mov') + for mime_type, ext in ( + ("application/octet-stream", ".bin"), + ("application/postscript", ".ps"), + ("application/vnd.apple.mpegurl", ".m3u"), + ("application/vnd.ms-excel", ".xls"), + ("application/vnd.ms-powerpoint", ".ppt"), + ("application/x-texinfo", ".texi"), + ("application/x-troff", ".roff"), + ("application/xml", ".xsl"), + ("audio/matroska", ".mka"), + ("audio/mpeg", ".mp3"), + ("image/avif", ".avif"), + ("image/webp", ".webp"), + ("image/jpeg", ".jpg"), + ("image/tiff", ".tiff"), + ("message/rfc822", ".eml"), + ("text/html", ".html"), + ("text/plain", ".txt"), + ("text/rtf", ".rtf"), + ("text/x-rst", ".rst"), + ("video/matroska", ".mkv"), + ("video/matroska-3d", ".mk3d"), + ("video/mpeg", ".mpeg"), + ("video/quicktime", ".mov"), + ): + with self.subTest(mime_type=mime_type, ext=ext): + self.assertEqual(mimetypes.guess_extension(mime_type), ext) check_extensions() mimetypes.init() diff --git a/Misc/NEWS.d/next/Library/2024-11-04-22-53-09.gh-issue-89416.YVQaas.rst b/Misc/NEWS.d/next/Library/2024-11-04-22-53-09.gh-issue-89416.YVQaas.rst new file mode 100644 index 00000000000000..f1a2fcbaff2564 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-11-04-22-53-09.gh-issue-89416.YVQaas.rst @@ -0,0 +1,2 @@ +Add :rfc:`9559` MIME types for Matroska audiovisual container formats. Patch +by Hugo van Kemenade. From b697d8c48e5e47c37fdd5dd74de40dfb4d6c0d01 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 11 Nov 2024 14:49:41 -0700 Subject: [PATCH 136/219] gh-76785: Minor Cleanup of Exception-related Cross-interpreter State (gh-126602) This change makes it easier to backport the _interpreters, _interpqueues, and _interpchannels modules to Python 3.12. --- Include/internal/pycore_crossinterp.h | 10 ++- Modules/_interpretersmodule.c | 2 +- Python/crossinterp.c | 36 +++++++---- Python/crossinterp_exceptions.h | 91 ++++++++++++++------------- 4 files changed, 81 insertions(+), 58 deletions(-) diff --git a/Include/internal/pycore_crossinterp.h b/Include/internal/pycore_crossinterp.h index e91e911feb38cc..a7e71efc5daa49 100644 --- a/Include/internal/pycore_crossinterp.h +++ b/Include/internal/pycore_crossinterp.h @@ -11,6 +11,7 @@ extern "C" { #include "pycore_lock.h" // PyMutex #include "pycore_pyerrors.h" + /**************/ /* exceptions */ /**************/ @@ -163,8 +164,13 @@ struct _xi_state { // heap types _PyXIData_lookup_t data_lookup; - // heap types - PyObject *PyExc_NotShareableError; + struct xi_exceptions { + // static types + PyObject *PyExc_InterpreterError; + PyObject *PyExc_InterpreterNotFoundError; + // heap types + PyObject *PyExc_NotShareableError; + } exceptions; }; extern PyStatus _PyXI_Init(PyInterpreterState *interp); diff --git a/Modules/_interpretersmodule.c b/Modules/_interpretersmodule.c index a9a966e79e0920..eb4ac9847dcd2b 100644 --- a/Modules/_interpretersmodule.c +++ b/Modules/_interpretersmodule.c @@ -1507,7 +1507,7 @@ module_exec(PyObject *mod) goto error; } PyObject *PyExc_NotShareableError = \ - _PyInterpreterState_GetXIState(interp)->PyExc_NotShareableError; + _PyInterpreterState_GetXIState(interp)->exceptions.PyExc_NotShareableError; if (PyModule_AddType(mod, (PyTypeObject *)PyExc_NotShareableError) < 0) { goto error; } diff --git a/Python/crossinterp.c b/Python/crossinterp.c index 2daba99988c12a..b7aa8da8ac550e 100644 --- a/Python/crossinterp.c +++ b/Python/crossinterp.c @@ -17,11 +17,11 @@ /* exceptions */ /**************/ -static int init_exceptions(PyInterpreterState *); -static void fini_exceptions(PyInterpreterState *); -static int _init_not_shareable_error_type(PyInterpreterState *); -static void _fini_not_shareable_error_type(PyInterpreterState *); -static PyObject * _get_not_shareable_error_type(PyInterpreterState *); +typedef struct xi_exceptions exceptions_t; +static int init_static_exctypes(exceptions_t *, PyInterpreterState *); +static void fini_static_exctypes(exceptions_t *, PyInterpreterState *); +static int init_heap_exctypes(exceptions_t *); +static void fini_heap_exctypes(exceptions_t *); #include "crossinterp_exceptions.h" @@ -205,7 +205,8 @@ static inline void _set_xid_lookup_failure(PyInterpreterState *interp, PyObject *obj, const char *msg) { - PyObject *exctype = _get_not_shareable_error_type(interp); + exceptions_t *state = &_PyInterpreterState_GetXIState(interp)->exceptions; + PyObject *exctype = state->PyExc_NotShareableError; assert(exctype != NULL); if (msg != NULL) { assert(obj == NULL); @@ -1605,7 +1606,9 @@ _propagate_not_shareable_error(_PyXI_session *session) return; } PyInterpreterState *interp = PyInterpreterState_Get(); - if (PyErr_ExceptionMatches(_get_not_shareable_error_type(interp))) { + exceptions_t *state = &_PyInterpreterState_GetXIState(interp)->exceptions; + assert(state->PyExc_NotShareableError != NULL); + if (PyErr_ExceptionMatches(state->PyExc_NotShareableError)) { // We want to propagate the exception directly. session->_error_override = _PyXI_ERR_NOT_SHAREABLE; session->error_override = &session->_error_override; @@ -1782,9 +1785,11 @@ _PyXI_Init(PyInterpreterState *interp) } xid_lookup_init(&_PyXI_GET_STATE(interp)->data_lookup); - // Initialize exceptions (heap types). - if (_init_not_shareable_error_type(interp) < 0) { - return _PyStatus_ERR("failed to initialize NotShareableError"); + // Initialize exceptions.(heap types). + // See _PyXI_InitTypes() for the static types. + if (init_heap_exctypes(&_PyXI_GET_STATE(interp)->exceptions) < 0) { + PyErr_PrintEx(0); + return _PyStatus_ERR("failed to initialize exceptions"); } return _PyStatus_OK(); @@ -1797,7 +1802,8 @@ void _PyXI_Fini(PyInterpreterState *interp) { // Finalize exceptions (heap types). - _fini_not_shareable_error_type(interp); + // See _PyXI_FiniTypes() for the static types. + fini_heap_exctypes(&_PyXI_GET_STATE(interp)->exceptions); // Finalize the XID lookup state (e.g. registry). xid_lookup_fini(&_PyXI_GET_STATE(interp)->data_lookup); @@ -1809,17 +1815,21 @@ _PyXI_Fini(PyInterpreterState *interp) PyStatus _PyXI_InitTypes(PyInterpreterState *interp) { - if (init_exceptions(interp) < 0) { + if (init_static_exctypes(&_PyXI_GET_STATE(interp)->exceptions, interp) < 0) { PyErr_PrintEx(0); return _PyStatus_ERR("failed to initialize an exception type"); } + // We would initialize heap types here too but that leads to ref leaks. + // Instead, we intialize them in _PyXI_Init(). return _PyStatus_OK(); } void _PyXI_FiniTypes(PyInterpreterState *interp) { - fini_exceptions(interp); + // We would finalize heap types here too but that leads to ref leaks. + // Instead, we finalize them in _PyXI_Fini(). + fini_static_exctypes(&_PyXI_GET_STATE(interp)->exceptions, interp); } diff --git a/Python/crossinterp_exceptions.h b/Python/crossinterp_exceptions.h index 278511da615c75..3cb45d2067710b 100644 --- a/Python/crossinterp_exceptions.h +++ b/Python/crossinterp_exceptions.h @@ -25,71 +25,78 @@ static PyTypeObject _PyExc_InterpreterNotFoundError = { }; PyObject *PyExc_InterpreterNotFoundError = (PyObject *)&_PyExc_InterpreterNotFoundError; -/* NotShareableError extends ValueError */ - -static int -_init_not_shareable_error_type(PyInterpreterState *interp) -{ - const char *name = "interpreters.NotShareableError"; - PyObject *base = PyExc_ValueError; - PyObject *ns = NULL; - PyObject *exctype = PyErr_NewException(name, base, ns); - if (exctype == NULL) { - return -1; - } - - _PyInterpreterState_GetXIState(interp)->PyExc_NotShareableError = exctype; - return 0; -} - -static void -_fini_not_shareable_error_type(PyInterpreterState *interp) -{ - Py_CLEAR(_PyInterpreterState_GetXIState(interp)->PyExc_NotShareableError); -} - -static PyObject * -_get_not_shareable_error_type(PyInterpreterState *interp) -{ - assert(_PyInterpreterState_GetXIState(interp)->PyExc_NotShareableError != NULL); - return _PyInterpreterState_GetXIState(interp)->PyExc_NotShareableError; -} - /* lifecycle */ static int -init_exceptions(PyInterpreterState *interp) +init_static_exctypes(exceptions_t *state, PyInterpreterState *interp) { + assert(state == &_PyXI_GET_STATE(interp)->exceptions); PyTypeObject *base = (PyTypeObject *)PyExc_Exception; - // builtin static types - + // PyExc_InterpreterError _PyExc_InterpreterError.tp_base = base; _PyExc_InterpreterError.tp_traverse = base->tp_traverse; _PyExc_InterpreterError.tp_clear = base->tp_clear; if (_PyStaticType_InitBuiltin(interp, &_PyExc_InterpreterError) < 0) { - return -1; + goto error; } + state->PyExc_InterpreterError = (PyObject *)&_PyExc_InterpreterError; + // PyExc_InterpreterNotFoundError _PyExc_InterpreterNotFoundError.tp_traverse = base->tp_traverse; _PyExc_InterpreterNotFoundError.tp_clear = base->tp_clear; if (_PyStaticType_InitBuiltin(interp, &_PyExc_InterpreterNotFoundError) < 0) { - return -1; + goto error; } + state->PyExc_InterpreterNotFoundError = + (PyObject *)&_PyExc_InterpreterNotFoundError; - // heap types + return 0; - // We would call _init_not_shareable_error_type() here too, - // but that leads to ref leaks +error: + fini_static_exctypes(state, interp); + return -1; +} + +static void +fini_static_exctypes(exceptions_t *state, PyInterpreterState *interp) +{ + assert(state == &_PyXI_GET_STATE(interp)->exceptions); + if (state->PyExc_InterpreterNotFoundError != NULL) { + state->PyExc_InterpreterNotFoundError = NULL; + _PyStaticType_FiniBuiltin(interp, &_PyExc_InterpreterNotFoundError); + } + if (state->PyExc_InterpreterError != NULL) { + state->PyExc_InterpreterError = NULL; + _PyStaticType_FiniBuiltin(interp, &_PyExc_InterpreterError); + } +} + +static int +init_heap_exctypes(exceptions_t *state) +{ + PyObject *exctype; + + /* NotShareableError extends ValueError */ + const char *name = "interpreters.NotShareableError"; + PyObject *base = PyExc_ValueError; + PyObject *ns = NULL; + exctype = PyErr_NewException(name, base, ns); + if (exctype == NULL) { + goto error; + } + state->PyExc_NotShareableError = exctype; return 0; + +error: + fini_heap_exctypes(state); + return -1; } static void -fini_exceptions(PyInterpreterState *interp) +fini_heap_exctypes(exceptions_t *state) { - // Likewise with _fini_not_shareable_error_type(). - _PyStaticType_FiniBuiltin(interp, &_PyExc_InterpreterNotFoundError); - _PyStaticType_FiniBuiltin(interp, &_PyExc_InterpreterError); + Py_CLEAR(state->PyExc_NotShareableError); } From dff074d1446bab23578a6b228b0c59a17006299c Mon Sep 17 00:00:00 2001 From: "Tomas R." Date: Mon, 11 Nov 2024 23:16:39 +0100 Subject: [PATCH 137/219] gh-126413: Add translation tests for getopt and optparse (GH-126698) --- Lib/test/support/i18n_helper.py | 63 ++++++++++++++++++++ Lib/test/test_argparse.py | 54 ++--------------- Lib/test/test_getopt.py | 19 ++++-- Lib/test/test_optparse.py | 11 +++- Lib/test/translationdata/getopt/msgids.txt | 6 ++ Lib/test/translationdata/optparse/msgids.txt | 14 +++++ Makefile.pre.in | 2 + 7 files changed, 114 insertions(+), 55 deletions(-) create mode 100644 Lib/test/support/i18n_helper.py create mode 100644 Lib/test/translationdata/getopt/msgids.txt create mode 100644 Lib/test/translationdata/optparse/msgids.txt diff --git a/Lib/test/support/i18n_helper.py b/Lib/test/support/i18n_helper.py new file mode 100644 index 00000000000000..2e304f29e8ba7f --- /dev/null +++ b/Lib/test/support/i18n_helper.py @@ -0,0 +1,63 @@ +import re +import subprocess +import sys +import unittest +from pathlib import Path +from test.support import REPO_ROOT, TEST_HOME_DIR, requires_subprocess +from test.test_tools import skip_if_missing + + +pygettext = Path(REPO_ROOT) / 'Tools' / 'i18n' / 'pygettext.py' + +msgid_pattern = re.compile(r'msgid(.*?)(?:msgid_plural|msgctxt|msgstr)', + re.DOTALL) +msgid_string_pattern = re.compile(r'"((?:\\"|[^"])*)"') + + +def _generate_po_file(path, *, stdout_only=True): + res = subprocess.run([sys.executable, pygettext, + '--no-location', '-o', '-', path], + stdout=subprocess.PIPE, stderr=subprocess.PIPE, + text=True) + if stdout_only: + return res.stdout + return res + + +def _extract_msgids(po): + msgids = [] + for msgid in msgid_pattern.findall(po): + msgid_string = ''.join(msgid_string_pattern.findall(msgid)) + msgid_string = msgid_string.replace(r'\"', '"') + if msgid_string: + msgids.append(msgid_string) + return sorted(msgids) + + +def _get_snapshot_path(module_name): + return Path(TEST_HOME_DIR) / 'translationdata' / module_name / 'msgids.txt' + + +@requires_subprocess() +class TestTranslationsBase(unittest.TestCase): + + def assertMsgidsEqual(self, module): + '''Assert that msgids extracted from a given module match a + snapshot. + + ''' + skip_if_missing('i18n') + res = _generate_po_file(module.__file__, stdout_only=False) + self.assertEqual(res.returncode, 0) + self.assertEqual(res.stderr, '') + msgids = _extract_msgids(res.stdout) + snapshot_path = _get_snapshot_path(module.__name__) + snapshot = snapshot_path.read_text().splitlines() + self.assertListEqual(msgids, snapshot) + + +def update_translation_snapshots(module): + contents = _generate_po_file(module.__file__) + msgids = _extract_msgids(contents) + snapshot_path = _get_snapshot_path(module.__name__) + snapshot_path.write_text('\n'.join(msgids)) diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index cbf119ed2dabbb..358cfb1c56aae4 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -7,10 +7,8 @@ import operator import os import py_compile -import re import shutil import stat -import subprocess import sys import textwrap import tempfile @@ -19,15 +17,11 @@ import warnings from enum import StrEnum -from pathlib import Path -from test.support import REPO_ROOT -from test.support import TEST_HOME_DIR from test.support import captured_stderr from test.support import import_helper from test.support import os_helper -from test.support import requires_subprocess from test.support import script_helper -from test.test_tools import skip_if_missing +from test.support.i18n_helper import TestTranslationsBase, update_translation_snapshots from unittest import mock @@ -7056,50 +7050,10 @@ def test_directory_in_zipfile_compiled(self): # Translation tests # ================= -pygettext = Path(REPO_ROOT) / 'Tools' / 'i18n' / 'pygettext.py' -snapshot_path = Path(TEST_HOME_DIR) / 'translationdata' / 'argparse' / 'msgids.txt' - -msgid_pattern = re.compile(r'msgid(.*?)(?:msgid_plural|msgctxt|msgstr)', re.DOTALL) -msgid_string_pattern = re.compile(r'"((?:\\"|[^"])*)"') - - -@requires_subprocess() -class TestTranslations(unittest.TestCase): +class TestTranslations(TestTranslationsBase): def test_translations(self): - # Test messages extracted from the argparse module against a snapshot - skip_if_missing('i18n') - res = generate_po_file(stdout_only=False) - self.assertEqual(res.returncode, 0) - self.assertEqual(res.stderr, '') - msgids = extract_msgids(res.stdout) - snapshot = snapshot_path.read_text().splitlines() - self.assertListEqual(msgids, snapshot) - - -def generate_po_file(*, stdout_only=True): - res = subprocess.run([sys.executable, pygettext, - '--no-location', '-o', '-', argparse.__file__], - stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) - if stdout_only: - return res.stdout - return res - - -def extract_msgids(po): - msgids = [] - for msgid in msgid_pattern.findall(po): - msgid_string = ''.join(msgid_string_pattern.findall(msgid)) - msgid_string = msgid_string.replace(r'\"', '"') - if msgid_string: - msgids.append(msgid_string) - return sorted(msgids) - - -def update_translation_snapshots(): - contents = generate_po_file() - msgids = extract_msgids(contents) - snapshot_path.write_text('\n'.join(msgids)) + self.assertMsgidsEqual(argparse) def tearDownModule(): @@ -7111,6 +7065,6 @@ def tearDownModule(): if __name__ == '__main__': # To regenerate translation snapshots if len(sys.argv) > 1 and sys.argv[1] == '--snapshot-update': - update_translation_snapshots() + update_translation_snapshots(argparse) sys.exit(0) unittest.main() diff --git a/Lib/test/test_getopt.py b/Lib/test/test_getopt.py index 984bdb73f34aa4..0675bcbb4e8247 100644 --- a/Lib/test/test_getopt.py +++ b/Lib/test/test_getopt.py @@ -1,11 +1,12 @@ # test_getopt.py # David Goodger 2000-08-19 -from test.support.os_helper import EnvironmentVarGuard import doctest -import unittest - import getopt +import sys +import unittest +from test.support.i18n_helper import TestTranslationsBase, update_translation_snapshots +from test.support.os_helper import EnvironmentVarGuard sentinel = object() @@ -224,10 +225,20 @@ def test_libref_examples(): ['a1', 'a2'] """ + +class TestTranslations(TestTranslationsBase): + def test_translations(self): + self.assertMsgidsEqual(getopt) + + def load_tests(loader, tests, pattern): tests.addTest(doctest.DocTestSuite()) return tests -if __name__ == "__main__": +if __name__ == '__main__': + # To regenerate translation snapshots + if len(sys.argv) > 1 and sys.argv[1] == '--snapshot-update': + update_translation_snapshots(getopt) + sys.exit(0) unittest.main() diff --git a/Lib/test/test_optparse.py b/Lib/test/test_optparse.py index 28b274462388ed..8655a0537a5e56 100644 --- a/Lib/test/test_optparse.py +++ b/Lib/test/test_optparse.py @@ -15,7 +15,7 @@ from io import StringIO from test import support from test.support import os_helper - +from test.support.i18n_helper import TestTranslationsBase, update_translation_snapshots import optparse from optparse import make_option, Option, \ @@ -1656,5 +1656,14 @@ def test__all__(self): support.check__all__(self, optparse, not_exported=not_exported) +class TestTranslations(TestTranslationsBase): + def test_translations(self): + self.assertMsgidsEqual(optparse) + + if __name__ == '__main__': + # To regenerate translation snapshots + if len(sys.argv) > 1 and sys.argv[1] == '--snapshot-update': + update_translation_snapshots(optparse) + sys.exit(0) unittest.main() diff --git a/Lib/test/translationdata/getopt/msgids.txt b/Lib/test/translationdata/getopt/msgids.txt new file mode 100644 index 00000000000000..1ffab1f31abad5 --- /dev/null +++ b/Lib/test/translationdata/getopt/msgids.txt @@ -0,0 +1,6 @@ +option -%s not recognized +option -%s requires argument +option --%s must not have an argument +option --%s not a unique prefix +option --%s not recognized +option --%s requires argument \ No newline at end of file diff --git a/Lib/test/translationdata/optparse/msgids.txt b/Lib/test/translationdata/optparse/msgids.txt new file mode 100644 index 00000000000000..ac5317c736af8c --- /dev/null +++ b/Lib/test/translationdata/optparse/msgids.txt @@ -0,0 +1,14 @@ +%prog [options] +%s option does not take a value +Options +Usage +Usage: %s\n +ambiguous option: %s (%s?) +complex +floating-point +integer +no such option: %s +option %s: invalid %s value: %r +option %s: invalid choice: %r (choose from %s) +show program's version number and exit +show this help message and exit \ No newline at end of file diff --git a/Makefile.pre.in b/Makefile.pre.in index a337223d4d8608..8d94ba361fd934 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -2567,6 +2567,8 @@ TESTSUBDIRS= idlelib/idle_test \ test/tracedmodules \ test/translationdata \ test/translationdata/argparse \ + test/translationdata/getopt \ + test/translationdata/optparse \ test/typinganndata \ test/wheeldata \ test/xmltestdata \ From 036930d84409d0725a4ab95fb976f74d1698c41f Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Mon, 11 Nov 2024 17:49:48 -0500 Subject: [PATCH 138/219] Docs: re-create pages for removed modules to document their removal. (#126622) Will also need to change the redirects that were created here: https://github.com/python/psf-salt/pull/521/files --- Doc/library/aifc.rst | 15 +++++++ Doc/library/asynchat.rst | 17 ++++++++ Doc/library/asyncore.rst | 17 ++++++++ Doc/library/audioop.rst | 15 +++++++ Doc/library/cgi.rst | 19 +++++++++ Doc/library/cgitb.rst | 19 +++++++++ Doc/library/chunk.rst | 15 +++++++ Doc/library/crypt.rst | 20 ++++++++++ Doc/library/distutils.rst | 17 ++++++++ Doc/library/imghdr.rst | 19 +++++++++ Doc/library/imp.rst | 18 +++++++++ Doc/library/index.rst | 1 + Doc/library/mailcap.rst | 15 +++++++ Doc/library/msilib.rst | 15 +++++++ Doc/library/nis.rst | 15 +++++++ Doc/library/nntplib.rst | 15 +++++++ Doc/library/ossaudiodev.rst | 15 +++++++ Doc/library/pipes.rst | 17 ++++++++ Doc/library/removed.rst | 39 +++++++++++++++++++ Doc/library/smtpd.rst | 18 +++++++++ Doc/library/sndhdr.rst | 19 +++++++++ Doc/library/spwd.rst | 18 +++++++++ Doc/library/sunau.rst | 15 +++++++ Doc/library/telnetlib.rst | 19 +++++++++ Doc/library/uu.rst | 15 +++++++ Doc/library/xdrlib.rst | 15 +++++++ Doc/whatsnew/3.12.rst | 6 +++ ...-11-09-19-43-10.gh-issue-126622.YacfDc.rst | 3 ++ 28 files changed, 451 insertions(+) create mode 100644 Doc/library/aifc.rst create mode 100644 Doc/library/asynchat.rst create mode 100644 Doc/library/asyncore.rst create mode 100644 Doc/library/audioop.rst create mode 100644 Doc/library/cgi.rst create mode 100644 Doc/library/cgitb.rst create mode 100644 Doc/library/chunk.rst create mode 100644 Doc/library/crypt.rst create mode 100644 Doc/library/distutils.rst create mode 100644 Doc/library/imghdr.rst create mode 100644 Doc/library/imp.rst create mode 100644 Doc/library/mailcap.rst create mode 100644 Doc/library/msilib.rst create mode 100644 Doc/library/nis.rst create mode 100644 Doc/library/nntplib.rst create mode 100644 Doc/library/ossaudiodev.rst create mode 100644 Doc/library/pipes.rst create mode 100644 Doc/library/removed.rst create mode 100644 Doc/library/smtpd.rst create mode 100644 Doc/library/sndhdr.rst create mode 100644 Doc/library/spwd.rst create mode 100644 Doc/library/sunau.rst create mode 100644 Doc/library/telnetlib.rst create mode 100644 Doc/library/uu.rst create mode 100644 Doc/library/xdrlib.rst create mode 100644 Misc/NEWS.d/next/Documentation/2024-11-09-19-43-10.gh-issue-126622.YacfDc.rst diff --git a/Doc/library/aifc.rst b/Doc/library/aifc.rst new file mode 100644 index 00000000000000..a756d679036ecb --- /dev/null +++ b/Doc/library/aifc.rst @@ -0,0 +1,15 @@ +:mod:`!aifc` --- Read and write AIFF and AIFC files +=================================================== + +.. module:: aifc + :synopsis: Removed in 3.13. + :deprecated: + +.. deprecated-removed:: 3.11 3.13 + +This module is no longer part of the Python standard library. +It was :ref:`removed in Python 3.13 ` after +being deprecated in Python 3.11. The removal was decided in :pep:`594`. + +The last version of Python that provided the :mod:`!aifc` module was +`Python 3.12 `_. diff --git a/Doc/library/asynchat.rst b/Doc/library/asynchat.rst new file mode 100644 index 00000000000000..5e5c3a99fe66f1 --- /dev/null +++ b/Doc/library/asynchat.rst @@ -0,0 +1,17 @@ +:mod:`!asynchat` --- Asynchronous socket command/response handler +================================================================= + +.. module:: asynchat + :synopsis: Removed in 3.12. + :deprecated: + +.. deprecated-removed:: 3.6 3.12 + +This module is no longer part of the Python standard library. +It was :ref:`removed in Python 3.12 ` after +being deprecated in Python 3.6. The removal was decided in :pep:`594`. + +Applications should use the :mod:`asyncio` module instead. + +The last version of Python that provided the :mod:`!asynchat` module was +`Python 3.11 `_. diff --git a/Doc/library/asyncore.rst b/Doc/library/asyncore.rst new file mode 100644 index 00000000000000..22c9881c3cca36 --- /dev/null +++ b/Doc/library/asyncore.rst @@ -0,0 +1,17 @@ +:mod:`!asyncore` --- Asynchronous socket handler +================================================ + +.. module:: asyncore + :synopsis: Removed in 3.12. + :deprecated: + +.. deprecated-removed:: 3.6 3.12 + +This module is no longer part of the Python standard library. +It was :ref:`removed in Python 3.12 ` after +being deprecated in Python 3.6. The removal was decided in :pep:`594`. + +Applications should use the :mod:`asyncio` module instead. + +The last version of Python that provided the :mod:`!asyncore` module was +`Python 3.11 `_. diff --git a/Doc/library/audioop.rst b/Doc/library/audioop.rst new file mode 100644 index 00000000000000..3bc580b0bd3433 --- /dev/null +++ b/Doc/library/audioop.rst @@ -0,0 +1,15 @@ +:mod:`!audioop` --- Manipulate raw audio data +============================================= + +.. module:: audioop + :synopsis: Removed in 3.13. + :deprecated: + +.. deprecated-removed:: 3.11 3.13 + +This module is no longer part of the Python standard library. +It was :ref:`removed in Python 3.13 ` after +being deprecated in Python 3.11. The removal was decided in :pep:`594`. + +The last version of Python that provided the :mod:`!audioop` module was +`Python 3.12 `_. diff --git a/Doc/library/cgi.rst b/Doc/library/cgi.rst new file mode 100644 index 00000000000000..f9108fa954a906 --- /dev/null +++ b/Doc/library/cgi.rst @@ -0,0 +1,19 @@ +:mod:`!cgi` --- Common Gateway Interface support +================================================ + +.. module:: cgi + :synopsis: Removed in 3.13. + :deprecated: + +.. deprecated-removed:: 3.11 3.13 + +This module is no longer part of the Python standard library. +It was :ref:`removed in Python 3.13 ` after +being deprecated in Python 3.11. The removal was decided in :pep:`594`. + +A fork of the module on PyPI can be used instead: :pypi:`legacy-cgi`. +This is a copy of the cgi module, no longer maintained or supported by the core +Python team. + +The last version of Python that provided the :mod:`!cgi` module was +`Python 3.12 `_. diff --git a/Doc/library/cgitb.rst b/Doc/library/cgitb.rst new file mode 100644 index 00000000000000..fc646aa4c48acd --- /dev/null +++ b/Doc/library/cgitb.rst @@ -0,0 +1,19 @@ +:mod:`!cgitb` --- Traceback manager for CGI scripts +=================================================== + +.. module:: cgitb + :synopsis: Removed in 3.13. + :deprecated: + +.. deprecated-removed:: 3.11 3.13 + +This module is no longer part of the Python standard library. +It was :ref:`removed in Python 3.13 ` after +being deprecated in Python 3.11. The removal was decided in :pep:`594`. + +A fork of the module on PyPI can now be used instead: :pypi:`legacy-cgi`. +This is a copy of the cgi module, no longer maintained or supported by the core +Python team. + +The last version of Python that provided the :mod:`!cgitb` module was +`Python 3.12 `_. diff --git a/Doc/library/chunk.rst b/Doc/library/chunk.rst new file mode 100644 index 00000000000000..9950a0ea70649a --- /dev/null +++ b/Doc/library/chunk.rst @@ -0,0 +1,15 @@ +:mod:`!chunk` --- Read IFF chunked data +======================================= + +.. module:: chunk + :synopsis: Removed in 3.13. + :deprecated: + +.. deprecated-removed:: 3.11 3.13 + +This module is no longer part of the Python standard library. +It was :ref:`removed in Python 3.13 ` after +being deprecated in Python 3.11. The removal was decided in :pep:`594`. + +The last version of Python that provided the :mod:`!chunk` module was +`Python 3.12 `_. diff --git a/Doc/library/crypt.rst b/Doc/library/crypt.rst new file mode 100644 index 00000000000000..9ff37196ccf69f --- /dev/null +++ b/Doc/library/crypt.rst @@ -0,0 +1,20 @@ +:mod:`!crypt` --- Function to check Unix passwords +================================================== + +.. module:: crypt + :synopsis: Removed in 3.13. + :deprecated: + +.. deprecated-removed:: 3.11 3.13 + +This module is no longer part of the Python standard library. +It was :ref:`removed in Python 3.13 ` after +being deprecated in Python 3.11. The removal was decided in :pep:`594`. + +Applications can use the :mod:`hashlib` module from the standard library. +Other possible replacements are third-party libraries from PyPI: +:pypi:`legacycrypt`, :pypi:`bcrypt`, :pypi:`argon2-cffi`, or :pypi:`passlib`. +These are not supported or maintained by the Python core team. + +The last version of Python that provided the :mod:`!crypt` module was +`Python 3.12 `_. diff --git a/Doc/library/distutils.rst b/Doc/library/distutils.rst new file mode 100644 index 00000000000000..af63e035bf3c4a --- /dev/null +++ b/Doc/library/distutils.rst @@ -0,0 +1,17 @@ +:mod:`!distutils` --- Building and installing Python modules +============================================================ + +.. module:: distutils + :synopsis: Removed in 3.12. + :deprecated: + +.. deprecated-removed:: 3.10 3.12 + +This module is no longer part of the Python standard library. +It was :ref:`removed in Python 3.12 ` after +being deprecated in Python 3.10. The removal was decided in :pep:`632`, +which has `migration advice +`_. + +The last version of Python that provided the :mod:`!distutils` module was +`Python 3.11 `_. diff --git a/Doc/library/imghdr.rst b/Doc/library/imghdr.rst new file mode 100644 index 00000000000000..56f26355f42558 --- /dev/null +++ b/Doc/library/imghdr.rst @@ -0,0 +1,19 @@ +:mod:`!imghdr` --- Determine the type of an image +================================================= + +.. module:: imghdr + :synopsis: Removed in 3.13. + :deprecated: + +.. deprecated-removed:: 3.11 3.13 + +This module is no longer part of the Python standard library. +It was :ref:`removed in Python 3.13 ` after +being deprecated in Python 3.11. The removal was decided in :pep:`594`. + +Possible replacements are third-party libraries from PyPI: +:pypi:`filetype`, :pypi:`puremagic`, or :pypi:`python-magic`. +These are not supported or maintained by the Python core team. + +The last version of Python that provided the :mod:`!imghdr` module was +`Python 3.12 `_. diff --git a/Doc/library/imp.rst b/Doc/library/imp.rst new file mode 100644 index 00000000000000..3dc4c568b1ae2f --- /dev/null +++ b/Doc/library/imp.rst @@ -0,0 +1,18 @@ +:mod:`!imp` --- Access the import internals +=========================================== + +.. module:: imp + :synopsis: Removed in 3.12. + :deprecated: + +.. deprecated-removed:: 3.4 3.12 + +This module is no longer part of the Python standard library. +It was :ref:`removed in Python 3.12 ` after +being deprecated in Python 3.4. + +The :ref:`removal notice ` includes guidance for +migrating code from :mod:`!imp` to :mod:`importlib`. + +The last version of Python that provided the :mod:`!imp` module was +`Python 3.11 `_. diff --git a/Doc/library/index.rst b/Doc/library/index.rst index 0b348ae6f5c8c0..951fbcf13fbb13 100644 --- a/Doc/library/index.rst +++ b/Doc/library/index.rst @@ -75,4 +75,5 @@ the `Python Package Index `_. unix.rst cmdline.rst superseded.rst + removed.rst security_warnings.rst diff --git a/Doc/library/mailcap.rst b/Doc/library/mailcap.rst new file mode 100644 index 00000000000000..4467da146a5a05 --- /dev/null +++ b/Doc/library/mailcap.rst @@ -0,0 +1,15 @@ +:mod:`!mailcap` --- Mailcap file handling +========================================= + +.. module:: mailcap + :synopsis: Removed in 3.13. + :deprecated: + +.. deprecated-removed:: 3.11 3.13 + +This module is no longer part of the Python standard library. +It was :ref:`removed in Python 3.13 ` after +being deprecated in Python 3.11. The removal was decided in :pep:`594`. + +The last version of Python that provided the :mod:`!mailcap` module was +`Python 3.12 `_. diff --git a/Doc/library/msilib.rst b/Doc/library/msilib.rst new file mode 100644 index 00000000000000..eb1ac551ded456 --- /dev/null +++ b/Doc/library/msilib.rst @@ -0,0 +1,15 @@ +:mod:`!msilib` --- Read and write Microsoft Installer files +=========================================================== + +.. module:: msilib + :synopsis: Removed in 3.13. + :deprecated: + +.. deprecated-removed:: 3.11 3.13 + +This module is no longer part of the Python standard library. +It was :ref:`removed in Python 3.13 ` after +being deprecated in Python 3.11. The removal was decided in :pep:`594`. + +The last version of Python that provided the :mod:`!msilib` module was +`Python 3.12 `_. diff --git a/Doc/library/nis.rst b/Doc/library/nis.rst new file mode 100644 index 00000000000000..dcc36dd43fc313 --- /dev/null +++ b/Doc/library/nis.rst @@ -0,0 +1,15 @@ +:mod:`!nis` --- Interface to Sun’s NIS (Yellow Pages) +===================================================== + +.. module:: nis + :synopsis: Removed in 3.13. + :deprecated: + +.. deprecated-removed:: 3.11 3.13 + +This module is no longer part of the Python standard library. +It was :ref:`removed in Python 3.13 ` after +being deprecated in Python 3.11. The removal was decided in :pep:`594`. + +The last version of Python that provided the :mod:`!nis` module was +`Python 3.12 `_. diff --git a/Doc/library/nntplib.rst b/Doc/library/nntplib.rst new file mode 100644 index 00000000000000..8053fe8cb8b9e1 --- /dev/null +++ b/Doc/library/nntplib.rst @@ -0,0 +1,15 @@ +:mod:`!nntplib` --- NNTP protocol client +======================================== + +.. module:: nntplib + :synopsis: Removed in 3.13. + :deprecated: + +.. deprecated-removed:: 3.11 3.13 + +This module is no longer part of the Python standard library. +It was :ref:`removed in Python 3.13 ` after +being deprecated in Python 3.11. The removal was decided in :pep:`594`. + +The last version of Python that provided the :mod:`!nntplib` module was +`Python 3.12 `_. diff --git a/Doc/library/ossaudiodev.rst b/Doc/library/ossaudiodev.rst new file mode 100644 index 00000000000000..320adbeff82539 --- /dev/null +++ b/Doc/library/ossaudiodev.rst @@ -0,0 +1,15 @@ +:mod:`!ossaudiodev` --- Access to OSS-compatible audio devices +============================================================== + +.. module:: ossaudiodev + :synopsis: Removed in 3.13. + :deprecated: + +.. deprecated-removed:: 3.11 3.13 + +This module is no longer part of the Python standard library. +It was :ref:`removed in Python 3.13 ` after +being deprecated in Python 3.11. The removal was decided in :pep:`594`. + +The last version of Python that provided the :mod:`!ossaudiodev` module was +`Python 3.12 `_. diff --git a/Doc/library/pipes.rst b/Doc/library/pipes.rst new file mode 100644 index 00000000000000..d9bcc3a5d99c9b --- /dev/null +++ b/Doc/library/pipes.rst @@ -0,0 +1,17 @@ +:mod:`!pipes` --- Interface to shell pipelines +============================================== + +.. module:: pipes + :synopsis: Removed in 3.13. + :deprecated: + +.. deprecated-removed:: 3.11 3.13 + +This module is no longer part of the Python standard library. +It was :ref:`removed in Python 3.13 ` after +being deprecated in Python 3.11. The removal was decided in :pep:`594`. + +Applications should use the :mod:`subprocess` module instead. + +The last version of Python that provided the :mod:`!pipes` module was +`Python 3.12 `_. diff --git a/Doc/library/removed.rst b/Doc/library/removed.rst new file mode 100644 index 00000000000000..4d75842eca1a03 --- /dev/null +++ b/Doc/library/removed.rst @@ -0,0 +1,39 @@ +:tocdepth: 1 + +.. _removed: + +*************** +Removed Modules +*************** + +The modules described in this chapter have been removed from the Python +standard library. They are documented here to help people find replacements. + + +.. toctree:: + :maxdepth: 1 + + aifc.rst + asynchat.rst + asyncore.rst + audioop.rst + cgi.rst + cgitb.rst + chunk.rst + crypt.rst + distutils.rst + imghdr.rst + imp.rst + mailcap.rst + msilib.rst + nis.rst + nntplib.rst + ossaudiodev.rst + pipes.rst + smtpd.rst + sndhdr.rst + spwd.rst + sunau.rst + telnetlib.rst + uu.rst + xdrlib.rst diff --git a/Doc/library/smtpd.rst b/Doc/library/smtpd.rst new file mode 100644 index 00000000000000..c704f4a241b469 --- /dev/null +++ b/Doc/library/smtpd.rst @@ -0,0 +1,18 @@ +:mod:`!smtpd` --- SMTP Server +============================= + +.. module:: smtpd + :synopsis: Removed in 3.12. + :deprecated: + +.. deprecated-removed:: 3.6 3.12 + +This module is no longer part of the Python standard library. +It was :ref:`removed in Python 3.12 ` after +being deprecated in Python 3.6. The removal was decided in :pep:`594`. + +A possible replacement is the third-party :pypi:`aiosmtpd` library. This +library is not maintained or supported by the Python core team. + +The last version of Python that provided the :mod:`!smtpd` module was +`Python 3.11 `_. diff --git a/Doc/library/sndhdr.rst b/Doc/library/sndhdr.rst new file mode 100644 index 00000000000000..6b71db4f6338a8 --- /dev/null +++ b/Doc/library/sndhdr.rst @@ -0,0 +1,19 @@ +:mod:`!sndhdr` --- Determine type of sound file +=============================================== + +.. module:: sndhdr + :synopsis: Removed in 3.13. + :deprecated: + +.. deprecated-removed:: 3.11 3.13 + +This module is no longer part of the Python standard library. +It was :ref:`removed in Python 3.13 ` after +being deprecated in Python 3.11. The removal was decided in :pep:`594`. + +Possible replacements are third-party modules from PyPI: +:pypi:`filetype`, :pypi:`puremagic`, or :pypi:`python-magic`. +These are not supported or maintained by the Python core team. + +The last version of Python that provided the :mod:`!sndhdr` module was +`Python 3.12 `_. diff --git a/Doc/library/spwd.rst b/Doc/library/spwd.rst new file mode 100644 index 00000000000000..c16854bb380e52 --- /dev/null +++ b/Doc/library/spwd.rst @@ -0,0 +1,18 @@ +:mod:`!spwd` --- The shadow password database +============================================= + +.. module:: spwd + :synopsis: Removed in 3.13. + :deprecated: + +.. deprecated-removed:: 3.11 3.13 + +This module is no longer part of the Python standard library. +It was :ref:`removed in Python 3.13 ` after +being deprecated in Python 3.11. The removal was decided in :pep:`594`. + +A possible replacement is the third-party library :pypi:`python-pam`. +This library is not supported or maintained by the Python core team. + +The last version of Python that provided the :mod:`!spwd` module was +`Python 3.12 `_. diff --git a/Doc/library/sunau.rst b/Doc/library/sunau.rst new file mode 100644 index 00000000000000..feb7768f8bdd68 --- /dev/null +++ b/Doc/library/sunau.rst @@ -0,0 +1,15 @@ +:mod:`!sunau` --- Read and write Sun AU files +============================================= + +.. module:: sunau + :synopsis: Removed in 3.13. + :deprecated: + +.. deprecated-removed:: 3.11 3.13 + +This module is no longer part of the Python standard library. +It was :ref:`removed in Python 3.13 ` after +being deprecated in Python 3.11. The removal was decided in :pep:`594`. + +The last version of Python that provided the :mod:`!sunau` module was +`Python 3.12 `_. diff --git a/Doc/library/telnetlib.rst b/Doc/library/telnetlib.rst new file mode 100644 index 00000000000000..6971ad33ff9751 --- /dev/null +++ b/Doc/library/telnetlib.rst @@ -0,0 +1,19 @@ +:mod:`!telnetlib` --- Telnet client +=================================== + +.. module:: telnetlib + :synopsis: Removed in 3.13. + :deprecated: + +.. deprecated-removed:: 3.11 3.13 + +This module is no longer part of the Python standard library. +It was :ref:`removed in Python 3.13 ` after +being deprecated in Python 3.11. The removal was decided in :pep:`594`. + +Possible replacements are third-party libraries from PyPI: :pypi:`telnetlib3` +or :pypi:`Exscript`. These are not supported or maintained by the Python core +team. + +The last version of Python that provided the :mod:`!telnetlib` module was +`Python 3.12 `_. diff --git a/Doc/library/uu.rst b/Doc/library/uu.rst new file mode 100644 index 00000000000000..0636d180294d47 --- /dev/null +++ b/Doc/library/uu.rst @@ -0,0 +1,15 @@ +:mod:`!uu` --- Encode and decode uuencode files +=============================================== + +.. module:: uu + :synopsis: Removed in 3.13. + :deprecated: + +.. deprecated-removed:: 3.11 3.13 + +This module is no longer part of the Python standard library. +It was :ref:`removed in Python 3.13 ` after +being deprecated in Python 3.11. The removal was decided in :pep:`594`. + +The last version of Python that provided the :mod:`!uu` module was +`Python 3.12 `_. diff --git a/Doc/library/xdrlib.rst b/Doc/library/xdrlib.rst new file mode 100644 index 00000000000000..59b801c8e4072e --- /dev/null +++ b/Doc/library/xdrlib.rst @@ -0,0 +1,15 @@ +:mod:`!xdrlib` --- Encode and decode XDR data +============================================= + +.. module:: xdrlib + :synopsis: Removed in 3.13. + :deprecated: + +.. deprecated-removed:: 3.11 3.13 + +This module is no longer part of the Python standard library. +It was :ref:`removed in Python 3.13 ` after +being deprecated in Python 3.11. The removal was decided in :pep:`594`. + +The last version of Python that provided the :mod:`!xdrlib` module was +`Python 3.12 `_. diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index d691185cb1ffc5..4fffc78a237791 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -1341,6 +1341,8 @@ Deprecated .. include:: ../deprecations/pending-removal-in-future.rst +.. _whatsnew312-removed: + Removed ======= @@ -1366,6 +1368,8 @@ configparser * :class:`configparser.ConfigParser` no longer has a ``readfp`` method. Use :meth:`~configparser.ConfigParser.read_file` instead. +.. _whatsnew312-removed-distutils: + distutils --------- @@ -1447,6 +1451,8 @@ importlib * ``importlib.abc.Finder``, ``pkgutil.ImpImporter``, and ``pkgutil.ImpLoader`` have been removed. (Contributed by Barry Warsaw in :gh:`98040`.) +.. _whatsnew312-removed-imp: + imp --- diff --git a/Misc/NEWS.d/next/Documentation/2024-11-09-19-43-10.gh-issue-126622.YacfDc.rst b/Misc/NEWS.d/next/Documentation/2024-11-09-19-43-10.gh-issue-126622.YacfDc.rst new file mode 100644 index 00000000000000..a2181b5712873b --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2024-11-09-19-43-10.gh-issue-126622.YacfDc.rst @@ -0,0 +1,3 @@ +Added stub pages for removed modules explaining their removal, where to find +replacements, and linking to the last Python version that supported them. +Contributed by Ned Batchelder. From a6d48e8f8323758771f5e130f67c9bdf7b4f25c5 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 11 Nov 2024 15:58:46 -0700 Subject: [PATCH 139/219] gh-76785: Improved Subinterpreters Compatibility with 3.12 (1/2) (gh-126704) These changes makes it easier to backport the _interpreters, _interpqueues, and _interpchannels modules to Python 3.12. This involves the following: * rename several structs and typedefs * add several typedefs * stop using the PyThreadState.state field directly in parking_lot.c --- Include/internal/pycore_crossinterp.h | 20 +++++----- .../pycore_crossinterp_data_registry.h | 18 ++++----- Include/internal/pycore_interp.h | 4 +- Include/internal/pycore_pystate.h | 6 +++ Include/internal/pycore_runtime.h | 4 +- Modules/_interpchannelsmodule.c | 2 +- Python/crossinterp.c | 37 +++++++++++-------- Python/crossinterp_data_lookup.h | 4 +- Python/parking_lot.c | 3 +- 9 files changed, 55 insertions(+), 43 deletions(-) diff --git a/Include/internal/pycore_crossinterp.h b/Include/internal/pycore_crossinterp.h index a7e71efc5daa49..66719796aeee22 100644 --- a/Include/internal/pycore_crossinterp.h +++ b/Include/internal/pycore_crossinterp.h @@ -39,14 +39,14 @@ extern int _Py_CallInInterpreterAndRawFree( /* cross-interpreter data */ /**************************/ -typedef struct _xid _PyXIData_t; -typedef PyObject *(*xid_newobjectfunc)(_PyXIData_t *); +typedef struct _xidata _PyXIData_t; +typedef PyObject *(*xid_newobjfunc)(_PyXIData_t *); typedef void (*xid_freefunc)(void *); // _PyXIData_t is similar to Py_buffer as an effectively // opaque struct that holds data outside the object machinery. This // is necessary to pass safely between interpreters in the same process. -struct _xid { +struct _xidata { // data is the cross-interpreter-safe derivation of a Python object // (see _PyObject_GetXIData). It will be NULL if the // new_object func (below) encodes the data. @@ -72,7 +72,7 @@ struct _xid { // interpreter given the data. The resulting object (a new // reference) will be equivalent to the original object. This field // is required. - xid_newobjectfunc new_object; + xid_newobjfunc new_object; // free is called when the data is released. If it is NULL then // nothing will be done to free the data. For some types this is // okay (e.g. bytes) and for those types this field should be set @@ -117,11 +117,11 @@ PyAPI_FUNC(int) _PyXIData_ReleaseAndRawFree(_PyXIData_t *); PyAPI_FUNC(void) _PyXIData_Init( _PyXIData_t *data, PyInterpreterState *interp, void *shared, PyObject *obj, - xid_newobjectfunc new_object); + xid_newobjfunc new_object); PyAPI_FUNC(int) _PyXIData_InitWithSize( _PyXIData_t *, PyInterpreterState *interp, const size_t, PyObject *, - xid_newobjectfunc); + xid_newobjfunc); PyAPI_FUNC(void) _PyXIData_Clear( PyInterpreterState *, _PyXIData_t *); // Normally the Init* functions are sufficient. The only time @@ -155,12 +155,12 @@ PyAPI_FUNC(void) _PyXIData_Clear( PyInterpreterState *, _PyXIData_t *); /* runtime state & lifecycle */ /*****************************/ -struct _xi_runtime_state { +typedef struct { // builtin types _PyXIData_lookup_t data_lookup; -}; +} _PyXI_global_state_t; -struct _xi_state { +typedef struct { // heap types _PyXIData_lookup_t data_lookup; @@ -171,7 +171,7 @@ struct _xi_state { // heap types PyObject *PyExc_NotShareableError; } exceptions; -}; +} _PyXI_state_t; extern PyStatus _PyXI_Init(PyInterpreterState *interp); extern void _PyXI_Fini(PyInterpreterState *interp); diff --git a/Include/internal/pycore_crossinterp_data_registry.h b/Include/internal/pycore_crossinterp_data_registry.h index 2990c6af62e952..04f25bc05fd1b8 100644 --- a/Include/internal/pycore_crossinterp_data_registry.h +++ b/Include/internal/pycore_crossinterp_data_registry.h @@ -7,30 +7,30 @@ // alternative would be to add a tp_* slot for a class's // xidatafunc. It would be simpler and more efficient. -struct _xidregitem; +struct _xid_regitem; -struct _xidregitem { - struct _xidregitem *prev; - struct _xidregitem *next; +typedef struct _xid_regitem { + struct _xid_regitem *prev; + struct _xid_regitem *next; /* This can be a dangling pointer, but only if weakref is set. */ PyTypeObject *cls; /* This is NULL for builtin types. */ PyObject *weakref; size_t refcount; xidatafunc getdata; -}; +} _PyXIData_regitem_t; -struct _xidregistry { +typedef struct { int global; /* builtin types or heap types */ int initialized; PyMutex mutex; - struct _xidregitem *head; -}; + _PyXIData_regitem_t *head; +} _PyXIData_registry_t; PyAPI_FUNC(int) _PyXIData_RegisterClass(PyTypeObject *, xidatafunc); PyAPI_FUNC(int) _PyXIData_UnregisterClass(PyTypeObject *); struct _xid_lookup_state { // XXX Remove this field once we have a tp_* slot. - struct _xidregistry registry; + _PyXIData_registry_t registry; }; diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index 9e3b4299693bbc..824b865eda60df 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -16,7 +16,7 @@ extern "C" { #include "pycore_code.h" // struct callable_cache #include "pycore_codecs.h" // struct codecs_state #include "pycore_context.h" // struct _Py_context_state -#include "pycore_crossinterp.h" // struct _xidregistry +#include "pycore_crossinterp.h" // _PyXI_state_t #include "pycore_dict_state.h" // struct _Py_dict_state #include "pycore_dtoa.h" // struct _dtoa_state #include "pycore_exceptions.h" // struct _Py_exc_state @@ -205,7 +205,7 @@ struct _is { freefunc co_extra_freefuncs[MAX_CO_EXTRA_USERS]; /* cross-interpreter data and utils */ - struct _xi_state xi; + _PyXI_state_t xi; #ifdef HAVE_FORK PyObject *before_forkers; diff --git a/Include/internal/pycore_pystate.h b/Include/internal/pycore_pystate.h index fade55945b7dbf..edcd75a55b686b 100644 --- a/Include/internal/pycore_pystate.h +++ b/Include/internal/pycore_pystate.h @@ -141,6 +141,12 @@ _PyThreadState_GET(void) #endif } +static inline int +_PyThreadState_IsAttached(PyThreadState *tstate) +{ + return (_Py_atomic_load_int_relaxed(&tstate->state) == _Py_THREAD_ATTACHED); +} + // Attaches the current thread to the interpreter. // // This may block while acquiring the GIL (if the GIL is enabled) or while diff --git a/Include/internal/pycore_runtime.h b/Include/internal/pycore_runtime.h index 7f592aa6cf9f05..2f2cec22cf1589 100644 --- a/Include/internal/pycore_runtime.h +++ b/Include/internal/pycore_runtime.h @@ -11,7 +11,7 @@ extern "C" { #include "pycore_atexit.h" // struct _atexit_runtime_state #include "pycore_audit.h" // _Py_AuditHookEntry #include "pycore_ceval_state.h" // struct _ceval_runtime_state -#include "pycore_crossinterp.h" // struct _xidregistry +#include "pycore_crossinterp.h" // _PyXI_global_state_t #include "pycore_debug_offsets.h" // _Py_DebugOffsets #include "pycore_faulthandler.h" // struct _faulthandler_runtime_state #include "pycore_floatobject.h" // struct _Py_float_runtime_state @@ -106,7 +106,7 @@ typedef struct pyruntimestate { tools. */ /* cross-interpreter data and utils */ - struct _xi_runtime_state xi; + _PyXI_global_state_t xi; struct _pymem_allocators allocators; struct _obmalloc_global_state obmalloc; diff --git a/Modules/_interpchannelsmodule.c b/Modules/_interpchannelsmodule.c index b8d7dfb87cce0e..cd3c5026938568 100644 --- a/Modules/_interpchannelsmodule.c +++ b/Modules/_interpchannelsmodule.c @@ -63,7 +63,7 @@ _globals (static struct globals): data (void *) obj (PyObject *) interpid (int64_t) - new_object (xid_newobjectfunc) + new_object (xid_newobjfunc) free (xid_freefunc) last (struct _channelitem *): ... diff --git a/Python/crossinterp.c b/Python/crossinterp.c index b7aa8da8ac550e..dfdb5f9d87a7c7 100644 --- a/Python/crossinterp.c +++ b/Python/crossinterp.c @@ -126,7 +126,7 @@ void _PyXIData_Init(_PyXIData_t *data, PyInterpreterState *interp, void *shared, PyObject *obj, - xid_newobjectfunc new_object) + xid_newobjfunc new_object) { assert(data != NULL); assert(new_object != NULL); @@ -150,7 +150,7 @@ int _PyXIData_InitWithSize(_PyXIData_t *data, PyInterpreterState *interp, const size_t size, PyObject *obj, - xid_newobjectfunc new_object) + xid_newobjfunc new_object) { assert(size > 0); // For now we always free the shared data in the same interpreter @@ -202,11 +202,9 @@ _check_xidata(PyThreadState *tstate, _PyXIData_t *data) } static inline void -_set_xid_lookup_failure(PyInterpreterState *interp, - PyObject *obj, const char *msg) +_set_xid_lookup_failure(_PyXI_state_t *state, PyObject *obj, const char *msg) { - exceptions_t *state = &_PyInterpreterState_GetXIState(interp)->exceptions; - PyObject *exctype = state->PyExc_NotShareableError; + PyObject *exctype = state->exceptions.PyExc_NotShareableError; assert(exctype != NULL); if (msg != NULL) { assert(obj == NULL); @@ -226,10 +224,11 @@ int _PyObject_CheckXIData(PyObject *obj) { PyInterpreterState *interp = PyInterpreterState_Get(); + _PyXI_state_t *state = _PyXI_GET_STATE(interp); xidatafunc getdata = lookup_getdata(interp, obj); if (getdata == NULL) { if (!PyErr_Occurred()) { - _set_xid_lookup_failure(interp, obj, NULL); + _set_xid_lookup_failure(state, obj, NULL); } return -1; } @@ -241,6 +240,7 @@ _PyObject_GetXIData(PyObject *obj, _PyXIData_t *data) { PyThreadState *tstate = PyThreadState_Get(); PyInterpreterState *interp = tstate->interp; + _PyXI_state_t *state = _PyXI_GET_STATE(interp); // Reset data before re-populating. *data = (_PyXIData_t){0}; @@ -252,7 +252,7 @@ _PyObject_GetXIData(PyObject *obj, _PyXIData_t *data) if (getdata == NULL) { Py_DECREF(obj); if (!PyErr_Occurred()) { - _set_xid_lookup_failure(interp, obj, NULL); + _set_xid_lookup_failure(state, obj, NULL); } return -1; } @@ -969,6 +969,7 @@ _PyXI_ClearExcInfo(_PyXI_excinfo *info) static int _PyXI_ApplyErrorCode(_PyXI_errcode code, PyInterpreterState *interp) { + _PyXI_state_t *state; assert(!PyErr_Occurred()); switch (code) { case _PyXI_ERR_NO_ERROR: _Py_FALLTHROUGH; @@ -999,7 +1000,8 @@ _PyXI_ApplyErrorCode(_PyXI_errcode code, PyInterpreterState *interp) "failed to apply namespace to __main__"); break; case _PyXI_ERR_NOT_SHAREABLE: - _set_xid_lookup_failure(interp, NULL, NULL); + state = _PyXI_GET_STATE(interp); + _set_xid_lookup_failure(state, NULL, NULL); break; default: #ifdef Py_DEBUG @@ -1061,7 +1063,8 @@ _PyXI_ApplyError(_PyXI_error *error) } else if (error->code == _PyXI_ERR_NOT_SHAREABLE) { // Propagate the exception directly. - _set_xid_lookup_failure(error->interp, NULL, error->uncaught.msg); + _PyXI_state_t *state = _PyXI_GET_STATE(error->interp); + _set_xid_lookup_failure(state, NULL, error->uncaught.msg); } else { // Raise an exception corresponding to the code. @@ -1606,9 +1609,9 @@ _propagate_not_shareable_error(_PyXI_session *session) return; } PyInterpreterState *interp = PyInterpreterState_Get(); - exceptions_t *state = &_PyInterpreterState_GetXIState(interp)->exceptions; - assert(state->PyExc_NotShareableError != NULL); - if (PyErr_ExceptionMatches(state->PyExc_NotShareableError)) { + _PyXI_state_t *state = _PyXI_GET_STATE(interp); + assert(state->exceptions.PyExc_NotShareableError != NULL); + if (PyErr_ExceptionMatches(state->exceptions.PyExc_NotShareableError)) { // We want to propagate the exception directly. session->_error_override = _PyXI_ERR_NOT_SHAREABLE; session->error_override = &session->_error_override; @@ -1779,11 +1782,13 @@ _PyXI_Exit(_PyXI_session *session) PyStatus _PyXI_Init(PyInterpreterState *interp) { + _PyXI_state_t *state = _PyXI_GET_STATE(interp); + // Initialize the XID lookup state (e.g. registry). if (_Py_IsMainInterpreter(interp)) { xid_lookup_init(&_PyXI_GET_GLOBAL_STATE(interp)->data_lookup); } - xid_lookup_init(&_PyXI_GET_STATE(interp)->data_lookup); + xid_lookup_init(&state->data_lookup); // Initialize exceptions.(heap types). // See _PyXI_InitTypes() for the static types. @@ -1801,12 +1806,14 @@ _PyXI_Init(PyInterpreterState *interp) void _PyXI_Fini(PyInterpreterState *interp) { + _PyXI_state_t *state = _PyXI_GET_STATE(interp); + // Finalize exceptions (heap types). // See _PyXI_FiniTypes() for the static types. fini_heap_exctypes(&_PyXI_GET_STATE(interp)->exceptions); // Finalize the XID lookup state (e.g. registry). - xid_lookup_fini(&_PyXI_GET_STATE(interp)->data_lookup); + xid_lookup_fini(&state->data_lookup); if (_Py_IsMainInterpreter(interp)) { xid_lookup_fini(&_PyXI_GET_GLOBAL_STATE(interp)->data_lookup); } diff --git a/Python/crossinterp_data_lookup.h b/Python/crossinterp_data_lookup.h index 88c662a3df00d6..9048f90cff160a 100644 --- a/Python/crossinterp_data_lookup.h +++ b/Python/crossinterp_data_lookup.h @@ -1,8 +1,8 @@ #include "pycore_weakref.h" // _PyWeakref_GET_REF() -typedef struct _xidregistry dlregistry_t; -typedef struct _xidregitem dlregitem_t; +typedef _PyXIData_registry_t dlregistry_t; +typedef _PyXIData_regitem_t dlregitem_t; // forward diff --git a/Python/parking_lot.c b/Python/parking_lot.c index bffc959e5d0978..8edf43235942ab 100644 --- a/Python/parking_lot.c +++ b/Python/parking_lot.c @@ -221,8 +221,7 @@ _PySemaphore_Wait(_PySemaphore *sema, PyTime_t timeout, int detach) PyThreadState *tstate = NULL; if (detach) { tstate = _PyThreadState_GET(); - if (tstate && _Py_atomic_load_int_relaxed(&tstate->state) == - _Py_THREAD_ATTACHED) { + if (tstate && _PyThreadState_IsAttached(tstate)) { // Only detach if we are attached PyEval_ReleaseThread(tstate); } From 494360afd00dc8f6b549f160525c3e86ec14905d Mon Sep 17 00:00:00 2001 From: Beomsoo Kim Date: Tue, 12 Nov 2024 09:11:40 +0900 Subject: [PATCH 140/219] gh-58749: Remove incorrect language spec claims about the global statement (GH-126523) * Removes erroneous explanation of the `global` statement restrictions; a name declared as global can be subsequently bound using any kind of name binding operation. * Updates `test_global.py` to also test various name-binding scenarios for global variables to ensure correct behavior --- Doc/reference/simple_stmts.rst | 21 +--- Lib/test/test_global.py | 213 ++++++++++++++++++++++++++++----- Misc/ACKS | 1 + 3 files changed, 191 insertions(+), 44 deletions(-) diff --git a/Doc/reference/simple_stmts.rst b/Doc/reference/simple_stmts.rst index 24df4a6ba7b678..a005395bfc402e 100644 --- a/Doc/reference/simple_stmts.rst +++ b/Doc/reference/simple_stmts.rst @@ -966,25 +966,14 @@ The :keyword:`!global` statement .. productionlist:: python-grammar global_stmt: "global" `identifier` ("," `identifier`)* -The :keyword:`global` statement is a declaration which holds for the entire -current code block. It means that the listed identifiers are to be interpreted -as globals. It would be impossible to assign to a global variable without +The :keyword:`global` causes the listed identifiers to be interpreted +as globals. It would be impossible to assign to a global variable without :keyword:`!global`, although free variables may refer to globals without being declared global. -Names listed in a :keyword:`global` statement must not be used in the same code -block textually preceding that :keyword:`!global` statement. - -Names listed in a :keyword:`global` statement must not be defined as formal -parameters, or as targets in :keyword:`with` statements or :keyword:`except` clauses, or in a :keyword:`for` target list, :keyword:`class` -definition, function definition, :keyword:`import` statement, or -:term:`variable annotations `. - -.. impl-detail:: - - The current implementation does not enforce some of these restrictions, but - programs should not abuse this freedom, as future implementations may enforce - them or silently change the meaning of the program. +The global statement applies to the entire scope of a function or +class body. A :exc:`SyntaxError` is raised if a variable is used or +assigned to prior to its global declaration in the scope. .. index:: pair: built-in function; exec diff --git a/Lib/test/test_global.py b/Lib/test/test_global.py index f5b38c25ea0728..11d0bd54e8b69b 100644 --- a/Lib/test/test_global.py +++ b/Lib/test/test_global.py @@ -1,7 +1,19 @@ -"""Verify that warnings are issued for global statements following use.""" +"""This module includes tests for syntax errors that occur when a name +declared as `global` is used in ways that violate the language +specification, such as after assignment, usage, or annotation. The tests +verify that syntax errors are correctly raised for improper `global` +statements following variable use or assignment within functions. +Additionally, it tests various name-binding scenarios for global +variables to ensure correct behavior. +See `test_scope.py` for additional related behavioral tests covering +variable scoping and usage in different contexts. +""" + +import contextlib from test.support import check_syntax_error from test.support.warnings_helper import check_warnings +from types import SimpleNamespace import unittest import warnings @@ -12,40 +24,185 @@ def setUp(self): self.enterContext(check_warnings()) warnings.filterwarnings("error", module="") - def test1(self): - prog_text_1 = """\ -def wrong1(): - a = 1 - b = 2 - global a - global b + ###################################################### + ### Syntax error cases as covered in Python/symtable.c + ###################################################### + + def test_name_param(self): + prog_text = """\ +def fn(name_param): + global name_param """ - check_syntax_error(self, prog_text_1, lineno=4, offset=5) + check_syntax_error(self, prog_text, lineno=2, offset=5) - def test2(self): - prog_text_2 = """\ -def wrong2(): - print(x) - global x + def test_name_after_assign(self): + prog_text = """\ +def fn(): + name_assign = 1 + global name_assign """ - check_syntax_error(self, prog_text_2, lineno=3, offset=5) + check_syntax_error(self, prog_text, lineno=3, offset=5) - def test3(self): - prog_text_3 = """\ -def wrong3(): - print(x) - x = 2 - global x + def test_name_after_use(self): + prog_text = """\ +def fn(): + print(name_use) + global name_use """ - check_syntax_error(self, prog_text_3, lineno=4, offset=5) + check_syntax_error(self, prog_text, lineno=3, offset=5) - def test4(self): - prog_text_4 = """\ -global x -x = 2 + def test_name_annot(self): + prog_text_3 = """\ +def fn(): + name_annot: int + global name_annot """ - # this should work - compile(prog_text_4, "", "exec") + check_syntax_error(self, prog_text_3, lineno=3, offset=5) + + ############################################################# + ### Tests for global variables across all name binding cases, + ### as described in executionmodel.rst + ############################################################# + + def test_assignment_statement(self): + global name_assignment_statement + value = object() + name_assignment_statement = value + self.assertIs(globals()["name_assignment_statement"], value) + del name_assignment_statement + + def test_unpacking_assignment(self): + global name_unpacking_assignment + value = object() + _, name_unpacking_assignment = [None, value] + self.assertIs(globals()["name_unpacking_assignment"], value) + del name_unpacking_assignment + + def test_assignment_expression(self): + global name_assignment_expression + value = object() + if name_assignment_expression := value: + pass + self.assertIs(globals()["name_assignment_expression"], value) + del name_assignment_expression + + def test_iteration_variable(self): + global name_iteration_variable + value = object() + for name_iteration_variable in [value]: + pass + self.assertIs(globals()["name_iteration_variable"], value) + del name_iteration_variable + + def test_func_def(self): + global name_func_def + + def name_func_def(): + pass + + value = name_func_def + self.assertIs(globals()["name_func_def"], value) + del name_func_def + + def test_class_def(self): + global name_class_def + + class name_class_def: + pass + + value = name_class_def + self.assertIs(globals()["name_class_def"], value) + del name_class_def + + def test_type_alias(self): + global name_type_alias + type name_type_alias = tuple[int, int] + value = name_type_alias + self.assertIs(globals()["name_type_alias"], value) + del name_type_alias + + def test_caught_exception(self): + global name_caught_exc + + try: + 1 / 0 + except ZeroDivisionError as name_caught_exc: + value = name_caught_exc + # `name_caught_exc` is cleared automatically after the except block + self.assertIs(globals()["name_caught_exc"], value) + + def test_caught_exception_group(self): + global name_caught_exc_group + try: + try: + 1 / 0 + except ZeroDivisionError as exc: + raise ExceptionGroup("eg", [exc]) + except* ZeroDivisionError as name_caught_exc_group: + value = name_caught_exc_group + # `name_caught_exc` is cleared automatically after the except block + self.assertIs(globals()["name_caught_exc_group"], value) + + def test_enter_result(self): + global name_enter_result + value = object() + with contextlib.nullcontext(value) as name_enter_result: + pass + self.assertIs(globals()["name_enter_result"], value) + del name_enter_result + + def test_import_result(self): + global name_import_result + value = contextlib + import contextlib as name_import_result + + self.assertIs(globals()["name_import_result"], value) + del name_import_result + + def test_match(self): + global name_match + value = object() + match value: + case name_match: + pass + self.assertIs(globals()["name_match"], value) + del name_match + + def test_match_as(self): + global name_match_as + value = object() + match value: + case _ as name_match_as: + pass + self.assertIs(globals()["name_match_as"], value) + del name_match_as + + def test_match_seq(self): + global name_match_seq + value = object() + match (None, value): + case (_, name_match_seq): + pass + self.assertIs(globals()["name_match_seq"], value) + del name_match_seq + + def test_match_map(self): + global name_match_map + value = object() + match {"key": value}: + case {"key": name_match_map}: + pass + self.assertIs(globals()["name_match_map"], value) + del name_match_map + + def test_match_attr(self): + global name_match_attr + value = object() + match SimpleNamespace(key=value): + case SimpleNamespace(key=name_match_attr): + pass + self.assertIs(globals()["name_match_attr"], value) + del name_match_attr def setUpModule(): diff --git a/Misc/ACKS b/Misc/ACKS index 1a25088052f4e1..6b5d7bf80cee6d 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -953,6 +953,7 @@ Sanyam Khurana Tyler Kieft Mads Kiilerich Jason Killen +Beomsoo Bombs Kim Derek D. Kim Gihwan Kim Jan Kim From c45be8aa71ad886e12e8c897274498e4d828c9a5 Mon Sep 17 00:00:00 2001 From: Diego Russo Date: Tue, 12 Nov 2024 01:20:10 +0000 Subject: [PATCH 141/219] GH-126195: Use M1 JIT memory protection APIs (GH-126196) --- ...4-10-30-18-16-10.gh-issue-126195.6ezBpr.rst | 1 + Python/jit.c | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-10-30-18-16-10.gh-issue-126195.6ezBpr.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-10-30-18-16-10.gh-issue-126195.6ezBpr.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-10-30-18-16-10.gh-issue-126195.6ezBpr.rst new file mode 100644 index 00000000000000..01424d8a545d78 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-10-30-18-16-10.gh-issue-126195.6ezBpr.rst @@ -0,0 +1 @@ +Improve JIT performance by 1.4% on macOS Apple Silicon by using platform-specific memory protection APIs. Patch by Diego Russo. diff --git a/Python/jit.c b/Python/jit.c index 90f693dfb7c41b..7dd0da7a45055a 100644 --- a/Python/jit.c +++ b/Python/jit.c @@ -58,7 +58,12 @@ jit_alloc(size_t size) int failed = memory == NULL; #else int flags = MAP_ANONYMOUS | MAP_PRIVATE; - unsigned char *memory = mmap(NULL, size, PROT_READ | PROT_WRITE, flags, -1, 0); + int prot = PROT_READ | PROT_WRITE; +# ifdef MAP_JIT + flags |= MAP_JIT; + prot |= PROT_EXEC; +# endif + unsigned char *memory = mmap(NULL, size, prot, flags, -1, 0); int failed = memory == MAP_FAILED; #endif if (failed) { @@ -102,8 +107,11 @@ mark_executable(unsigned char *memory, size_t size) int old; int failed = !VirtualProtect(memory, size, PAGE_EXECUTE_READ, &old); #else + int failed = 0; __builtin___clear_cache((char *)memory, (char *)memory + size); - int failed = mprotect(memory, size, PROT_EXEC | PROT_READ); +#ifndef MAP_JIT + failed = mprotect(memory, size, PROT_EXEC | PROT_READ); +#endif #endif if (failed) { jit_error("unable to protect executable memory"); @@ -499,6 +507,9 @@ _PyJIT_Compile(_PyExecutorObject *executor, const _PyUOpInstruction trace[], siz if (memory == NULL) { return -1; } +#ifdef MAP_JIT + pthread_jit_write_protect_np(0); +#endif // Update the offsets of each instruction: for (size_t i = 0; i < length; i++) { state.instruction_starts[i] += (uintptr_t)memory; @@ -529,6 +540,9 @@ _PyJIT_Compile(_PyExecutorObject *executor, const _PyUOpInstruction trace[], siz data += group->data_size; assert(code == memory + code_size); assert(data == memory + code_size + data_size); +#ifdef MAP_JIT + pthread_jit_write_protect_np(1); +#endif if (mark_executable(memory, total_size)) { jit_free(memory, total_size); return -1; From 599bfc986d2c32c507822ef785b63619bd616608 Mon Sep 17 00:00:00 2001 From: Sahil Prajapati Date: Tue, 12 Nov 2024 12:18:38 +0530 Subject: [PATCH 142/219] gh-84852: Add MIME types for .eot, ,otf, .ttf, .woff and .woff2 fonts (#20199) Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- Doc/whatsnew/3.14.rst | 10 ++++++++++ Lib/mimetypes.py | 5 +++++ Lib/test/test_mimetypes.py | 5 +++++ .../2020-05-19-01-12-47.gh-issue-84852.FEjHJW.rst | 2 ++ 4 files changed, 22 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2020-05-19-01-12-47.gh-issue-84852.FEjHJW.rst diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index f9b219828d3d94..9c032637d651ec 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -366,6 +366,16 @@ json mimetypes --------- +* Add MS and :rfc:`8081` MIME types for fonts: + + * Embedded OpenType: ``application/vnd.ms-fontobject`` + * OpenType Layout (OTF) ``font/otf`` + * TrueType: ``font/ttf`` + * WOFF 1.0 ``font/woff`` + * WOFF 2.0 ``font/woff2`` + + (Contributed by Sahil Prajapati and Hugo van Kemenade in :gh:`84852`.) + * Add :rfc:`9559` MIME types for Matroska audiovisual data container structures, containing: diff --git a/Lib/mimetypes.py b/Lib/mimetypes.py index fd343a78c98ae1..210d2264757d08 100644 --- a/Lib/mimetypes.py +++ b/Lib/mimetypes.py @@ -479,6 +479,7 @@ def _default_mime_types(): '.m3u8' : 'application/vnd.apple.mpegurl', '.xls' : 'application/vnd.ms-excel', '.xlb' : 'application/vnd.ms-excel', + '.eot' : 'application/vnd.ms-fontobject', '.ppt' : 'application/vnd.ms-powerpoint', '.pot' : 'application/vnd.ms-powerpoint', '.ppa' : 'application/vnd.ms-powerpoint', @@ -543,6 +544,10 @@ def _default_mime_types(): '.aiff' : 'audio/x-aiff', '.ra' : 'audio/x-pn-realaudio', '.wav' : 'audio/x-wav', + '.otf' : 'font/otf', + '.ttf' : 'font/ttf', + '.woff' : 'font/woff', + '.woff2' : 'font/woff2', '.avif' : 'image/avif', '.bmp' : 'image/bmp', '.gif' : 'image/gif', diff --git a/Lib/test/test_mimetypes.py b/Lib/test/test_mimetypes.py index 8d3e8fcafb6740..c4bb8dfb1a7422 100644 --- a/Lib/test/test_mimetypes.py +++ b/Lib/test/test_mimetypes.py @@ -228,12 +228,17 @@ def check_extensions(): ("application/postscript", ".ps"), ("application/vnd.apple.mpegurl", ".m3u"), ("application/vnd.ms-excel", ".xls"), + ("application/vnd.ms-fontobject", ".eot"), ("application/vnd.ms-powerpoint", ".ppt"), ("application/x-texinfo", ".texi"), ("application/x-troff", ".roff"), ("application/xml", ".xsl"), ("audio/matroska", ".mka"), ("audio/mpeg", ".mp3"), + ("font/otf", ".otf"), + ("font/ttf", ".ttf"), + ("font/woff", ".woff"), + ("font/woff2", ".woff2"), ("image/avif", ".avif"), ("image/webp", ".webp"), ("image/jpeg", ".jpg"), diff --git a/Misc/NEWS.d/next/Library/2020-05-19-01-12-47.gh-issue-84852.FEjHJW.rst b/Misc/NEWS.d/next/Library/2020-05-19-01-12-47.gh-issue-84852.FEjHJW.rst new file mode 100644 index 00000000000000..2581697591af62 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-05-19-01-12-47.gh-issue-84852.FEjHJW.rst @@ -0,0 +1,2 @@ +Add MIME types for MS Embedded OpenType, OpenType Layout, TrueType, +WOFF 1.0 and 2.0 fonts. Patch by Sahil Prajapati and Hugo van Kemenade. From 0052a8c638518447baf39ae02b6ff6a309efd4ce Mon Sep 17 00:00:00 2001 From: sobolevn Date: Tue, 12 Nov 2024 10:51:13 +0300 Subject: [PATCH 143/219] Fix error message of "Check if Autoconf files are up to date" job (#126683) --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f63c4606220494..b769bba72816d6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -76,7 +76,7 @@ jobs: # Check for changes in regenerated files if test -n "$changes"; then echo "Generated files not up to date." - echo "Perhaps you forgot to run make regen-all or build.bat --regen. ;)" + echo "Perhaps you forgot to run make regen-configure ;)" echo "configure files must be regenerated with a specific version of autoconf." echo "$changes" echo "" From feb3e0b19cb03f06364a3f5e970f0861b8883d1c Mon Sep 17 00:00:00 2001 From: Stephen Morton Date: Tue, 12 Nov 2024 01:17:07 -0800 Subject: [PATCH 144/219] gh-126699: allow AsyncIterator to be used as a base for Protocols (#126702) --- Lib/test/test_typing.py | 3 +++ Lib/typing.py | 3 ++- .../Library/2024-11-11-13-24-22.gh-issue-126699.ONGbMd.rst | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2024-11-11-13-24-22.gh-issue-126699.ONGbMd.rst diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 2f1f9e86a0bce4..244ce1e5da9bd2 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -4255,6 +4255,9 @@ class CustomProtocol(TestCase, Protocol): class CustomContextManager(typing.ContextManager, Protocol): pass + class CustomAsyncIterator(typing.AsyncIterator, Protocol): + pass + def test_non_runtime_protocol_isinstance_check(self): class P(Protocol): x: int diff --git a/Lib/typing.py b/Lib/typing.py index c924c767042552..8e6381033fd28e 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1940,7 +1940,8 @@ def _allow_reckless_class_checks(depth=2): _PROTO_ALLOWLIST = { 'collections.abc': [ 'Callable', 'Awaitable', 'Iterable', 'Iterator', 'AsyncIterable', - 'Hashable', 'Sized', 'Container', 'Collection', 'Reversible', 'Buffer', + 'AsyncIterator', 'Hashable', 'Sized', 'Container', 'Collection', + 'Reversible', 'Buffer', ], 'contextlib': ['AbstractContextManager', 'AbstractAsyncContextManager'], } diff --git a/Misc/NEWS.d/next/Library/2024-11-11-13-24-22.gh-issue-126699.ONGbMd.rst b/Misc/NEWS.d/next/Library/2024-11-11-13-24-22.gh-issue-126699.ONGbMd.rst new file mode 100644 index 00000000000000..9741294487d716 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-11-11-13-24-22.gh-issue-126699.ONGbMd.rst @@ -0,0 +1 @@ +Allow :class:`collections.abc.AsyncIterator` to be a base for Protocols. From f223efb2a2d6a3e86556be7295cbbd3ef839f489 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Tue, 12 Nov 2024 13:23:57 +0300 Subject: [PATCH 145/219] gh-126525: Fix `makeunicodedata.py` output on macOS and Windows (#126526) --- Tools/unicode/makeunicodedata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tools/unicode/makeunicodedata.py b/Tools/unicode/makeunicodedata.py index c94de7f9377b74..889ae8fc869b8a 100644 --- a/Tools/unicode/makeunicodedata.py +++ b/Tools/unicode/makeunicodedata.py @@ -35,7 +35,7 @@ from textwrap import dedent from typing import Iterator, List, Optional, Set, Tuple -SCRIPT = sys.argv[0] +SCRIPT = os.path.normpath(sys.argv[0]) VERSION = "3.3" # The Unicode Database From 0ef84b0e2bf511b2cb5268a9ce64d7f2209fb3c4 Mon Sep 17 00:00:00 2001 From: Daehee Kim Date: Tue, 12 Nov 2024 21:01:56 +0900 Subject: [PATCH 146/219] gh-126209: Fix inconsistency of `skip_file_prefixes` in `warnings.warn`'s C and Python implementations (GH-126329) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Terry Jan Reedy Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> Co-authored-by: Kirill Podoprigora --- Lib/test/test_warnings/__init__.py | 12 ++++++++++++ Lib/test/test_warnings/data/stacklevel.py | 10 ++++++---- .../2024-11-02-18-01-31.gh-issue-126209.2ZIhrS.rst | 3 +++ Python/_warnings.c | 3 ++- 4 files changed, 23 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-11-02-18-01-31.gh-issue-126209.2ZIhrS.rst diff --git a/Lib/test/test_warnings/__init__.py b/Lib/test/test_warnings/__init__.py index 8b59630717e790..4e3c877896f295 100644 --- a/Lib/test/test_warnings/__init__.py +++ b/Lib/test/test_warnings/__init__.py @@ -533,6 +533,18 @@ def test_skip_file_prefixes(self): warning_tests.package("prefix02", stacklevel=3) self.assertIn("unittest", w[-1].filename) + def test_skip_file_prefixes_file_path(self): + # see: gh-126209 + with warnings_state(self.module): + skipped = warning_tests.__file__ + with original_warnings.catch_warnings( + record=True, module=self.module, + ) as w: + warning_tests.outer("msg", skip_file_prefixes=(skipped,)) + + self.assertEqual(len(w), 1) + self.assertNotEqual(w[-1].filename, skipped) + def test_skip_file_prefixes_type_errors(self): with warnings_state(self.module): warn = warning_tests.warnings.warn diff --git a/Lib/test/test_warnings/data/stacklevel.py b/Lib/test/test_warnings/data/stacklevel.py index c6dd24733b3b74..fe36242d3d20c2 100644 --- a/Lib/test/test_warnings/data/stacklevel.py +++ b/Lib/test/test_warnings/data/stacklevel.py @@ -4,11 +4,13 @@ import warnings from test.test_warnings.data import package_helper -def outer(message, stacklevel=1): - inner(message, stacklevel) -def inner(message, stacklevel=1): - warnings.warn(message, stacklevel=stacklevel) +def outer(message, stacklevel=1, skip_file_prefixes=()): + inner(message, stacklevel, skip_file_prefixes) + +def inner(message, stacklevel=1, skip_file_prefixes=()): + warnings.warn(message, stacklevel=stacklevel, + skip_file_prefixes=skip_file_prefixes) def package(message, *, stacklevel): package_helper.inner_api(message, stacklevel=stacklevel, diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-02-18-01-31.gh-issue-126209.2ZIhrS.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-02-18-01-31.gh-issue-126209.2ZIhrS.rst new file mode 100644 index 00000000000000..727f7f8180ab22 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-02-18-01-31.gh-issue-126209.2ZIhrS.rst @@ -0,0 +1,3 @@ +Fix an issue with ``skip_file_prefixes`` parameter which resulted in an inconsistent +behaviour between the C and Python implementations of :func:`warnings.warn`. +Patch by Daehee Kim. diff --git a/Python/_warnings.c b/Python/_warnings.c index 3f9e73b5376223..e05ba99e8eaec4 100644 --- a/Python/_warnings.c +++ b/Python/_warnings.c @@ -803,7 +803,8 @@ is_filename_to_skip(PyObject *filename, PyTupleObject *skip_file_prefixes) for (Py_ssize_t idx = 0; idx < prefixes; ++idx) { PyObject *prefix = PyTuple_GET_ITEM(skip_file_prefixes, idx); - Py_ssize_t found = PyUnicode_Tailmatch(filename, prefix, 0, -1, -1); + Py_ssize_t found = PyUnicode_Tailmatch(filename, prefix, + 0, PY_SSIZE_T_MAX, -1); if (found == 1) { return true; } From 37c57dfad12744608091653fd753a1f770e2479b Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Tue, 12 Nov 2024 18:01:34 +0530 Subject: [PATCH 147/219] gh-126405: fix use-after-free in `_asyncio.Future.remove_done_callback` (#126733) --- Modules/_asynciomodule.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 617a3dca35d9c2..f883125a2c70b2 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -1017,8 +1017,10 @@ _asyncio_Future_remove_done_callback_impl(FutureObj *self, PyTypeObject *cls, if (len == 1) { PyObject *cb_tup = PyList_GET_ITEM(self->fut_callbacks, 0); + Py_INCREF(cb_tup); int cmp = PyObject_RichCompareBool( PyTuple_GET_ITEM(cb_tup, 0), fn, Py_EQ); + Py_DECREF(cb_tup); if (cmp == -1) { return NULL; } From 6e3bb8a91380ba98d704f2dca8e98923c0abc8a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Tue, 12 Nov 2024 14:10:10 +0100 Subject: [PATCH 148/219] gh-126595: fix a crash when calling `itertools.count(sys.maxsize)` (#126617) --- Lib/test/test_itertools.py | 8 ++++++++ .../2024-11-09-10-31-10.gh-issue-126595.A-7MyC.rst | 2 ++ Modules/itertoolsmodule.c | 3 +++ 3 files changed, 13 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2024-11-09-10-31-10.gh-issue-126595.A-7MyC.rst diff --git a/Lib/test/test_itertools.py b/Lib/test/test_itertools.py index a52e1d3fa142d9..b94d688738f9e8 100644 --- a/Lib/test/test_itertools.py +++ b/Lib/test/test_itertools.py @@ -494,6 +494,8 @@ def test_count(self): self.assertEqual(take(2, zip('abc',count(-3))), [('a', -3), ('b', -2)]) self.assertRaises(TypeError, count, 2, 3, 4) self.assertRaises(TypeError, count, 'a') + self.assertEqual(take(3, count(maxsize)), + [maxsize, maxsize + 1, maxsize + 2]) self.assertEqual(take(10, count(maxsize-5)), list(range(maxsize-5, maxsize+5))) self.assertEqual(take(10, count(-maxsize-5)), @@ -540,6 +542,12 @@ def test_count_with_step(self): self.assertEqual(take(20, count(-maxsize-15, 3)), take(20, range(-maxsize-15,-maxsize+100, 3))) self.assertEqual(take(3, count(10, maxsize+5)), list(range(10, 10+3*(maxsize+5), maxsize+5))) + self.assertEqual(take(3, count(maxsize, 2)), + [maxsize, maxsize + 2, maxsize + 4]) + self.assertEqual(take(3, count(maxsize, maxsize)), + [maxsize, 2 * maxsize, 3 * maxsize]) + self.assertEqual(take(3, count(-maxsize, maxsize)), + [-maxsize, 0, maxsize]) self.assertEqual(take(3, count(2, 1.25)), [2, 3.25, 4.5]) self.assertEqual(take(3, count(2, 3.25-4j)), [2, 5.25-4j, 8.5-8j]) self.assertEqual(take(3, count(Decimal('1.1'), Decimal('.1'))), diff --git a/Misc/NEWS.d/next/Library/2024-11-09-10-31-10.gh-issue-126595.A-7MyC.rst b/Misc/NEWS.d/next/Library/2024-11-09-10-31-10.gh-issue-126595.A-7MyC.rst new file mode 100644 index 00000000000000..84a5dc0b23922f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-11-09-10-31-10.gh-issue-126595.A-7MyC.rst @@ -0,0 +1,2 @@ +Fix a crash when instantiating :class:`itertools.count` with an initial +count of :data:`sys.maxsize` on debug builds. Patch by Bénédikt Tran. diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c index 1201fa094902d7..78fbdcdf77a923 100644 --- a/Modules/itertoolsmodule.c +++ b/Modules/itertoolsmodule.c @@ -3291,6 +3291,9 @@ itertools_count_impl(PyTypeObject *type, PyObject *long_cnt, PyErr_Clear(); fast_mode = 0; } + else if (cnt == PY_SSIZE_T_MAX) { + fast_mode = 0; + } } } else { cnt = 0; From abb90ba46c597a1b192027e914ad312dd62d2462 Mon Sep 17 00:00:00 2001 From: Sayandip Dutta Date: Tue, 12 Nov 2024 18:41:58 +0530 Subject: [PATCH 149/219] gh-125916: Allow functools.reduce() 'initial' to be a keyword argument (#125917) --- Doc/library/functools.rst | 7 ++- Doc/whatsnew/3.14.rst | 5 +++ Lib/functools.py | 2 +- Lib/test/test_functools.py | 23 ++++++++++ Misc/ACKS | 1 + ...-10-24-13-40-20.gh-issue-126916.MAgz6D.rst | 2 + Modules/_functoolsmodule.c | 4 +- Modules/clinic/_functoolsmodule.c.h | 45 +++++++++++++++---- 8 files changed, 76 insertions(+), 13 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-10-24-13-40-20.gh-issue-126916.MAgz6D.rst diff --git a/Doc/library/functools.rst b/Doc/library/functools.rst index e26a2226aa947a..a9aceee4170004 100644 --- a/Doc/library/functools.rst +++ b/Doc/library/functools.rst @@ -453,7 +453,7 @@ The :mod:`functools` module defines the following functions: .. versionadded:: 3.4 -.. function:: reduce(function, iterable[, initial], /) +.. function:: reduce(function, iterable, /[, initial]) Apply *function* of two arguments cumulatively to the items of *iterable*, from left to right, so as to reduce the iterable to a single value. For example, @@ -468,7 +468,7 @@ The :mod:`functools` module defines the following functions: initial_missing = object() - def reduce(function, iterable, initial=initial_missing, /): + def reduce(function, iterable, /, initial=initial_missing): it = iter(iterable) if initial is initial_missing: value = next(it) @@ -481,6 +481,9 @@ The :mod:`functools` module defines the following functions: See :func:`itertools.accumulate` for an iterator that yields all intermediate values. + .. versionchanged:: next + *initial* is now supported as a keyword argument. + .. decorator:: singledispatch Transform a function into a :term:`single-dispatch value + reduce(function, iterable, /[, initial]) -> value Apply a function of two arguments cumulatively to the items of an iterable, from left to right. diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index d590af090abc6e..6d60f6941c4c5d 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -1005,6 +1005,29 @@ def __getitem__(self, i): d = {"one": 1, "two": 2, "three": 3} self.assertEqual(self.reduce(add, d), "".join(d.keys())) + # test correctness of keyword usage of `initial` in `reduce` + def test_initial_keyword(self): + def add(x, y): + return x + y + self.assertEqual( + self.reduce(add, ['a', 'b', 'c'], ''), + self.reduce(add, ['a', 'b', 'c'], initial=''), + ) + self.assertEqual( + self.reduce(add, [['a', 'c'], [], ['d', 'w']], []), + self.reduce(add, [['a', 'c'], [], ['d', 'w']], initial=[]), + ) + self.assertEqual( + self.reduce(lambda x, y: x*y, range(2,8), 1), + self.reduce(lambda x, y: x*y, range(2,8), initial=1), + ) + self.assertEqual( + self.reduce(lambda x, y: x*y, range(2,21), 1), + self.reduce(lambda x, y: x*y, range(2,21), initial=1), + ) + self.assertRaises(TypeError, self.reduce, add, [0, 1], initial="") + self.assertEqual(self.reduce(42, "", initial="1"), "1") # func is never called with one item + @unittest.skipUnless(c_functools, 'requires the C _functools module') class TestReduceC(TestReduce, unittest.TestCase): diff --git a/Misc/ACKS b/Misc/ACKS index 6b5d7bf80cee6d..dce322fc867743 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -485,6 +485,7 @@ Luke Dunstan Virgil Dupras Bruno Dupuis Andy Dustman +Sayandip Dutta Gary Duzan Eugene Dvurechenski Karmen Dykstra diff --git a/Misc/NEWS.d/next/Library/2024-10-24-13-40-20.gh-issue-126916.MAgz6D.rst b/Misc/NEWS.d/next/Library/2024-10-24-13-40-20.gh-issue-126916.MAgz6D.rst new file mode 100644 index 00000000000000..cbe2fc166ba6af --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-10-24-13-40-20.gh-issue-126916.MAgz6D.rst @@ -0,0 +1,2 @@ +Allow the *initial* parameter of :func:`functools.reduce` to be passed as a keyword argument. +Patch by Sayandip Dutta. diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index d2afe1a1bea018..5e0cf057dcd1a2 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -937,8 +937,8 @@ _functools.reduce function as func: object iterable as seq: object - initial as result: object = NULL / + initial as result: object = NULL Apply a function of two arguments cumulatively to the items of an iterable, from left to right. @@ -953,7 +953,7 @@ calculates ((((1 + 2) + 3) + 4) + 5). static PyObject * _functools_reduce_impl(PyObject *module, PyObject *func, PyObject *seq, PyObject *result) -/*[clinic end generated code: output=30d898fe1267c79d input=d233c2670cba7f66]*/ +/*[clinic end generated code: output=30d898fe1267c79d input=1511e9a8c38581ac]*/ { PyObject *args, *it; diff --git a/Modules/clinic/_functoolsmodule.c.h b/Modules/clinic/_functoolsmodule.c.h index 760877928db60d..afd5eb4eb12b78 100644 --- a/Modules/clinic/_functoolsmodule.c.h +++ b/Modules/clinic/_functoolsmodule.c.h @@ -69,7 +69,7 @@ _functools_cmp_to_key(PyObject *module, PyObject *const *args, Py_ssize_t nargs, } PyDoc_STRVAR(_functools_reduce__doc__, -"reduce($module, function, iterable, initial=, /)\n" +"reduce($module, function, iterable, /, initial=)\n" "--\n" "\n" "Apply a function of two arguments cumulatively to the items of an iterable, from left to right.\n" @@ -82,30 +82,59 @@ PyDoc_STRVAR(_functools_reduce__doc__, "calculates ((((1 + 2) + 3) + 4) + 5)."); #define _FUNCTOOLS_REDUCE_METHODDEF \ - {"reduce", _PyCFunction_CAST(_functools_reduce), METH_FASTCALL, _functools_reduce__doc__}, + {"reduce", _PyCFunction_CAST(_functools_reduce), METH_FASTCALL|METH_KEYWORDS, _functools_reduce__doc__}, static PyObject * _functools_reduce_impl(PyObject *module, PyObject *func, PyObject *seq, PyObject *result); static PyObject * -_functools_reduce(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +_functools_reduce(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(initial), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"", "", "initial", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "reduce", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[3]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 2; PyObject *func; PyObject *seq; PyObject *result = NULL; - if (!_PyArg_CheckPositional("reduce", nargs, 2, 3)) { + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { goto exit; } func = args[0]; seq = args[1]; - if (nargs < 3) { - goto skip_optional; + if (!noptargs) { + goto skip_optional_pos; } result = args[2]; -skip_optional: +skip_optional_pos: return_value = _functools_reduce_impl(module, func, seq, result); exit: @@ -159,4 +188,4 @@ _functools__lru_cache_wrapper_cache_clear(PyObject *self, PyObject *Py_UNUSED(ig return return_value; } -/*[clinic end generated code: output=0c3df7e5131200b7 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=e6edcc01f0720daf input=a9049054013a1b77]*/ From 8ff7efb46d34ee454239bd86ff5136f386b9749b Mon Sep 17 00:00:00 2001 From: "RUANG (James Roy)" Date: Tue, 12 Nov 2024 21:18:06 +0800 Subject: [PATCH 150/219] gh-126061: Add PyLong_IsPositive/Zero/Negative() functions (#126065) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sergey B Kirpichev Co-authored-by: Peter Bierma Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Doc/c-api/long.rst | 33 ++++++++++++++ Doc/whatsnew/3.14.rst | 5 +++ Include/cpython/longobject.h | 18 ++++++++ Lib/test/test_capi/test_long.py | 45 +++++++++++++++++++ ...-10-28-15-56-03.gh-issue-126061.Py51_1.rst | 3 ++ Modules/_testcapi/long.c | 27 +++++++++++ Objects/longobject.c | 33 ++++++++++++++ 7 files changed, 164 insertions(+) create mode 100644 Misc/NEWS.d/next/C_API/2024-10-28-15-56-03.gh-issue-126061.Py51_1.rst diff --git a/Doc/c-api/long.rst b/Doc/c-api/long.rst index 9ff3e5265004a1..32bb451b08d413 100644 --- a/Doc/c-api/long.rst +++ b/Doc/c-api/long.rst @@ -582,6 +582,39 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate. .. versionadded:: 3.14 +.. c:function:: int PyLong_IsPositive(PyObject *obj) + + Check if the integer object *obj* is positive (``obj > 0``). + + If *obj* is an instance of :c:type:`PyLongObject` or its subtype, + return ``1`` when it's positive and ``0`` otherwise. Else set an + exception and return ``-1``. + + .. versionadded:: next + + +.. c:function:: int PyLong_IsNegative(PyObject *obj) + + Check if the integer object *obj* is negative (``obj < 0``). + + If *obj* is an instance of :c:type:`PyLongObject` or its subtype, + return ``1`` when it's negative and ``0`` otherwise. Else set an + exception and return ``-1``. + + .. versionadded:: next + + +.. c:function:: int PyLong_IsZero(PyObject *obj) + + Check if the integer object *obj* is zero. + + If *obj* is an instance of :c:type:`PyLongObject` or its subtype, + return ``1`` when it's zero and ``0`` otherwise. Else set an + exception and return ``-1``. + + .. versionadded:: next + + .. c:function:: PyObject* PyLong_GetInfo(void) On success, return a read only :term:`named tuple`, that holds diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 66f8c432aead61..20bd3aa5221454 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -807,6 +807,11 @@ New features an interned string and deallocate it during module shutdown. (Contributed by Eddie Elizondo in :gh:`113601`.) +* Add :c:func:`PyLong_IsPositive`, :c:func:`PyLong_IsNegative` + and :c:func:`PyLong_IsZero` for checking if :c:type:`PyLongObject` + is positive, negative, or zero, respectively. + (Contribued by James Roy and Sergey B Kirpichev in :gh:`126061`.) + * Add new functions to convert C ```` numbers from/to Python :class:`int`: diff --git a/Include/cpython/longobject.h b/Include/cpython/longobject.h index c1214d5e3714ea..4d6e618f831ad8 100644 --- a/Include/cpython/longobject.h +++ b/Include/cpython/longobject.h @@ -61,6 +61,24 @@ PyAPI_FUNC(PyObject*) PyLong_FromUnsignedNativeBytes(const void* buffer, PyAPI_FUNC(int) PyUnstable_Long_IsCompact(const PyLongObject* op); PyAPI_FUNC(Py_ssize_t) PyUnstable_Long_CompactValue(const PyLongObject* op); +/* PyLong_IsPositive. Check if the integer object is positive. + + - On success, return 1 if *obj is positive, and 0 otherwise. + - On failure, set an exception, and return -1. */ +PyAPI_FUNC(int) PyLong_IsPositive(PyObject *obj); + +/* PyLong_IsNegative. Check if the integer object is negative. + + - On success, return 1 if *obj is negative, and 0 otherwise. + - On failure, set an exception, and return -1. */ +PyAPI_FUNC(int) PyLong_IsNegative(PyObject *obj); + +/* PyLong_IsZero. Check if the integer object is zero. + + - On success, return 1 if *obj is zero, and 0 if it is non-zero. + - On failure, set an exception, and return -1. */ +PyAPI_FUNC(int) PyLong_IsZero(PyObject *obj); + /* PyLong_GetSign. Get the sign of an integer object: 0, -1 or +1 for zero, negative or positive integer, respectively. diff --git a/Lib/test/test_capi/test_long.py b/Lib/test/test_capi/test_long.py index 925fccd660bde3..a77094588a0edf 100644 --- a/Lib/test/test_capi/test_long.py +++ b/Lib/test/test_capi/test_long.py @@ -643,6 +643,51 @@ def test_long_getsign(self): # CRASHES getsign(NULL) + def test_long_ispositive(self): + # Test PyLong_IsPositive() + ispositive = _testcapi.pylong_ispositive + self.assertEqual(ispositive(1), 1) + self.assertEqual(ispositive(123), 1) + self.assertEqual(ispositive(-1), 0) + self.assertEqual(ispositive(0), 0) + self.assertEqual(ispositive(True), 1) + self.assertEqual(ispositive(False), 0) + self.assertEqual(ispositive(IntSubclass(-1)), 0) + self.assertRaises(TypeError, ispositive, 1.0) + self.assertRaises(TypeError, ispositive, Index(123)) + + # CRASHES ispositive(NULL) + + def test_long_isnegative(self): + # Test PyLong_IsNegative() + isnegative = _testcapi.pylong_isnegative + self.assertEqual(isnegative(1), 0) + self.assertEqual(isnegative(123), 0) + self.assertEqual(isnegative(-1), 1) + self.assertEqual(isnegative(0), 0) + self.assertEqual(isnegative(True), 0) + self.assertEqual(isnegative(False), 0) + self.assertEqual(isnegative(IntSubclass(-1)), 1) + self.assertRaises(TypeError, isnegative, 1.0) + self.assertRaises(TypeError, isnegative, Index(123)) + + # CRASHES isnegative(NULL) + + def test_long_iszero(self): + # Test PyLong_IsZero() + iszero = _testcapi.pylong_iszero + self.assertEqual(iszero(1), 0) + self.assertEqual(iszero(-1), 0) + self.assertEqual(iszero(0), 1) + self.assertEqual(iszero(True), 0) + self.assertEqual(iszero(False), 1) + self.assertEqual(iszero(IntSubclass(-1)), 0) + self.assertEqual(iszero(IntSubclass(0)), 1) + self.assertRaises(TypeError, iszero, 1.0) + self.assertRaises(TypeError, iszero, Index(123)) + + # CRASHES iszero(NULL) + def test_long_asint32(self): # Test PyLong_AsInt32() and PyLong_FromInt32() to_int32 = _testlimitedcapi.pylong_asint32 diff --git a/Misc/NEWS.d/next/C_API/2024-10-28-15-56-03.gh-issue-126061.Py51_1.rst b/Misc/NEWS.d/next/C_API/2024-10-28-15-56-03.gh-issue-126061.Py51_1.rst new file mode 100644 index 00000000000000..0a4ad4ea2874cf --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2024-10-28-15-56-03.gh-issue-126061.Py51_1.rst @@ -0,0 +1,3 @@ +Add :c:func:`PyLong_IsPositive`, :c:func:`PyLong_IsNegative` +and :c:func:`PyLong_IsZero` for checking if a :c:type:`PyLongObject` +is positive, negative, or zero, respectively. diff --git a/Modules/_testcapi/long.c b/Modules/_testcapi/long.c index 2b5e85d5707522..ebea09080ef11c 100644 --- a/Modules/_testcapi/long.c +++ b/Modules/_testcapi/long.c @@ -105,6 +105,30 @@ pylong_getsign(PyObject *module, PyObject *arg) } +static PyObject * +pylong_ispositive(PyObject *module, PyObject *arg) +{ + NULLABLE(arg); + RETURN_INT(PyLong_IsPositive(arg)); +} + + +static PyObject * +pylong_isnegative(PyObject *module, PyObject *arg) +{ + NULLABLE(arg); + RETURN_INT(PyLong_IsNegative(arg)); +} + + +static PyObject * +pylong_iszero(PyObject *module, PyObject *arg) +{ + NULLABLE(arg); + RETURN_INT(PyLong_IsZero(arg)); +} + + static PyObject * pylong_aspid(PyObject *module, PyObject *arg) { @@ -124,6 +148,9 @@ static PyMethodDef test_methods[] = { {"pylong_fromnativebytes", pylong_fromnativebytes, METH_VARARGS}, {"pylong_getsign", pylong_getsign, METH_O}, {"pylong_aspid", pylong_aspid, METH_O}, + {"pylong_ispositive", pylong_ispositive, METH_O}, + {"pylong_isnegative", pylong_isnegative, METH_O}, + {"pylong_iszero", pylong_iszero, METH_O}, {NULL}, }; diff --git a/Objects/longobject.c b/Objects/longobject.c index b4c0f63a9843ce..4aa35685b509f2 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -784,6 +784,39 @@ PyLong_AsUnsignedLongMask(PyObject *op) return val; } +int +PyLong_IsPositive(PyObject *obj) +{ + assert(obj != NULL); + if (!PyLong_Check(obj)) { + PyErr_Format(PyExc_TypeError, "expected int, got %T", obj); + return -1; + } + return _PyLong_IsPositive((PyLongObject *)obj); +} + +int +PyLong_IsNegative(PyObject *obj) +{ + assert(obj != NULL); + if (!PyLong_Check(obj)) { + PyErr_Format(PyExc_TypeError, "expected int, got %T", obj); + return -1; + } + return _PyLong_IsNegative((PyLongObject *)obj); +} + +int +PyLong_IsZero(PyObject *obj) +{ + assert(obj != NULL); + if (!PyLong_Check(obj)) { + PyErr_Format(PyExc_TypeError, "expected int, got %T", obj); + return -1; + } + return _PyLong_IsZero((PyLongObject *)obj); +} + int _PyLong_Sign(PyObject *vv) { From 91f4908798074db6c41925b4417bee1f933aca93 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Tue, 12 Nov 2024 15:59:19 +0200 Subject: [PATCH 151/219] gh-126133: Only use start year in PSF copyright, remove end years (#126236) --- Doc/conf.py | 5 +---- Doc/copyright.rst | 2 +- Doc/license.rst | 2 +- LICENSE | 2 +- Lib/email/__init__.py | 2 +- Lib/email/_parseaddr.py | 2 +- Lib/email/base64mime.py | 2 +- Lib/email/charset.py | 2 +- Lib/email/encoders.py | 2 +- Lib/email/errors.py | 2 +- Lib/email/feedparser.py | 2 +- Lib/email/generator.py | 2 +- Lib/email/header.py | 2 +- Lib/email/iterators.py | 2 +- Lib/email/message.py | 2 +- Lib/email/mime/application.py | 2 +- Lib/email/mime/audio.py | 2 +- Lib/email/mime/base.py | 2 +- Lib/email/mime/image.py | 2 +- Lib/email/mime/message.py | 2 +- Lib/email/mime/multipart.py | 2 +- Lib/email/mime/nonmultipart.py | 2 +- Lib/email/mime/text.py | 2 +- Lib/email/parser.py | 2 +- Lib/email/quoprimime.py | 2 +- Lib/email/utils.py | 2 +- Lib/functools.py | 2 +- Lib/optparse.py | 2 +- Lib/test/test_csv.py | 2 +- Lib/test/test_email/test_asian_codecs.py | 2 +- Lib/test/test_email/test_email.py | 2 +- Lib/test/test_email/torture_test.py | 2 +- Lib/test/test_plistlib.py | 2 +- Lib/textwrap.py | 2 +- Lib/unittest/__init__.py | 2 +- Lib/wsgiref/headers.py | 2 +- Mac/BuildScript/resources/License.rtf | 2 +- Mac/PythonLauncher/Info.plist.in | 4 ++-- Modules/_decimal/docstrings.h | 2 +- Modules/_decimal/tests/bench.py | 2 +- Modules/_functoolsmodule.c | 2 +- PC/python_ver_rc.h | 2 +- PC/store_info.txt | 2 +- Python/getcopyright.c | 2 +- README.rst | 4 ++-- 45 files changed, 47 insertions(+), 50 deletions(-) diff --git a/Doc/conf.py b/Doc/conf.py index 73d7d5db26ff7b..738c9901eef06f 100644 --- a/Doc/conf.py +++ b/Doc/conf.py @@ -67,10 +67,7 @@ # General substitutions. project = 'Python' -if sphinx.version_info[:2] >= (8, 1): - copyright = "2001-%Y, Python Software Foundation" -else: - copyright = f"2001-{time.strftime('%Y')}, Python Software Foundation" +copyright = "2001 Python Software Foundation" # We look for the Include/patchlevel.h file in the current Python source tree # and replace the values accordingly. diff --git a/Doc/copyright.rst b/Doc/copyright.rst index 8629ed1fc38009..9210d5f50ed841 100644 --- a/Doc/copyright.rst +++ b/Doc/copyright.rst @@ -4,7 +4,7 @@ Copyright Python and this documentation is: -Copyright © 2001-2024 Python Software Foundation. All rights reserved. +Copyright © 2001 Python Software Foundation. All rights reserved. Copyright © 2000 BeOpen.com. All rights reserved. diff --git a/Doc/license.rst b/Doc/license.rst index 674ac5f56e6f97..428dc22b817ebe 100644 --- a/Doc/license.rst +++ b/Doc/license.rst @@ -100,7 +100,7 @@ PSF LICENSE AGREEMENT FOR PYTHON |release| analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python |release| alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of - copyright, i.e., "Copyright © 2001-2024 Python Software Foundation; All Rights + copyright, i.e., "Copyright © 2001 Python Software Foundation; All Rights Reserved" are retained in Python |release| alone or in any derivative version prepared by Licensee. diff --git a/LICENSE b/LICENSE index 14603b95c2e23b..20cf39097c68ba 100644 --- a/LICENSE +++ b/LICENSE @@ -83,7 +83,7 @@ grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, -i.e., "Copyright (c) 2001-2024 Python Software Foundation; All Rights Reserved" +i.e., "Copyright (c) 2001 Python Software Foundation; All Rights Reserved" are retained in Python alone or in any derivative version prepared by Licensee. 3. In the event Licensee prepares a derivative work that is based on diff --git a/Lib/email/__init__.py b/Lib/email/__init__.py index 9fa47783004185..6d597006e5eefe 100644 --- a/Lib/email/__init__.py +++ b/Lib/email/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2001-2007 Python Software Foundation +# Copyright (C) 2001 Python Software Foundation # Author: Barry Warsaw # Contact: email-sig@python.org diff --git a/Lib/email/_parseaddr.py b/Lib/email/_parseaddr.py index 36625e35ffb6a7..84917038874ba1 100644 --- a/Lib/email/_parseaddr.py +++ b/Lib/email/_parseaddr.py @@ -1,4 +1,4 @@ -# Copyright (C) 2002-2007 Python Software Foundation +# Copyright (C) 2002 Python Software Foundation # Contact: email-sig@python.org """Email address parsing code. diff --git a/Lib/email/base64mime.py b/Lib/email/base64mime.py index d440de95255bf1..a5a3f737a97b51 100644 --- a/Lib/email/base64mime.py +++ b/Lib/email/base64mime.py @@ -1,4 +1,4 @@ -# Copyright (C) 2002-2007 Python Software Foundation +# Copyright (C) 2002 Python Software Foundation # Author: Ben Gertzfield # Contact: email-sig@python.org diff --git a/Lib/email/charset.py b/Lib/email/charset.py index cfd5a0c456e497..5036c3f58a5633 100644 --- a/Lib/email/charset.py +++ b/Lib/email/charset.py @@ -1,4 +1,4 @@ -# Copyright (C) 2001-2007 Python Software Foundation +# Copyright (C) 2001 Python Software Foundation # Author: Ben Gertzfield, Barry Warsaw # Contact: email-sig@python.org diff --git a/Lib/email/encoders.py b/Lib/email/encoders.py index 17bd1ab7b19f32..55741a22a07b20 100644 --- a/Lib/email/encoders.py +++ b/Lib/email/encoders.py @@ -1,4 +1,4 @@ -# Copyright (C) 2001-2006 Python Software Foundation +# Copyright (C) 2001 Python Software Foundation # Author: Barry Warsaw # Contact: email-sig@python.org diff --git a/Lib/email/errors.py b/Lib/email/errors.py index 02aa5eced6ae46..6bc744bd59c5bb 100644 --- a/Lib/email/errors.py +++ b/Lib/email/errors.py @@ -1,4 +1,4 @@ -# Copyright (C) 2001-2006 Python Software Foundation +# Copyright (C) 2001 Python Software Foundation # Author: Barry Warsaw # Contact: email-sig@python.org diff --git a/Lib/email/feedparser.py b/Lib/email/feedparser.py index 06d6b4a3afcd07..b2bc4afc1cc26f 100644 --- a/Lib/email/feedparser.py +++ b/Lib/email/feedparser.py @@ -1,4 +1,4 @@ -# Copyright (C) 2004-2006 Python Software Foundation +# Copyright (C) 2004 Python Software Foundation # Authors: Baxter, Wouters and Warsaw # Contact: email-sig@python.org diff --git a/Lib/email/generator.py b/Lib/email/generator.py index 205caf0fe9e81d..ab5bd0653e440c 100644 --- a/Lib/email/generator.py +++ b/Lib/email/generator.py @@ -1,4 +1,4 @@ -# Copyright (C) 2001-2010 Python Software Foundation +# Copyright (C) 2001 Python Software Foundation # Author: Barry Warsaw # Contact: email-sig@python.org diff --git a/Lib/email/header.py b/Lib/email/header.py index 66a1d46db50c45..113a81f41314ec 100644 --- a/Lib/email/header.py +++ b/Lib/email/header.py @@ -1,4 +1,4 @@ -# Copyright (C) 2002-2007 Python Software Foundation +# Copyright (C) 2002 Python Software Foundation # Author: Ben Gertzfield, Barry Warsaw # Contact: email-sig@python.org diff --git a/Lib/email/iterators.py b/Lib/email/iterators.py index 2f436aefc2300b..08ede3ec679613 100644 --- a/Lib/email/iterators.py +++ b/Lib/email/iterators.py @@ -1,4 +1,4 @@ -# Copyright (C) 2001-2006 Python Software Foundation +# Copyright (C) 2001 Python Software Foundation # Author: Barry Warsaw # Contact: email-sig@python.org diff --git a/Lib/email/message.py b/Lib/email/message.py index 08192c50a8ff5c..a58afc5fe5f68e 100644 --- a/Lib/email/message.py +++ b/Lib/email/message.py @@ -1,4 +1,4 @@ -# Copyright (C) 2001-2007 Python Software Foundation +# Copyright (C) 2001 Python Software Foundation # Author: Barry Warsaw # Contact: email-sig@python.org diff --git a/Lib/email/mime/application.py b/Lib/email/mime/application.py index f67cbad3f03407..9a9d213d2a940d 100644 --- a/Lib/email/mime/application.py +++ b/Lib/email/mime/application.py @@ -1,4 +1,4 @@ -# Copyright (C) 2001-2006 Python Software Foundation +# Copyright (C) 2001 Python Software Foundation # Author: Keith Dart # Contact: email-sig@python.org diff --git a/Lib/email/mime/audio.py b/Lib/email/mime/audio.py index aa0c4905cbb2b4..85f4a955238c52 100644 --- a/Lib/email/mime/audio.py +++ b/Lib/email/mime/audio.py @@ -1,4 +1,4 @@ -# Copyright (C) 2001-2007 Python Software Foundation +# Copyright (C) 2001 Python Software Foundation # Author: Anthony Baxter # Contact: email-sig@python.org diff --git a/Lib/email/mime/base.py b/Lib/email/mime/base.py index f601f621cec393..da4c6e591a5cb8 100644 --- a/Lib/email/mime/base.py +++ b/Lib/email/mime/base.py @@ -1,4 +1,4 @@ -# Copyright (C) 2001-2006 Python Software Foundation +# Copyright (C) 2001 Python Software Foundation # Author: Barry Warsaw # Contact: email-sig@python.org diff --git a/Lib/email/mime/image.py b/Lib/email/mime/image.py index 4b7f2f9cbad425..dab9685848172b 100644 --- a/Lib/email/mime/image.py +++ b/Lib/email/mime/image.py @@ -1,4 +1,4 @@ -# Copyright (C) 2001-2006 Python Software Foundation +# Copyright (C) 2001 Python Software Foundation # Author: Barry Warsaw # Contact: email-sig@python.org diff --git a/Lib/email/mime/message.py b/Lib/email/mime/message.py index 61836b5a7861fc..13d9ff599f86db 100644 --- a/Lib/email/mime/message.py +++ b/Lib/email/mime/message.py @@ -1,4 +1,4 @@ -# Copyright (C) 2001-2006 Python Software Foundation +# Copyright (C) 2001 Python Software Foundation # Author: Barry Warsaw # Contact: email-sig@python.org diff --git a/Lib/email/mime/multipart.py b/Lib/email/mime/multipart.py index 47fc218e1ae032..1abb84d5fed0bb 100644 --- a/Lib/email/mime/multipart.py +++ b/Lib/email/mime/multipart.py @@ -1,4 +1,4 @@ -# Copyright (C) 2002-2006 Python Software Foundation +# Copyright (C) 2002 Python Software Foundation # Author: Barry Warsaw # Contact: email-sig@python.org diff --git a/Lib/email/mime/nonmultipart.py b/Lib/email/mime/nonmultipart.py index a41386eb148c0c..5beab3a441e2bc 100644 --- a/Lib/email/mime/nonmultipart.py +++ b/Lib/email/mime/nonmultipart.py @@ -1,4 +1,4 @@ -# Copyright (C) 2002-2006 Python Software Foundation +# Copyright (C) 2002 Python Software Foundation # Author: Barry Warsaw # Contact: email-sig@python.org diff --git a/Lib/email/mime/text.py b/Lib/email/mime/text.py index 7672b789138600..aa4da7f8217e43 100644 --- a/Lib/email/mime/text.py +++ b/Lib/email/mime/text.py @@ -1,4 +1,4 @@ -# Copyright (C) 2001-2006 Python Software Foundation +# Copyright (C) 2001 Python Software Foundation # Author: Barry Warsaw # Contact: email-sig@python.org diff --git a/Lib/email/parser.py b/Lib/email/parser.py index 475aa2b1a66680..039f03cba74fa0 100644 --- a/Lib/email/parser.py +++ b/Lib/email/parser.py @@ -1,4 +1,4 @@ -# Copyright (C) 2001-2007 Python Software Foundation +# Copyright (C) 2001 Python Software Foundation # Author: Barry Warsaw, Thomas Wouters, Anthony Baxter # Contact: email-sig@python.org diff --git a/Lib/email/quoprimime.py b/Lib/email/quoprimime.py index 500bbc5151769d..27c7ea55c7871f 100644 --- a/Lib/email/quoprimime.py +++ b/Lib/email/quoprimime.py @@ -1,4 +1,4 @@ -# Copyright (C) 2001-2006 Python Software Foundation +# Copyright (C) 2001 Python Software Foundation # Author: Ben Gertzfield # Contact: email-sig@python.org diff --git a/Lib/email/utils.py b/Lib/email/utils.py index f276303197396b..7eab74dc0db9df 100644 --- a/Lib/email/utils.py +++ b/Lib/email/utils.py @@ -1,4 +1,4 @@ -# Copyright (C) 2001-2010 Python Software Foundation +# Copyright (C) 2001 Python Software Foundation # Author: Barry Warsaw # Contact: email-sig@python.org diff --git a/Lib/functools.py b/Lib/functools.py index bde0a3e95b8275..eff6540c7f606e 100644 --- a/Lib/functools.py +++ b/Lib/functools.py @@ -6,7 +6,7 @@ # Written by Nick Coghlan , # Raymond Hettinger , # and Łukasz Langa . -# Copyright (C) 2006-2024 Python Software Foundation. +# Copyright (C) 2006 Python Software Foundation. # See C source code for _functools credits/copyright __all__ = ['update_wrapper', 'wraps', 'WRAPPER_ASSIGNMENTS', 'WRAPPER_UPDATES', diff --git a/Lib/optparse.py b/Lib/optparse.py index 04112eca37c801..cbe3451ced8bc3 100644 --- a/Lib/optparse.py +++ b/Lib/optparse.py @@ -43,7 +43,7 @@ __copyright__ = """ Copyright (c) 2001-2006 Gregory P. Ward. All rights reserved. -Copyright (c) 2002-2006 Python Software Foundation. All rights reserved. +Copyright (c) 2002 Python Software Foundation. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are diff --git a/Lib/test/test_csv.py b/Lib/test/test_csv.py index ce5c03659f1979..4af8f7f480e759 100644 --- a/Lib/test/test_csv.py +++ b/Lib/test/test_csv.py @@ -1,4 +1,4 @@ -# Copyright (C) 2001,2002 Python Software Foundation +# Copyright (C) 2001 Python Software Foundation # csv package unit tests import copy diff --git a/Lib/test/test_email/test_asian_codecs.py b/Lib/test/test_email/test_asian_codecs.py index 1e0caeeaed0810..ca44f54c69b39b 100644 --- a/Lib/test/test_email/test_asian_codecs.py +++ b/Lib/test/test_email/test_asian_codecs.py @@ -1,4 +1,4 @@ -# Copyright (C) 2002-2006 Python Software Foundation +# Copyright (C) 2002 Python Software Foundation # Contact: email-sig@python.org # email package unit tests for (optional) Asian codecs diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py index 65ddbabcaa1997..abe9ef2e94409f 100644 --- a/Lib/test/test_email/test_email.py +++ b/Lib/test/test_email/test_email.py @@ -1,4 +1,4 @@ -# Copyright (C) 2001-2010 Python Software Foundation +# Copyright (C) 2001 Python Software Foundation # Contact: email-sig@python.org # email package unit tests diff --git a/Lib/test/test_email/torture_test.py b/Lib/test/test_email/torture_test.py index 9cf9362c9b77e0..d15948a38b25dd 100644 --- a/Lib/test/test_email/torture_test.py +++ b/Lib/test/test_email/torture_test.py @@ -1,4 +1,4 @@ -# Copyright (C) 2002-2004 Python Software Foundation +# Copyright (C) 2002 Python Software Foundation # # A torture test of the email package. This should not be run as part of the # standard Python test suite since it requires several meg of email messages diff --git a/Lib/test/test_plistlib.py b/Lib/test/test_plistlib.py index b231b05f864ab9..a0c76e5dec5ebe 100644 --- a/Lib/test/test_plistlib.py +++ b/Lib/test/test_plistlib.py @@ -1,4 +1,4 @@ -# Copyright (C) 2003-2013 Python Software Foundation +# Copyright (C) 2003 Python Software Foundation import copy import operator import pickle diff --git a/Lib/textwrap.py b/Lib/textwrap.py index 7ca393d1c371aa..1bf07aa46cad99 100644 --- a/Lib/textwrap.py +++ b/Lib/textwrap.py @@ -2,7 +2,7 @@ """ # Copyright (C) 1999-2001 Gregory P. Ward. -# Copyright (C) 2002, 2003 Python Software Foundation. +# Copyright (C) 2002 Python Software Foundation. # Written by Greg Ward import re diff --git a/Lib/unittest/__init__.py b/Lib/unittest/__init__.py index 324e5d038aef03..78ff6bb4fdcce5 100644 --- a/Lib/unittest/__init__.py +++ b/Lib/unittest/__init__.py @@ -27,7 +27,7 @@ def testMultiply(self): http://docs.python.org/library/unittest.html Copyright (c) 1999-2003 Steve Purcell -Copyright (c) 2003-2010 Python Software Foundation +Copyright (c) 2003 Python Software Foundation This module is free software, and you may redistribute it and/or modify it under the same terms as Python itself, so long as this copyright message and disclaimer are retained in their original form. diff --git a/Lib/wsgiref/headers.py b/Lib/wsgiref/headers.py index 05d2ba4c664e5e..c78879f80c7df2 100644 --- a/Lib/wsgiref/headers.py +++ b/Lib/wsgiref/headers.py @@ -1,7 +1,7 @@ """Manage HTTP Response Headers Much of this module is red-handedly pilfered from email.message in the stdlib, -so portions are Copyright (C) 2001,2002 Python Software Foundation, and were +so portions are Copyright (C) 2001 Python Software Foundation, and were written by Barry Warsaw. """ diff --git a/Mac/BuildScript/resources/License.rtf b/Mac/BuildScript/resources/License.rtf index 1255d1ce48ed6c..b5cb8ec41c86e2 100644 --- a/Mac/BuildScript/resources/License.rtf +++ b/Mac/BuildScript/resources/License.rtf @@ -64,7 +64,7 @@ Some software incorporated into Python is under different licenses. The licenses \f1\b0 \ 1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and the Individual or Organization ("Licensee") accessing and otherwise using this software ("Python") in source or binary form and its associated documentation.\ \ -2. Subject to the terms and conditions of this License Agreement, PSF hereby grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., "Copyright \'a9 2001-2020 Python Software Foundation; All Rights Reserved" are retained in Python alone or in any derivative version prepared by Licensee.\ +2. Subject to the terms and conditions of this License Agreement, PSF hereby grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., "Copyright \'a9 2001 Python Software Foundation; All Rights Reserved" are retained in Python alone or in any derivative version prepared by Licensee.\ \ 3. In the event Licensee prepares a derivative work that is based on or incorporates Python or any part thereof, and wants to make the derivative work available to others as provided herein, then Licensee hereby agrees to include in any such work a brief summary of the changes made to Python.\ \ diff --git a/Mac/PythonLauncher/Info.plist.in b/Mac/PythonLauncher/Info.plist.in index 233694788ac2b7..ce8f27cd7d4de7 100644 --- a/Mac/PythonLauncher/Info.plist.in +++ b/Mac/PythonLauncher/Info.plist.in @@ -40,9 +40,9 @@ CFBundleExecutable Python Launcher NSHumanReadableCopyright - Copyright © 2001-2024 Python Software Foundation + Copyright © 2001 Python Software Foundation CFBundleGetInfoString - %VERSION%, © 2001-2024 Python Software Foundation + %VERSION%, © 2001 Python Software Foundation CFBundleIconFile PythonLauncher.icns CFBundleIdentifier diff --git a/Modules/_decimal/docstrings.h b/Modules/_decimal/docstrings.h index b34bff83d3f4e9..5abd7b9d807e19 100644 --- a/Modules/_decimal/docstrings.h +++ b/Modules/_decimal/docstrings.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2012 Python Software Foundation. All Rights Reserved. + * Copyright (c) 2001 Python Software Foundation. All Rights Reserved. * Modified and extended by Stefan Krah. */ diff --git a/Modules/_decimal/tests/bench.py b/Modules/_decimal/tests/bench.py index 640290f2ec7962..6605e9a92e2dde 100644 --- a/Modules/_decimal/tests/bench.py +++ b/Modules/_decimal/tests/bench.py @@ -1,5 +1,5 @@ # -# Copyright (C) 2001-2012 Python Software Foundation. All Rights Reserved. +# Copyright (C) 2001 Python Software Foundation. All Rights Reserved. # Modified and extended by Stefan Krah. # diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 5e0cf057dcd1a2..24b38063dde9e5 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -18,7 +18,7 @@ class _functools._lru_cache_wrapper "PyObject *" "&lru_cache_type_spec" /* _functools module written and maintained by Hye-Shik Chang with adaptations by Raymond Hettinger - Copyright (c) 2004, 2005, 2006 Python Software Foundation. + Copyright (c) 2004 Python Software Foundation. All rights reserved. */ diff --git a/PC/python_ver_rc.h b/PC/python_ver_rc.h index 08509f96ed1db8..ee867fe41224c3 100644 --- a/PC/python_ver_rc.h +++ b/PC/python_ver_rc.h @@ -5,7 +5,7 @@ #include "winver.h" #define PYTHON_COMPANY "Python Software Foundation" -#define PYTHON_COPYRIGHT "Copyright \xA9 2001-2024 Python Software Foundation. Copyright \xA9 2000 BeOpen.com. Copyright \xA9 1995-2001 CNRI. Copyright \xA9 1991-1995 SMC." +#define PYTHON_COPYRIGHT "Copyright \xA9 2001 Python Software Foundation. Copyright \xA9 2000 BeOpen.com. Copyright \xA9 1995-2001 CNRI. Copyright \xA9 1991-1995 SMC." #define MS_WINDOWS #include "modsupport.h" diff --git a/PC/store_info.txt b/PC/store_info.txt index f6a85cb8ebec1f..d150ba17cbe62d 100644 --- a/PC/store_info.txt +++ b/PC/store_info.txt @@ -109,7 +109,7 @@ PSF LICENSE AGREEMENT FOR PYTHON 3.9 analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python 3.9 alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of - copyright, i.e., "Copyright © 2001-2018 Python Software Foundation; All Rights + copyright, i.e., "Copyright © 2001 Python Software Foundation; All Rights Reserved" are retained in Python 3.9 alone or in any derivative version prepared by Licensee. diff --git a/Python/getcopyright.c b/Python/getcopyright.c index 066c2ed66acddf..964584ddf7998e 100644 --- a/Python/getcopyright.c +++ b/Python/getcopyright.c @@ -4,7 +4,7 @@ static const char cprt[] = "\ -Copyright (c) 2001-2024 Python Software Foundation.\n\ +Copyright (c) 2001 Python Software Foundation.\n\ All Rights Reserved.\n\ \n\ Copyright (c) 2000 BeOpen.com.\n\ diff --git a/README.rst b/README.rst index 3f694771e090cb..0134aafe2a969a 100644 --- a/README.rst +++ b/README.rst @@ -14,7 +14,7 @@ This is Python version 3.14.0 alpha 1 :target: https://discuss.python.org/ -Copyright © 2001-2024 Python Software Foundation. All rights reserved. +Copyright © 2001 Python Software Foundation. All rights reserved. See the end of this file for further copyright and license information. @@ -215,7 +215,7 @@ Copyright and License Information --------------------------------- -Copyright © 2001-2024 Python Software Foundation. All rights reserved. +Copyright © 2001 Python Software Foundation. All rights reserved. Copyright © 2000 BeOpen.com. All rights reserved. From 6b2a19681eb5c132caa73a268724c7d502794867 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 12 Nov 2024 19:19:15 +0200 Subject: [PATCH 152/219] gh-95382: Use cache for indentations in the JSON encoder (GH-118636) --- Modules/_json.c | 182 +++++++++++++++++++++++++++++++----------------- 1 file changed, 118 insertions(+), 64 deletions(-) diff --git a/Modules/_json.c b/Modules/_json.c index ce0093ab431d05..a99abbe72bf7a0 100644 --- a/Modules/_json.c +++ b/Modules/_json.c @@ -86,11 +86,11 @@ encoder_dealloc(PyObject *self); static int encoder_clear(PyEncoderObject *self); static int -encoder_listencode_list(PyEncoderObject *s, PyUnicodeWriter *writer, PyObject *seq, PyObject *newline_indent); +encoder_listencode_list(PyEncoderObject *s, PyUnicodeWriter *writer, PyObject *seq, Py_ssize_t indent_level, PyObject *indent_cache); static int -encoder_listencode_obj(PyEncoderObject *s, PyUnicodeWriter *writer, PyObject *obj, PyObject *newline_indent); +encoder_listencode_obj(PyEncoderObject *s, PyUnicodeWriter *writer, PyObject *obj, Py_ssize_t indent_level, PyObject *indent_cache); static int -encoder_listencode_dict(PyEncoderObject *s, PyUnicodeWriter *writer, PyObject *dct, PyObject *newline_indent); +encoder_listencode_dict(PyEncoderObject *s, PyUnicodeWriter *writer, PyObject *dct, Py_ssize_t indent_level, PyObject *indent_cache); static PyObject * _encoded_const(PyObject *obj); static void @@ -1252,17 +1252,92 @@ encoder_new(PyTypeObject *type, PyObject *args, PyObject *kwds) return (PyObject *)s; } + +/* indent_cache is a list that contains intermixed values at even and odd + * positions: + * + * 2*k : '\n' + indent * (k + initial_indent_level) + * strings written after opening and before closing brackets + * 2*k-1 : item_separator + '\n' + indent * (k + initial_indent_level) + * strings written between items + * + * Its size is always an odd number. + */ static PyObject * -_create_newline_indent(PyObject *indent, Py_ssize_t indent_level) +create_indent_cache(PyEncoderObject *s, Py_ssize_t indent_level) { PyObject *newline_indent = PyUnicode_FromOrdinal('\n'); if (newline_indent != NULL && indent_level) { PyUnicode_AppendAndDel(&newline_indent, - PySequence_Repeat(indent, indent_level)); + PySequence_Repeat(s->indent, indent_level)); + } + if (newline_indent == NULL) { + return NULL; + } + PyObject *indent_cache = PyList_New(1); + if (indent_cache == NULL) { + Py_DECREF(newline_indent); + return NULL; } - return newline_indent; + PyList_SET_ITEM(indent_cache, 0, newline_indent); + return indent_cache; +} + +/* Extend indent_cache by adding values for the next level. + * It should have values for the indent_level-1 level before the call. + */ +static int +update_indent_cache(PyEncoderObject *s, + Py_ssize_t indent_level, PyObject *indent_cache) +{ + assert(indent_level * 2 == PyList_GET_SIZE(indent_cache) + 1); + assert(indent_level > 0); + PyObject *newline_indent = PyList_GET_ITEM(indent_cache, (indent_level - 1)*2); + newline_indent = PyUnicode_Concat(newline_indent, s->indent); + if (newline_indent == NULL) { + return -1; + } + PyObject *separator_indent = PyUnicode_Concat(s->item_separator, newline_indent); + if (separator_indent == NULL) { + Py_DECREF(newline_indent); + return -1; + } + + if (PyList_Append(indent_cache, separator_indent) < 0 || + PyList_Append(indent_cache, newline_indent) < 0) + { + Py_DECREF(separator_indent); + Py_DECREF(newline_indent); + return -1; + } + Py_DECREF(separator_indent); + Py_DECREF(newline_indent); + return 0; } +static PyObject * +get_item_separator(PyEncoderObject *s, + Py_ssize_t indent_level, PyObject *indent_cache) +{ + assert(indent_level > 0); + if (indent_level * 2 > PyList_GET_SIZE(indent_cache)) { + if (update_indent_cache(s, indent_level, indent_cache) < 0) { + return NULL; + } + } + assert(indent_level * 2 < PyList_GET_SIZE(indent_cache)); + return PyList_GET_ITEM(indent_cache, indent_level * 2 - 1); +} + +static int +write_newline_indent(PyUnicodeWriter *writer, + Py_ssize_t indent_level, PyObject *indent_cache) +{ + PyObject *newline_indent = PyList_GET_ITEM(indent_cache, indent_level * 2); + return PyUnicodeWriter_WriteStr(writer, newline_indent); +} + + static PyObject * encoder_call(PyEncoderObject *self, PyObject *args, PyObject *kwds) { @@ -1280,20 +1355,20 @@ encoder_call(PyEncoderObject *self, PyObject *args, PyObject *kwds) return NULL; } - PyObject *newline_indent = NULL; + PyObject *indent_cache = NULL; if (self->indent != Py_None) { - newline_indent = _create_newline_indent(self->indent, indent_level); - if (newline_indent == NULL) { + indent_cache = create_indent_cache(self, indent_level); + if (indent_cache == NULL) { PyUnicodeWriter_Discard(writer); return NULL; } } - if (encoder_listencode_obj(self, writer, obj, newline_indent)) { + if (encoder_listencode_obj(self, writer, obj, indent_level, indent_cache)) { PyUnicodeWriter_Discard(writer); - Py_XDECREF(newline_indent); + Py_XDECREF(indent_cache); return NULL; } - Py_XDECREF(newline_indent); + Py_XDECREF(indent_cache); PyObject *str = PyUnicodeWriter_Finish(writer); if (str == NULL) { @@ -1381,7 +1456,8 @@ _steal_accumulate(PyUnicodeWriter *writer, PyObject *stolen) static int encoder_listencode_obj(PyEncoderObject *s, PyUnicodeWriter *writer, - PyObject *obj, PyObject *newline_indent) + PyObject *obj, + Py_ssize_t indent_level, PyObject *indent_cache) { /* Encode Python object obj to a JSON term */ PyObject *newobj; @@ -1421,14 +1497,14 @@ encoder_listencode_obj(PyEncoderObject *s, PyUnicodeWriter *writer, else if (PyList_Check(obj) || PyTuple_Check(obj)) { if (_Py_EnterRecursiveCall(" while encoding a JSON object")) return -1; - rv = encoder_listencode_list(s, writer, obj, newline_indent); + rv = encoder_listencode_list(s, writer, obj, indent_level, indent_cache); _Py_LeaveRecursiveCall(); return rv; } else if (PyDict_Check(obj)) { if (_Py_EnterRecursiveCall(" while encoding a JSON object")) return -1; - rv = encoder_listencode_dict(s, writer, obj, newline_indent); + rv = encoder_listencode_dict(s, writer, obj, indent_level, indent_cache); _Py_LeaveRecursiveCall(); return rv; } @@ -1462,7 +1538,7 @@ encoder_listencode_obj(PyEncoderObject *s, PyUnicodeWriter *writer, Py_XDECREF(ident); return -1; } - rv = encoder_listencode_obj(s, writer, newobj, newline_indent); + rv = encoder_listencode_obj(s, writer, newobj, indent_level, indent_cache); _Py_LeaveRecursiveCall(); Py_DECREF(newobj); @@ -1485,7 +1561,7 @@ encoder_listencode_obj(PyEncoderObject *s, PyUnicodeWriter *writer, static int encoder_encode_key_value(PyEncoderObject *s, PyUnicodeWriter *writer, bool *first, PyObject *dct, PyObject *key, PyObject *value, - PyObject *newline_indent, + Py_ssize_t indent_level, PyObject *indent_cache, PyObject *item_separator) { PyObject *keystr = NULL; @@ -1541,7 +1617,7 @@ encoder_encode_key_value(PyEncoderObject *s, PyUnicodeWriter *writer, bool *firs if (PyUnicodeWriter_WriteStr(writer, s->key_separator) < 0) { return -1; } - if (encoder_listencode_obj(s, writer, value, newline_indent) < 0) { + if (encoder_listencode_obj(s, writer, value, indent_level, indent_cache) < 0) { _PyErr_FormatNote("when serializing %T item %R", dct, key); return -1; } @@ -1550,15 +1626,14 @@ encoder_encode_key_value(PyEncoderObject *s, PyUnicodeWriter *writer, bool *firs static int encoder_listencode_dict(PyEncoderObject *s, PyUnicodeWriter *writer, - PyObject *dct, PyObject *newline_indent) + PyObject *dct, + Py_ssize_t indent_level, PyObject *indent_cache) { /* Encode Python dict dct a JSON term */ PyObject *ident = NULL; PyObject *items = NULL; PyObject *key, *value; bool first = true; - PyObject *new_newline_indent = NULL; - PyObject *separator_indent = NULL; if (PyDict_GET_SIZE(dct) == 0) { /* Fast path */ @@ -1585,19 +1660,13 @@ encoder_listencode_dict(PyEncoderObject *s, PyUnicodeWriter *writer, goto bail; } - PyObject *current_item_separator = s->item_separator; // borrowed reference + PyObject *separator = s->item_separator; // borrowed reference if (s->indent != Py_None) { - new_newline_indent = PyUnicode_Concat(newline_indent, s->indent); - if (new_newline_indent == NULL) { - goto bail; - } - separator_indent = PyUnicode_Concat(current_item_separator, new_newline_indent); - if (separator_indent == NULL) { - goto bail; - } - // update item separator with a borrowed reference - current_item_separator = separator_indent; - if (PyUnicodeWriter_WriteStr(writer, new_newline_indent) < 0) { + indent_level++; + separator = get_item_separator(s, indent_level, indent_cache); + if (separator == NULL || + write_newline_indent(writer, indent_level, indent_cache) < 0) + { goto bail; } } @@ -1618,8 +1687,8 @@ encoder_listencode_dict(PyEncoderObject *s, PyUnicodeWriter *writer, key = PyTuple_GET_ITEM(item, 0); value = PyTuple_GET_ITEM(item, 1); if (encoder_encode_key_value(s, writer, &first, dct, key, value, - new_newline_indent, - current_item_separator) < 0) + indent_level, indent_cache, + separator) < 0) goto bail; } Py_CLEAR(items); @@ -1628,8 +1697,8 @@ encoder_listencode_dict(PyEncoderObject *s, PyUnicodeWriter *writer, Py_ssize_t pos = 0; while (PyDict_Next(dct, &pos, &key, &value)) { if (encoder_encode_key_value(s, writer, &first, dct, key, value, - new_newline_indent, - current_item_separator) < 0) + indent_level, indent_cache, + separator) < 0) goto bail; } } @@ -1640,10 +1709,8 @@ encoder_listencode_dict(PyEncoderObject *s, PyUnicodeWriter *writer, Py_CLEAR(ident); } if (s->indent != Py_None) { - Py_CLEAR(new_newline_indent); - Py_CLEAR(separator_indent); - - if (PyUnicodeWriter_WriteStr(writer, newline_indent) < 0) { + indent_level--; + if (write_newline_indent(writer, indent_level, indent_cache) < 0) { goto bail; } } @@ -1656,20 +1723,17 @@ encoder_listencode_dict(PyEncoderObject *s, PyUnicodeWriter *writer, bail: Py_XDECREF(items); Py_XDECREF(ident); - Py_XDECREF(separator_indent); - Py_XDECREF(new_newline_indent); return -1; } static int encoder_listencode_list(PyEncoderObject *s, PyUnicodeWriter *writer, - PyObject *seq, PyObject *newline_indent) + PyObject *seq, + Py_ssize_t indent_level, PyObject *indent_cache) { PyObject *ident = NULL; PyObject *s_fast = NULL; Py_ssize_t i; - PyObject *new_newline_indent = NULL; - PyObject *separator_indent = NULL; ident = NULL; s_fast = PySequence_Fast(seq, "_iterencode_list needs a sequence"); @@ -1702,20 +1766,13 @@ encoder_listencode_list(PyEncoderObject *s, PyUnicodeWriter *writer, PyObject *separator = s->item_separator; // borrowed reference if (s->indent != Py_None) { - new_newline_indent = PyUnicode_Concat(newline_indent, s->indent); - if (new_newline_indent == NULL) { - goto bail; - } - - if (PyUnicodeWriter_WriteStr(writer, new_newline_indent) < 0) { - goto bail; - } - - separator_indent = PyUnicode_Concat(separator, new_newline_indent); - if (separator_indent == NULL) { + indent_level++; + separator = get_item_separator(s, indent_level, indent_cache); + if (separator == NULL || + write_newline_indent(writer, indent_level, indent_cache) < 0) + { goto bail; } - separator = separator_indent; // assign separator with borrowed reference } for (i = 0; i < PySequence_Fast_GET_SIZE(s_fast); i++) { PyObject *obj = PySequence_Fast_GET_ITEM(s_fast, i); @@ -1723,7 +1780,7 @@ encoder_listencode_list(PyEncoderObject *s, PyUnicodeWriter *writer, if (PyUnicodeWriter_WriteStr(writer, separator) < 0) goto bail; } - if (encoder_listencode_obj(s, writer, obj, new_newline_indent)) { + if (encoder_listencode_obj(s, writer, obj, indent_level, indent_cache)) { _PyErr_FormatNote("when serializing %T item %zd", seq, i); goto bail; } @@ -1735,9 +1792,8 @@ encoder_listencode_list(PyEncoderObject *s, PyUnicodeWriter *writer, } if (s->indent != Py_None) { - Py_CLEAR(new_newline_indent); - Py_CLEAR(separator_indent); - if (PyUnicodeWriter_WriteStr(writer, newline_indent) < 0) { + indent_level--; + if (write_newline_indent(writer, indent_level, indent_cache) < 0) { goto bail; } } @@ -1751,8 +1807,6 @@ encoder_listencode_list(PyEncoderObject *s, PyUnicodeWriter *writer, bail: Py_XDECREF(ident); Py_DECREF(s_fast); - Py_XDECREF(separator_indent); - Py_XDECREF(new_newline_indent); return -1; } From 73cf0690997647c9801d9ebba43305a868d3b776 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 12 Nov 2024 10:41:51 -0700 Subject: [PATCH 153/219] gh-76785: Improved Subinterpreters Compatibility with 3.12 (2/2) (gh-126707) These changes makes it easier to backport the _interpreters, _interpqueues, and _interpchannels modules to Python 3.12. This involves the following: * add the _PyXI_GET_STATE() and _PyXI_GET_GLOBAL_STATE() macros * add _PyXIData_lookup_context_t and _PyXIData_GetLookupContext() * add _Py_xi_state_init() and _Py_xi_state_fini() --- Include/internal/pycore_crossinterp.h | 33 +++- .../pycore_crossinterp_data_registry.h | 9 +- Modules/_interpchannelsmodule.c | 7 +- Modules/_interpqueuesmodule.c | 11 +- Modules/_interpreters_common.h | 15 +- Modules/_interpretersmodule.c | 17 +- Modules/_testinternalcapi.c | 8 +- Python/crossinterp.c | 163 +++++++++++++----- Python/crossinterp_data_lookup.h | 75 ++++---- 9 files changed, 247 insertions(+), 91 deletions(-) diff --git a/Include/internal/pycore_crossinterp.h b/Include/internal/pycore_crossinterp.h index 66719796aeee22..69a60d73e05c26 100644 --- a/Include/internal/pycore_crossinterp.h +++ b/Include/internal/pycore_crossinterp.h @@ -100,9 +100,26 @@ typedef int (*xidatafunc)(PyThreadState *tstate, PyObject *, _PyXIData_t *); typedef struct _xid_lookup_state _PyXIData_lookup_t; -PyAPI_FUNC(xidatafunc) _PyXIData_Lookup(PyObject *); -PyAPI_FUNC(int) _PyObject_CheckXIData(PyObject *); -PyAPI_FUNC(int) _PyObject_GetXIData(PyObject *, _PyXIData_t *); +typedef struct { + _PyXIData_lookup_t *global; + _PyXIData_lookup_t *local; + PyObject *PyExc_NotShareableError; +} _PyXIData_lookup_context_t; + +PyAPI_FUNC(int) _PyXIData_GetLookupContext( + PyInterpreterState *, + _PyXIData_lookup_context_t *); + +PyAPI_FUNC(xidatafunc) _PyXIData_Lookup( + _PyXIData_lookup_context_t *, + PyObject *); +PyAPI_FUNC(int) _PyObject_CheckXIData( + _PyXIData_lookup_context_t *, + PyObject *); +PyAPI_FUNC(int) _PyObject_GetXIData( + _PyXIData_lookup_context_t *, + PyObject *, + _PyXIData_t *); /* using cross-interpreter data */ @@ -173,12 +190,20 @@ typedef struct { } exceptions; } _PyXI_state_t; +#define _PyXI_GET_GLOBAL_STATE(interp) (&(interp)->runtime->xi) +#define _PyXI_GET_STATE(interp) (&(interp)->xi) + +#ifndef Py_BUILD_CORE_MODULE extern PyStatus _PyXI_Init(PyInterpreterState *interp); extern void _PyXI_Fini(PyInterpreterState *interp); extern PyStatus _PyXI_InitTypes(PyInterpreterState *interp); extern void _PyXI_FiniTypes(PyInterpreterState *interp); +#endif // Py_BUILD_CORE_MODULE -#define _PyInterpreterState_GetXIState(interp) (&(interp)->xi) +int _Py_xi_global_state_init(_PyXI_global_state_t *); +void _Py_xi_global_state_fini(_PyXI_global_state_t *); +int _Py_xi_state_init(_PyXI_state_t *, PyInterpreterState *); +void _Py_xi_state_fini(_PyXI_state_t *, PyInterpreterState *); /***************************/ diff --git a/Include/internal/pycore_crossinterp_data_registry.h b/Include/internal/pycore_crossinterp_data_registry.h index 04f25bc05fd1b8..bbad4de770857f 100644 --- a/Include/internal/pycore_crossinterp_data_registry.h +++ b/Include/internal/pycore_crossinterp_data_registry.h @@ -27,8 +27,13 @@ typedef struct { _PyXIData_regitem_t *head; } _PyXIData_registry_t; -PyAPI_FUNC(int) _PyXIData_RegisterClass(PyTypeObject *, xidatafunc); -PyAPI_FUNC(int) _PyXIData_UnregisterClass(PyTypeObject *); +PyAPI_FUNC(int) _PyXIData_RegisterClass( + _PyXIData_lookup_context_t *, + PyTypeObject *, + xidatafunc); +PyAPI_FUNC(int) _PyXIData_UnregisterClass( + _PyXIData_lookup_context_t *, + PyTypeObject *); struct _xid_lookup_state { // XXX Remove this field once we have a tp_* slot. diff --git a/Modules/_interpchannelsmodule.c b/Modules/_interpchannelsmodule.c index cd3c5026938568..75d69ade1d3c9b 100644 --- a/Modules/_interpchannelsmodule.c +++ b/Modules/_interpchannelsmodule.c @@ -1758,6 +1758,11 @@ channel_send(_channels *channels, int64_t cid, PyObject *obj, } int64_t interpid = PyInterpreterState_GetID(interp); + _PyXIData_lookup_context_t ctx; + if (_PyXIData_GetLookupContext(interp, &ctx) < 0) { + return -1; + } + // Look up the channel. PyThread_type_lock mutex = NULL; _channel_state *chan = NULL; @@ -1779,7 +1784,7 @@ channel_send(_channels *channels, int64_t cid, PyObject *obj, PyThread_release_lock(mutex); return -1; } - if (_PyObject_GetXIData(obj, data) != 0) { + if (_PyObject_GetXIData(&ctx, obj, data) != 0) { PyThread_release_lock(mutex); GLOBAL_FREE(data); return -1; diff --git a/Modules/_interpqueuesmodule.c b/Modules/_interpqueuesmodule.c index 8d0e223db7ff19..808938a9e8cd16 100644 --- a/Modules/_interpqueuesmodule.c +++ b/Modules/_interpqueuesmodule.c @@ -1127,6 +1127,12 @@ queue_destroy(_queues *queues, int64_t qid) static int queue_put(_queues *queues, int64_t qid, PyObject *obj, int fmt, int unboundop) { + PyInterpreterState *interp = PyInterpreterState_Get(); + _PyXIData_lookup_context_t ctx; + if (_PyXIData_GetLookupContext(interp, &ctx) < 0) { + return -1; + } + // Look up the queue. _queue *queue = NULL; int err = _queues_lookup(queues, qid, &queue); @@ -1141,13 +1147,12 @@ queue_put(_queues *queues, int64_t qid, PyObject *obj, int fmt, int unboundop) _queue_unmark_waiter(queue, queues->mutex); return -1; } - if (_PyObject_GetXIData(obj, data) != 0) { + if (_PyObject_GetXIData(&ctx, obj, data) != 0) { _queue_unmark_waiter(queue, queues->mutex); GLOBAL_FREE(data); return -1; } - assert(_PyXIData_INTERPID(data) == \ - PyInterpreterState_GetID(PyInterpreterState_Get())); + assert(_PyXIData_INTERPID(data) == PyInterpreterState_GetID(interp)); // Add the data to the queue. int64_t interpid = -1; // _queueitem_init() will set it. diff --git a/Modules/_interpreters_common.h b/Modules/_interpreters_common.h index b0e31a33734dab..a6c639feea5d14 100644 --- a/Modules/_interpreters_common.h +++ b/Modules/_interpreters_common.h @@ -8,15 +8,24 @@ static int ensure_xid_class(PyTypeObject *cls, xidatafunc getdata) { - //assert(cls->tp_flags & Py_TPFLAGS_HEAPTYPE); - return _PyXIData_RegisterClass(cls, getdata); + PyInterpreterState *interp = PyInterpreterState_Get(); + _PyXIData_lookup_context_t ctx; + if (_PyXIData_GetLookupContext(interp, &ctx) < 0) { + return -1; + } + return _PyXIData_RegisterClass(&ctx, cls, getdata); } #ifdef REGISTERS_HEAP_TYPES static int clear_xid_class(PyTypeObject *cls) { - return _PyXIData_UnregisterClass(cls); + PyInterpreterState *interp = PyInterpreterState_Get(); + _PyXIData_lookup_context_t ctx; + if (_PyXIData_GetLookupContext(interp, &ctx) < 0) { + return -1; + } + return _PyXIData_UnregisterClass(&ctx, cls); } #endif diff --git a/Modules/_interpretersmodule.c b/Modules/_interpretersmodule.c index eb4ac9847dcd2b..a36823c4bb982b 100644 --- a/Modules/_interpretersmodule.c +++ b/Modules/_interpretersmodule.c @@ -1186,7 +1186,13 @@ object_is_shareable(PyObject *self, PyObject *args, PyObject *kwds) return NULL; } - if (_PyObject_CheckXIData(obj) == 0) { + PyInterpreterState *interp = PyInterpreterState_Get(); + _PyXIData_lookup_context_t ctx; + if (_PyXIData_GetLookupContext(interp, &ctx) < 0) { + return NULL; + } + + if (_PyObject_CheckXIData(&ctx, obj) == 0) { Py_RETURN_TRUE; } PyErr_Clear(); @@ -1485,6 +1491,11 @@ module_exec(PyObject *mod) PyInterpreterState *interp = PyInterpreterState_Get(); module_state *state = get_module_state(mod); + _PyXIData_lookup_context_t ctx; + if (_PyXIData_GetLookupContext(interp, &ctx) < 0) { + return -1; + } + #define ADD_WHENCE(NAME) \ if (PyModule_AddIntConstant(mod, "WHENCE_" #NAME, \ _PyInterpreterState_WHENCE_##NAME) < 0) \ @@ -1506,9 +1517,7 @@ module_exec(PyObject *mod) if (PyModule_AddType(mod, (PyTypeObject *)PyExc_InterpreterNotFoundError) < 0) { goto error; } - PyObject *PyExc_NotShareableError = \ - _PyInterpreterState_GetXIState(interp)->exceptions.PyExc_NotShareableError; - if (PyModule_AddType(mod, (PyTypeObject *)PyExc_NotShareableError) < 0) { + if (PyModule_AddType(mod, (PyTypeObject *)ctx.PyExc_NotShareableError) < 0) { goto error; } diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index 327a077671047c..2c1ebcbbfdf419 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -1797,6 +1797,12 @@ _xid_capsule_destructor(PyObject *capsule) static PyObject * get_crossinterp_data(PyObject *self, PyObject *args) { + PyInterpreterState *interp = PyInterpreterState_Get(); + _PyXIData_lookup_context_t ctx; + if (_PyXIData_GetLookupContext(interp, &ctx) < 0) { + return NULL; + } + PyObject *obj = NULL; if (!PyArg_ParseTuple(args, "O:get_crossinterp_data", &obj)) { return NULL; @@ -1806,7 +1812,7 @@ get_crossinterp_data(PyObject *self, PyObject *args) if (data == NULL) { return NULL; } - if (_PyObject_GetXIData(obj, data) != 0) { + if (_PyObject_GetXIData(&ctx, obj, data) != 0) { _PyXIData_Free(data); return NULL; } diff --git a/Python/crossinterp.c b/Python/crossinterp.c index dfdb5f9d87a7c7..fe7d75f6b72f68 100644 --- a/Python/crossinterp.c +++ b/Python/crossinterp.c @@ -9,10 +9,6 @@ #include "pycore_pyerrors.h" // _PyErr_Clear() -#define _PyXI_GET_GLOBAL_STATE(interp) (&(interp)->runtime->xi) -#define _PyXI_GET_STATE(interp) (&(interp)->xi) - - /**************/ /* exceptions */ /**************/ @@ -68,7 +64,7 @@ _Py_CallInInterpreterAndRawFree(PyInterpreterState *interp, static void xid_lookup_init(_PyXIData_lookup_t *); static void xid_lookup_fini(_PyXIData_lookup_t *); -static xidatafunc lookup_getdata(PyInterpreterState *, PyObject *); +static xidatafunc lookup_getdata(_PyXIData_lookup_context_t *, PyObject *); #include "crossinterp_data_lookup.h" @@ -202,9 +198,9 @@ _check_xidata(PyThreadState *tstate, _PyXIData_t *data) } static inline void -_set_xid_lookup_failure(_PyXI_state_t *state, PyObject *obj, const char *msg) +_set_xid_lookup_failure(dlcontext_t *ctx, PyObject *obj, const char *msg) { - PyObject *exctype = state->exceptions.PyExc_NotShareableError; + PyObject *exctype = ctx->PyExc_NotShareableError; assert(exctype != NULL); if (msg != NULL) { assert(obj == NULL); @@ -221,14 +217,12 @@ _set_xid_lookup_failure(_PyXI_state_t *state, PyObject *obj, const char *msg) } int -_PyObject_CheckXIData(PyObject *obj) +_PyObject_CheckXIData(_PyXIData_lookup_context_t *ctx, PyObject *obj) { - PyInterpreterState *interp = PyInterpreterState_Get(); - _PyXI_state_t *state = _PyXI_GET_STATE(interp); - xidatafunc getdata = lookup_getdata(interp, obj); + xidatafunc getdata = lookup_getdata(ctx, obj); if (getdata == NULL) { if (!PyErr_Occurred()) { - _set_xid_lookup_failure(state, obj, NULL); + _set_xid_lookup_failure(ctx, obj, NULL); } return -1; } @@ -236,11 +230,11 @@ _PyObject_CheckXIData(PyObject *obj) } int -_PyObject_GetXIData(PyObject *obj, _PyXIData_t *data) +_PyObject_GetXIData(_PyXIData_lookup_context_t *ctx, + PyObject *obj, _PyXIData_t *data) { PyThreadState *tstate = PyThreadState_Get(); PyInterpreterState *interp = tstate->interp; - _PyXI_state_t *state = _PyXI_GET_STATE(interp); // Reset data before re-populating. *data = (_PyXIData_t){0}; @@ -248,11 +242,11 @@ _PyObject_GetXIData(PyObject *obj, _PyXIData_t *data) // Call the "getdata" func for the object. Py_INCREF(obj); - xidatafunc getdata = lookup_getdata(interp, obj); + xidatafunc getdata = lookup_getdata(ctx, obj); if (getdata == NULL) { Py_DECREF(obj); if (!PyErr_Occurred()) { - _set_xid_lookup_failure(state, obj, NULL); + _set_xid_lookup_failure(ctx, obj, NULL); } return -1; } @@ -969,7 +963,8 @@ _PyXI_ClearExcInfo(_PyXI_excinfo *info) static int _PyXI_ApplyErrorCode(_PyXI_errcode code, PyInterpreterState *interp) { - _PyXI_state_t *state; + dlcontext_t ctx; + assert(!PyErr_Occurred()); switch (code) { case _PyXI_ERR_NO_ERROR: _Py_FALLTHROUGH; @@ -1000,8 +995,10 @@ _PyXI_ApplyErrorCode(_PyXI_errcode code, PyInterpreterState *interp) "failed to apply namespace to __main__"); break; case _PyXI_ERR_NOT_SHAREABLE: - state = _PyXI_GET_STATE(interp); - _set_xid_lookup_failure(state, NULL, NULL); + if (_PyXIData_GetLookupContext(interp, &ctx) < 0) { + return -1; + } + _set_xid_lookup_failure(&ctx, NULL, NULL); break; default: #ifdef Py_DEBUG @@ -1063,8 +1060,11 @@ _PyXI_ApplyError(_PyXI_error *error) } else if (error->code == _PyXI_ERR_NOT_SHAREABLE) { // Propagate the exception directly. - _PyXI_state_t *state = _PyXI_GET_STATE(error->interp); - _set_xid_lookup_failure(state, NULL, error->uncaught.msg); + dlcontext_t ctx; + if (_PyXIData_GetLookupContext(error->interp, &ctx) < 0) { + return NULL; + } + _set_xid_lookup_failure(&ctx, NULL, error->uncaught.msg); } else { // Raise an exception corresponding to the code. @@ -1151,7 +1151,12 @@ _sharednsitem_set_value(_PyXI_namespace_item *item, PyObject *value) PyErr_NoMemory(); return -1; } - if (_PyObject_GetXIData(value, item->data) != 0) { + PyInterpreterState *interp = PyInterpreterState_Get(); + dlcontext_t ctx; + if (_PyXIData_GetLookupContext(interp, &ctx) < 0) { + return -1; + } + if (_PyObject_GetXIData(&ctx, value, item->data) != 0) { PyMem_RawFree(item->data); item->data = NULL; // The caller may want to propagate PyExc_NotShareableError @@ -1609,9 +1614,13 @@ _propagate_not_shareable_error(_PyXI_session *session) return; } PyInterpreterState *interp = PyInterpreterState_Get(); - _PyXI_state_t *state = _PyXI_GET_STATE(interp); - assert(state->exceptions.PyExc_NotShareableError != NULL); - if (PyErr_ExceptionMatches(state->exceptions.PyExc_NotShareableError)) { + dlcontext_t ctx; + if (_PyXIData_GetLookupContext(interp, &ctx) < 0) { + PyErr_FormatUnraisable( + "Exception ignored while propagating not shareable error"); + return; + } + if (PyErr_ExceptionMatches(ctx.PyExc_NotShareableError)) { // We want to propagate the exception directly. session->_error_override = _PyXI_ERR_NOT_SHAREABLE; session->error_override = &session->_error_override; @@ -1779,22 +1788,87 @@ _PyXI_Exit(_PyXI_session *session) /* runtime lifecycle */ /*********************/ +int +_Py_xi_global_state_init(_PyXI_global_state_t *state) +{ + assert(state != NULL); + xid_lookup_init(&state->data_lookup); + return 0; +} + +void +_Py_xi_global_state_fini(_PyXI_global_state_t *state) +{ + assert(state != NULL); + xid_lookup_fini(&state->data_lookup); +} + +int +_Py_xi_state_init(_PyXI_state_t *state, PyInterpreterState *interp) +{ + assert(state != NULL); + assert(interp == NULL || state == _PyXI_GET_STATE(interp)); + + xid_lookup_init(&state->data_lookup); + + // Initialize exceptions. + if (interp != NULL) { + if (init_static_exctypes(&state->exceptions, interp) < 0) { + fini_heap_exctypes(&state->exceptions); + return -1; + } + } + if (init_heap_exctypes(&state->exceptions) < 0) { + return -1; + } + + return 0; +} + +void +_Py_xi_state_fini(_PyXI_state_t *state, PyInterpreterState *interp) +{ + assert(state != NULL); + assert(interp == NULL || state == _PyXI_GET_STATE(interp)); + + fini_heap_exctypes(&state->exceptions); + if (interp != NULL) { + fini_static_exctypes(&state->exceptions, interp); + } + + xid_lookup_fini(&state->data_lookup); +} + + PyStatus _PyXI_Init(PyInterpreterState *interp) { - _PyXI_state_t *state = _PyXI_GET_STATE(interp); - - // Initialize the XID lookup state (e.g. registry). if (_Py_IsMainInterpreter(interp)) { - xid_lookup_init(&_PyXI_GET_GLOBAL_STATE(interp)->data_lookup); + _PyXI_global_state_t *global_state = _PyXI_GET_GLOBAL_STATE(interp); + if (global_state == NULL) { + PyErr_PrintEx(0); + return _PyStatus_ERR( + "failed to get global cross-interpreter state"); + } + if (_Py_xi_global_state_init(global_state) < 0) { + PyErr_PrintEx(0); + return _PyStatus_ERR( + "failed to initialize global cross-interpreter state"); + } } - xid_lookup_init(&state->data_lookup); - // Initialize exceptions.(heap types). - // See _PyXI_InitTypes() for the static types. - if (init_heap_exctypes(&_PyXI_GET_STATE(interp)->exceptions) < 0) { + _PyXI_state_t *state = _PyXI_GET_STATE(interp); + if (state == NULL) { + PyErr_PrintEx(0); + return _PyStatus_ERR( + "failed to get interpreter's cross-interpreter state"); + } + // The static types were already initialized in _PyXI_InitTypes(), + // so we pass in NULL here to avoid initializing them again. + if (_Py_xi_state_init(state, NULL) < 0) { PyErr_PrintEx(0); - return _PyStatus_ERR("failed to initialize exceptions"); + return _PyStatus_ERR( + "failed to initialize interpreter's cross-interpreter state"); } return _PyStatus_OK(); @@ -1807,15 +1881,19 @@ void _PyXI_Fini(PyInterpreterState *interp) { _PyXI_state_t *state = _PyXI_GET_STATE(interp); +#ifndef NDEBUG + if (state == NULL) { + PyErr_PrintEx(0); + return; + } +#endif + // The static types will be finalized soon in _PyXI_FiniTypes(), + // so we pass in NULL here to avoid finalizing them right now. + _Py_xi_state_fini(state, NULL); - // Finalize exceptions (heap types). - // See _PyXI_FiniTypes() for the static types. - fini_heap_exctypes(&_PyXI_GET_STATE(interp)->exceptions); - - // Finalize the XID lookup state (e.g. registry). - xid_lookup_fini(&state->data_lookup); if (_Py_IsMainInterpreter(interp)) { - xid_lookup_fini(&_PyXI_GET_GLOBAL_STATE(interp)->data_lookup); + _PyXI_global_state_t *global_state = _PyXI_GET_GLOBAL_STATE(interp); + _Py_xi_global_state_fini(global_state); } } @@ -1824,7 +1902,8 @@ _PyXI_InitTypes(PyInterpreterState *interp) { if (init_static_exctypes(&_PyXI_GET_STATE(interp)->exceptions, interp) < 0) { PyErr_PrintEx(0); - return _PyStatus_ERR("failed to initialize an exception type"); + return _PyStatus_ERR( + "failed to initialize the cross-interpreter exception types"); } // We would initialize heap types here too but that leads to ref leaks. // Instead, we intialize them in _PyXI_Init(). diff --git a/Python/crossinterp_data_lookup.h b/Python/crossinterp_data_lookup.h index 9048f90cff160a..48e5d9762cd697 100644 --- a/Python/crossinterp_data_lookup.h +++ b/Python/crossinterp_data_lookup.h @@ -1,6 +1,7 @@ #include "pycore_weakref.h" // _PyWeakref_GET_REF() +typedef _PyXIData_lookup_context_t dlcontext_t; typedef _PyXIData_registry_t dlregistry_t; typedef _PyXIData_regitem_t dlregitem_t; @@ -8,7 +9,7 @@ typedef _PyXIData_regitem_t dlregitem_t; // forward static void _xidregistry_init(dlregistry_t *); static void _xidregistry_fini(dlregistry_t *); -static xidatafunc _lookup_getdata_from_registry(PyInterpreterState *, PyObject *); +static xidatafunc _lookup_getdata_from_registry(dlcontext_t *, PyObject *); /* used in crossinterp.c */ @@ -26,22 +27,43 @@ xid_lookup_fini(_PyXIData_lookup_t *state) } static xidatafunc -lookup_getdata(PyInterpreterState *interp, PyObject *obj) +lookup_getdata(dlcontext_t *ctx, PyObject *obj) { /* Cross-interpreter objects are looked up by exact match on the class. We can reassess this policy when we move from a global registry to a tp_* slot. */ - return _lookup_getdata_from_registry(interp, obj); + return _lookup_getdata_from_registry(ctx, obj); } /* exported API */ +int +_PyXIData_GetLookupContext(PyInterpreterState *interp, + _PyXIData_lookup_context_t *res) +{ + _PyXI_global_state_t *global = _PyXI_GET_GLOBAL_STATE(interp); + if (global == NULL) { + assert(PyErr_Occurred()); + return -1; + } + _PyXI_state_t *local = _PyXI_GET_STATE(interp); + if (local == NULL) { + assert(PyErr_Occurred()); + return -1; + } + *res = (dlcontext_t){ + .global = &global->data_lookup, + .local = &local->data_lookup, + .PyExc_NotShareableError = local->exceptions.PyExc_NotShareableError, + }; + return 0; +} + xidatafunc -_PyXIData_Lookup(PyObject *obj) +_PyXIData_Lookup(_PyXIData_lookup_context_t *ctx, PyObject *obj) { - PyInterpreterState *interp = PyInterpreterState_Get(); - return lookup_getdata(interp, obj); + return lookup_getdata(ctx, obj); } @@ -110,25 +132,12 @@ _xidregistry_unlock(dlregistry_t *registry) /* accessing the registry */ static inline dlregistry_t * -_get_global_xidregistry(_PyRuntimeState *runtime) +_get_xidregistry_for_type(dlcontext_t *ctx, PyTypeObject *cls) { - return &runtime->xi.data_lookup.registry; -} - -static inline dlregistry_t * -_get_xidregistry(PyInterpreterState *interp) -{ - return &interp->xi.data_lookup.registry; -} - -static inline dlregistry_t * -_get_xidregistry_for_type(PyInterpreterState *interp, PyTypeObject *cls) -{ - dlregistry_t *registry = _get_global_xidregistry(interp->runtime); if (cls->tp_flags & Py_TPFLAGS_HEAPTYPE) { - registry = _get_xidregistry(interp); + return &ctx->local->registry; } - return registry; + return &ctx->global->registry; } static dlregitem_t* _xidregistry_remove_entry(dlregistry_t *, dlregitem_t *); @@ -160,11 +169,11 @@ _xidregistry_find_type(dlregistry_t *xidregistry, PyTypeObject *cls) } static xidatafunc -_lookup_getdata_from_registry(PyInterpreterState *interp, PyObject *obj) +_lookup_getdata_from_registry(dlcontext_t *ctx, PyObject *obj) { PyTypeObject *cls = Py_TYPE(obj); - dlregistry_t *xidregistry = _get_xidregistry_for_type(interp, cls); + dlregistry_t *xidregistry = _get_xidregistry_for_type(ctx, cls); _xidregistry_lock(xidregistry); dlregitem_t *matched = _xidregistry_find_type(xidregistry, cls); @@ -241,7 +250,8 @@ _xidregistry_clear(dlregistry_t *xidregistry) } int -_PyXIData_RegisterClass(PyTypeObject *cls, xidatafunc getdata) +_PyXIData_RegisterClass(_PyXIData_lookup_context_t *ctx, + PyTypeObject *cls, xidatafunc getdata) { if (!PyType_Check(cls)) { PyErr_Format(PyExc_ValueError, "only classes may be registered"); @@ -253,8 +263,7 @@ _PyXIData_RegisterClass(PyTypeObject *cls, xidatafunc getdata) } int res = 0; - PyInterpreterState *interp = _PyInterpreterState_GET(); - dlregistry_t *xidregistry = _get_xidregistry_for_type(interp, cls); + dlregistry_t *xidregistry = _get_xidregistry_for_type(ctx, cls); _xidregistry_lock(xidregistry); dlregitem_t *matched = _xidregistry_find_type(xidregistry, cls); @@ -272,11 +281,10 @@ _PyXIData_RegisterClass(PyTypeObject *cls, xidatafunc getdata) } int -_PyXIData_UnregisterClass(PyTypeObject *cls) +_PyXIData_UnregisterClass(_PyXIData_lookup_context_t *ctx, PyTypeObject *cls) { int res = 0; - PyInterpreterState *interp = _PyInterpreterState_GET(); - dlregistry_t *xidregistry = _get_xidregistry_for_type(interp, cls); + dlregistry_t *xidregistry = _get_xidregistry_for_type(ctx, cls); _xidregistry_lock(xidregistry); dlregitem_t *matched = _xidregistry_find_type(xidregistry, cls); @@ -500,6 +508,11 @@ _tuple_shared_free(void* data) static int _tuple_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *data) { + dlcontext_t ctx; + if (_PyXIData_GetLookupContext(tstate->interp, &ctx) < 0) { + return -1; + } + Py_ssize_t len = PyTuple_GET_SIZE(obj); if (len < 0) { return -1; @@ -526,7 +539,7 @@ _tuple_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *data) int res = -1; if (!_Py_EnterRecursiveCallTstate(tstate, " while sharing a tuple")) { - res = _PyObject_GetXIData(item, data); + res = _PyObject_GetXIData(&ctx, item, data); _Py_LeaveRecursiveCallTstate(tstate); } if (res < 0) { From a83472f49b958c55befd82c871be26afbf500306 Mon Sep 17 00:00:00 2001 From: Stephen Morton Date: Tue, 12 Nov 2024 09:54:13 -0800 Subject: [PATCH 154/219] gh-126705: Make os.PathLike more like a protocol (#126706) it can now be used as a base class in other protocols --- Lib/test/test_typing.py | 4 ++++ Lib/typing.py | 1 + .../Library/2024-11-11-14-52-21.gh-issue-126705.0W7jFW.rst | 1 + 3 files changed, 6 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2024-11-11-14-52-21.gh-issue-126705.0W7jFW.rst diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 244ce1e5da9bd2..aa42beca5f9256 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -8,6 +8,7 @@ import inspect import itertools import operator +import os import pickle import re import sys @@ -4252,6 +4253,9 @@ def test_builtin_protocol_allowlist(self): class CustomProtocol(TestCase, Protocol): pass + class CustomPathLikeProtocol(os.PathLike, Protocol): + pass + class CustomContextManager(typing.ContextManager, Protocol): pass diff --git a/Lib/typing.py b/Lib/typing.py index 8e6381033fd28e..938e52922aee03 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1944,6 +1944,7 @@ def _allow_reckless_class_checks(depth=2): 'Reversible', 'Buffer', ], 'contextlib': ['AbstractContextManager', 'AbstractAsyncContextManager'], + 'os': ['PathLike'], } diff --git a/Misc/NEWS.d/next/Library/2024-11-11-14-52-21.gh-issue-126705.0W7jFW.rst b/Misc/NEWS.d/next/Library/2024-11-11-14-52-21.gh-issue-126705.0W7jFW.rst new file mode 100644 index 00000000000000..f49c9c765d778f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-11-11-14-52-21.gh-issue-126705.0W7jFW.rst @@ -0,0 +1 @@ +Allow :class:`os.PathLike` to be a base for Protocols. From 03924b5deeb766fabd53ced28ba707e4dd08fb60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Tue, 12 Nov 2024 19:08:49 +0100 Subject: [PATCH 155/219] gh-89083: add support for UUID version 8 (RFC 9562) (#123224) Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- Doc/library/uuid.rst | 42 +++++++++++++++---- Doc/whatsnew/3.14.rst | 8 ++++ Lib/test/test_uuid.py | 35 +++++++++++++++- Lib/uuid.py | 41 ++++++++++++++---- ...4-08-22-12-12-35.gh-issue-89083.b6zFh0.rst | 2 + 5 files changed, 109 insertions(+), 19 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-08-22-12-12-35.gh-issue-89083.b6zFh0.rst diff --git a/Doc/library/uuid.rst b/Doc/library/uuid.rst index 0f2d7820cb25c8..6166c22caedf81 100644 --- a/Doc/library/uuid.rst +++ b/Doc/library/uuid.rst @@ -1,8 +1,8 @@ -:mod:`!uuid` --- UUID objects according to :rfc:`4122` +:mod:`!uuid` --- UUID objects according to :rfc:`9562` ====================================================== .. module:: uuid - :synopsis: UUID objects (universally unique identifiers) according to RFC 4122 + :synopsis: UUID objects (universally unique identifiers) according to RFC 9562 .. moduleauthor:: Ka-Ping Yee .. sectionauthor:: George Yoshida @@ -12,7 +12,8 @@ This module provides immutable :class:`UUID` objects (the :class:`UUID` class) and the functions :func:`uuid1`, :func:`uuid3`, :func:`uuid4`, :func:`uuid5` for -generating version 1, 3, 4, and 5 UUIDs as specified in :rfc:`4122`. +generating version 1, 3, 4, 5, and 8 UUIDs as specified in :rfc:`9562` (which +supersedes :rfc:`4122`). If all you want is a unique ID, you should probably call :func:`uuid1` or :func:`uuid4`. Note that :func:`uuid1` may compromise privacy since it creates @@ -65,7 +66,7 @@ which relays any information about the UUID's safety, using this enumeration: Exactly one of *hex*, *bytes*, *bytes_le*, *fields*, or *int* must be given. The *version* argument is optional; if given, the resulting UUID will have its - variant and version number set according to :rfc:`4122`, overriding bits in the + variant and version number set according to :rfc:`9562`, overriding bits in the given *hex*, *bytes*, *bytes_le*, *fields*, or *int*. Comparison of UUID objects are made by way of comparing their @@ -137,7 +138,7 @@ which relays any information about the UUID's safety, using this enumeration: .. attribute:: UUID.urn - The UUID as a URN as specified in :rfc:`4122`. + The UUID as a URN as specified in :rfc:`9562`. .. attribute:: UUID.variant @@ -149,9 +150,13 @@ which relays any information about the UUID's safety, using this enumeration: .. attribute:: UUID.version - The UUID version number (1 through 5, meaningful only when the variant is + The UUID version number (1 through 8, meaningful only when the variant is :const:`RFC_4122`). + .. versionchanged:: next + Added UUID version 8. + + .. attribute:: UUID.is_safe An enumeration of :class:`SafeUUID` which indicates whether the platform @@ -216,6 +221,23 @@ The :mod:`uuid` module defines the following functions: .. index:: single: uuid5 + +.. function:: uuid8(a=None, b=None, c=None) + + Generate a pseudo-random UUID according to + :rfc:`RFC 9562, §5.8 <9562#section-5.8>`. + + When specified, the parameters *a*, *b* and *c* are expected to be + positive integers of 48, 12 and 62 bits respectively. If they exceed + their expected bit count, only their least significant bits are kept; + non-specified arguments are substituted for a pseudo-random integer of + appropriate size. + + .. versionadded:: next + +.. index:: single: uuid8 + + The :mod:`uuid` module defines the following namespace identifiers for use with :func:`uuid3` or :func:`uuid5`. @@ -252,7 +274,9 @@ of the :attr:`~UUID.variant` attribute: .. data:: RFC_4122 - Specifies the UUID layout given in :rfc:`4122`. + Specifies the UUID layout given in :rfc:`4122`. This constant is kept + for backward compatibility even though :rfc:`4122` has been superseded + by :rfc:`9562`. .. data:: RESERVED_MICROSOFT @@ -267,7 +291,7 @@ of the :attr:`~UUID.variant` attribute: .. seealso:: - :rfc:`4122` - A Universally Unique IDentifier (UUID) URN Namespace + :rfc:`9562` - A Universally Unique IDentifier (UUID) URN Namespace This specification defines a Uniform Resource Name namespace for UUIDs, the internal format of UUIDs, and methods of generating UUIDs. @@ -283,7 +307,7 @@ The :mod:`uuid` module can be executed as a script from the command line. .. code-block:: sh - python -m uuid [-h] [-u {uuid1,uuid3,uuid4,uuid5}] [-n NAMESPACE] [-N NAME] + python -m uuid [-h] [-u {uuid1,uuid3,uuid4,uuid5,uuid8}] [-n NAMESPACE] [-N NAME] The following options are accepted: diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 20bd3aa5221454..c2cf46902fd7fe 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -517,6 +517,14 @@ unittest (Contributed by Jacob Walls in :gh:`80958`.) +uuid +---- + +* Add support for UUID version 8 via :func:`uuid.uuid8` as specified + in :rfc:`9562`. + (Contributed by Bénédikt Tran in :gh:`89083`.) + + .. Add improved modules above alphabetically, not here at the end. Optimizations diff --git a/Lib/test/test_uuid.py b/Lib/test/test_uuid.py index e177464c00f7a6..7bd26a8ca34b62 100755 --- a/Lib/test/test_uuid.py +++ b/Lib/test/test_uuid.py @@ -8,8 +8,10 @@ import io import os import pickle +import random import sys import weakref +from itertools import product from unittest import mock py_uuid = import_helper.import_fresh_module('uuid', blocked=['_uuid']) @@ -267,7 +269,7 @@ def test_exceptions(self): # Version number out of range. badvalue(lambda: self.uuid.UUID('00'*16, version=0)) - badvalue(lambda: self.uuid.UUID('00'*16, version=6)) + badvalue(lambda: self.uuid.UUID('00'*16, version=42)) # Integer value out of range. badvalue(lambda: self.uuid.UUID(int=-1)) @@ -681,6 +683,37 @@ def test_uuid5(self): equal(u, self.uuid.UUID(v)) equal(str(u), v) + def test_uuid8(self): + equal = self.assertEqual + u = self.uuid.uuid8() + + equal(u.variant, self.uuid.RFC_4122) + equal(u.version, 8) + + for (_, hi, mid, lo) in product( + range(10), # repeat 10 times + [None, 0, random.getrandbits(48)], + [None, 0, random.getrandbits(12)], + [None, 0, random.getrandbits(62)], + ): + u = self.uuid.uuid8(hi, mid, lo) + equal(u.variant, self.uuid.RFC_4122) + equal(u.version, 8) + if hi is not None: + equal((u.int >> 80) & 0xffffffffffff, hi) + if mid is not None: + equal((u.int >> 64) & 0xfff, mid) + if lo is not None: + equal(u.int & 0x3fffffffffffffff, lo) + + def test_uuid8_uniqueness(self): + # Test that UUIDv8-generated values are unique + # (up to a negligible probability of failure). + u1 = self.uuid.uuid8() + u2 = self.uuid.uuid8() + self.assertNotEqual(u1.int, u2.int) + self.assertEqual(u1.version, u2.version) + @support.requires_fork() def testIssue8621(self): # On at least some versions of OSX self.uuid.uuid4 generates diff --git a/Lib/uuid.py b/Lib/uuid.py index 4d4f06cfc9ebbe..9c6ad9643cf6d5 100644 --- a/Lib/uuid.py +++ b/Lib/uuid.py @@ -1,8 +1,8 @@ -r"""UUID objects (universally unique identifiers) according to RFC 4122. +r"""UUID objects (universally unique identifiers) according to RFC 4122/9562. This module provides immutable UUID objects (class UUID) and the functions -uuid1(), uuid3(), uuid4(), uuid5() for generating version 1, 3, 4, and 5 -UUIDs as specified in RFC 4122. +uuid1(), uuid3(), uuid4(), uuid5(), and uuid8() for generating version 1, 3, +4, 5, and 8 UUIDs as specified in RFC 4122/9562. If all you want is a unique ID, you should probably call uuid1() or uuid4(). Note that uuid1() may compromise privacy since it creates a UUID containing @@ -124,12 +124,12 @@ class UUID: int the UUID as a 128-bit integer - urn the UUID as a URN as specified in RFC 4122 + urn the UUID as a URN as specified in RFC 4122/9562 variant the UUID variant (one of the constants RESERVED_NCS, RFC_4122, RESERVED_MICROSOFT, or RESERVED_FUTURE) - version the UUID version number (1 through 5, meaningful only + version the UUID version number (1 through 8, meaningful only when the variant is RFC_4122) is_safe An enum indicating whether the UUID has been generated in @@ -214,9 +214,9 @@ def __init__(self, hex=None, bytes=None, bytes_le=None, fields=None, if not 0 <= int < 1<<128: raise ValueError('int is out of range (need a 128-bit value)') if version is not None: - if not 1 <= version <= 5: + if not 1 <= version <= 8: raise ValueError('illegal version number') - # Set the variant to RFC 4122. + # Set the variant to RFC 4122/9562. int &= ~(0xc000 << 48) int |= 0x8000 << 48 # Set the version number. @@ -355,7 +355,7 @@ def variant(self): @property def version(self): - # The version bits are only meaningful for RFC 4122 UUIDs. + # The version bits are only meaningful for RFC 4122/9562 UUIDs. if self.variant == RFC_4122: return int((self.int >> 76) & 0xf) @@ -719,6 +719,28 @@ def uuid5(namespace, name): hash = sha1(namespace.bytes + name).digest() return UUID(bytes=hash[:16], version=5) +def uuid8(a=None, b=None, c=None): + """Generate a UUID from three custom blocks. + + * 'a' is the first 48-bit chunk of the UUID (octets 0-5); + * 'b' is the mid 12-bit chunk (octets 6-7); + * 'c' is the last 62-bit chunk (octets 8-15). + + When a value is not specified, a pseudo-random value is generated. + """ + if a is None: + import random + a = random.getrandbits(48) + if b is None: + import random + b = random.getrandbits(12) + if c is None: + import random + c = random.getrandbits(62) + int_uuid_8 = (a & 0xffff_ffff_ffff) << 80 + int_uuid_8 |= (b & 0xfff) << 64 + int_uuid_8 |= c & 0x3fff_ffff_ffff_ffff + return UUID(int=int_uuid_8, version=8) def main(): """Run the uuid command line interface.""" @@ -726,7 +748,8 @@ def main(): "uuid1": uuid1, "uuid3": uuid3, "uuid4": uuid4, - "uuid5": uuid5 + "uuid5": uuid5, + "uuid8": uuid8, } uuid_namespace_funcs = ("uuid3", "uuid5") namespaces = { diff --git a/Misc/NEWS.d/next/Library/2024-08-22-12-12-35.gh-issue-89083.b6zFh0.rst b/Misc/NEWS.d/next/Library/2024-08-22-12-12-35.gh-issue-89083.b6zFh0.rst new file mode 100644 index 00000000000000..d37d585d51b490 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-08-22-12-12-35.gh-issue-89083.b6zFh0.rst @@ -0,0 +1,2 @@ +Add :func:`uuid.uuid8` for generating UUIDv8 objects as specified in +:rfc:`9562`. Patch by Bénédikt Tran From 7577307ebdaeef6702b639e22a896080e81aae4e Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 12 Nov 2024 21:10:29 +0200 Subject: [PATCH 156/219] gh-116897: Deprecate generic false values in urllib.parse.parse_qsl() (GH-116903) Accepting objects with false values (like 0 and []) except empty strings and byte-like objects and None in urllib.parse functions parse_qsl() and parse_qs() is now deprecated. --- Doc/library/urllib.parse.rst | 8 ++++++ Doc/whatsnew/3.14.rst | 7 ++++++ Lib/test/test_urlparse.py | 10 +++++++- Lib/urllib/parse.py | 25 +++++++++++++------ ...-03-16-13-38-27.gh-issue-116897.UDQTjp.rst | 4 +++ 5 files changed, 45 insertions(+), 9 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-03-16-13-38-27.gh-issue-116897.UDQTjp.rst diff --git a/Doc/library/urllib.parse.rst b/Doc/library/urllib.parse.rst index fb5353e1895bf9..0501dc8733b2cd 100644 --- a/Doc/library/urllib.parse.rst +++ b/Doc/library/urllib.parse.rst @@ -239,6 +239,10 @@ or on combining URL components into a URL string. query parameter separator. This has been changed to allow only a single separator key, with ``&`` as the default separator. + .. deprecated:: 3.14 + Accepting objects with false values (like ``0`` and ``[]``) except empty + strings and byte-like objects and ``None`` is now deprecated. + .. function:: parse_qsl(qs, keep_blank_values=False, strict_parsing=False, encoding='utf-8', errors='replace', max_num_fields=None, separator='&') @@ -745,6 +749,10 @@ task isn't already covered by the URL parsing functions above. .. versionchanged:: 3.5 Added the *quote_via* parameter. + .. deprecated:: 3.14 + Accepting objects with false values (like ``0`` and ``[]``) except empty + strings and byte-like objects and ``None`` is now deprecated. + .. seealso:: diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index c2cf46902fd7fe..a98fe3f468b685 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -583,6 +583,13 @@ Deprecated Deprecate :meth:`symtable.Class.get_methods` due to the lack of interest. (Contributed by Bénédikt Tran in :gh:`119698`.) +* :mod:`urllib.parse`: + Accepting objects with false values (like ``0`` and ``[]``) except empty + strings, byte-like objects and ``None`` in :mod:`urllib.parse` functions + :func:`~urllib.parse.parse_qsl` and :func:`~urllib.parse.parse_qs` is now + deprecated. + (Contributed by Serhiy Storchaka in :gh:`116897`.) + .. Add deprecations above alphabetically, not here at the end. .. include:: ../deprecations/pending-removal-in-3.15.rst diff --git a/Lib/test/test_urlparse.py b/Lib/test/test_urlparse.py index 297fb4831c16bf..4516bdea6adb19 100644 --- a/Lib/test/test_urlparse.py +++ b/Lib/test/test_urlparse.py @@ -1314,9 +1314,17 @@ def test_parse_qsl_bytes(self): def test_parse_qsl_false_value(self): kwargs = dict(keep_blank_values=True, strict_parsing=True) - for x in '', b'', None, 0, 0.0, [], {}, memoryview(b''): + for x in '', b'', None, memoryview(b''): self.assertEqual(urllib.parse.parse_qsl(x, **kwargs), []) self.assertRaises(ValueError, urllib.parse.parse_qsl, x, separator=1) + for x in 0, 0.0, [], {}: + with self.assertWarns(DeprecationWarning) as cm: + self.assertEqual(urllib.parse.parse_qsl(x, **kwargs), []) + self.assertEqual(cm.filename, __file__) + with self.assertWarns(DeprecationWarning) as cm: + self.assertEqual(urllib.parse.parse_qs(x, **kwargs), {}) + self.assertEqual(cm.filename, __file__) + self.assertRaises(ValueError, urllib.parse.parse_qsl, x, separator=1) def test_parse_qsl_errors(self): self.assertRaises(TypeError, urllib.parse.parse_qsl, list(b'a=b')) diff --git a/Lib/urllib/parse.py b/Lib/urllib/parse.py index a721d777c82f82..8d7631d5693ece 100644 --- a/Lib/urllib/parse.py +++ b/Lib/urllib/parse.py @@ -753,7 +753,8 @@ def parse_qs(qs, keep_blank_values=False, strict_parsing=False, parsed_result = {} pairs = parse_qsl(qs, keep_blank_values, strict_parsing, encoding=encoding, errors=errors, - max_num_fields=max_num_fields, separator=separator) + max_num_fields=max_num_fields, separator=separator, + _stacklevel=2) for name, value in pairs: if name in parsed_result: parsed_result[name].append(value) @@ -763,7 +764,7 @@ def parse_qs(qs, keep_blank_values=False, strict_parsing=False, def parse_qsl(qs, keep_blank_values=False, strict_parsing=False, - encoding='utf-8', errors='replace', max_num_fields=None, separator='&'): + encoding='utf-8', errors='replace', max_num_fields=None, separator='&', *, _stacklevel=1): """Parse a query given as a string argument. Arguments: @@ -791,7 +792,6 @@ def parse_qsl(qs, keep_blank_values=False, strict_parsing=False, Returns a list, as G-d intended. """ - if not separator or not isinstance(separator, (str, bytes)): raise ValueError("Separator must be of type string or bytes.") if isinstance(qs, str): @@ -800,12 +800,21 @@ def parse_qsl(qs, keep_blank_values=False, strict_parsing=False, eq = '=' def _unquote(s): return unquote_plus(s, encoding=encoding, errors=errors) + elif qs is None: + return [] else: - if not qs: - return [] - # Use memoryview() to reject integers and iterables, - # acceptable by the bytes constructor. - qs = bytes(memoryview(qs)) + try: + # Use memoryview() to reject integers and iterables, + # acceptable by the bytes constructor. + qs = bytes(memoryview(qs)) + except TypeError: + if not qs: + warnings.warn(f"Accepting {type(qs).__name__} objects with " + f"false value in urllib.parse.parse_qsl() is " + f"deprecated as of 3.14", + DeprecationWarning, stacklevel=_stacklevel + 1) + return [] + raise if isinstance(separator, str): separator = bytes(separator, 'ascii') eq = b'=' diff --git a/Misc/NEWS.d/next/Library/2024-03-16-13-38-27.gh-issue-116897.UDQTjp.rst b/Misc/NEWS.d/next/Library/2024-03-16-13-38-27.gh-issue-116897.UDQTjp.rst new file mode 100644 index 00000000000000..6c8e4b16f20de8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-03-16-13-38-27.gh-issue-116897.UDQTjp.rst @@ -0,0 +1,4 @@ +Accepting objects with false values (like ``0`` and ``[]``) except empty +strings, byte-like objects and ``None`` in :mod:`urllib.parse` functions +:func:`~urllib.parse.parse_qsl` and :func:`~urllib.parse.parse_qs` is now +deprecated. From bf224bd7cef5d24eaff35945ebe7ffe14df7710f Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Tue, 12 Nov 2024 19:52:30 +0000 Subject: [PATCH 157/219] GH-120423: `pathname2url()`: handle forward slashes in Windows paths (#126593) Adjust `urllib.request.pathname2url()` so that forward slashes in Windows paths are handled identically to backward slashes. --- Lib/nturl2path.py | 13 +++++++------ Lib/test/test_urllib.py | 5 +++++ .../2024-11-08-17-05-10.gh-issue-120423.7rdLVV.rst | 2 ++ 3 files changed, 14 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-11-08-17-05-10.gh-issue-120423.7rdLVV.rst diff --git a/Lib/nturl2path.py b/Lib/nturl2path.py index 2f9fec7893afd1..9ecabff21c33e1 100644 --- a/Lib/nturl2path.py +++ b/Lib/nturl2path.py @@ -44,20 +44,21 @@ def pathname2url(p): import urllib.parse # First, clean up some special forms. We are going to sacrifice # the additional information anyway - if p[:4] == '\\\\?\\': + p = p.replace('\\', '/') + if p[:4] == '//?/': p = p[4:] - if p[:4].upper() == 'UNC\\': - p = '\\\\' + p[4:] + if p[:4].upper() == 'UNC/': + p = '//' + p[4:] elif p[1:2] != ':': raise OSError('Bad path: ' + p) if not ':' in p: - # No drive specifier, just convert slashes and quote the name - return urllib.parse.quote(p.replace('\\', '/')) + # No DOS drive specified, just quote the pathname + return urllib.parse.quote(p) comp = p.split(':', maxsplit=2) if len(comp) != 2 or len(comp[0]) > 1: error = 'Bad path: ' + p raise OSError(error) drive = urllib.parse.quote(comp[0].upper()) - tail = urllib.parse.quote(comp[1].replace('\\', '/')) + tail = urllib.parse.quote(comp[1]) return '///' + drive + ':' + tail diff --git a/Lib/test/test_urllib.py b/Lib/test/test_urllib.py index 28369b21db06d4..66e948fc3a06be 100644 --- a/Lib/test/test_urllib.py +++ b/Lib/test/test_urllib.py @@ -1542,6 +1542,11 @@ def test_pathname2url_win(self): self.assertEqual(fn('\\\\some\\share\\'), '//some/share/') self.assertEqual(fn('\\\\some\\share\\a\\b.c'), '//some/share/a/b.c') self.assertEqual(fn('\\\\some\\share\\a\\b%#c\xe9'), '//some/share/a/b%25%23c%C3%A9') + # Alternate path separator + self.assertEqual(fn('C:/a/b.c'), '///C:/a/b.c') + self.assertEqual(fn('//some/share/a/b.c'), '//some/share/a/b.c') + self.assertEqual(fn('//?/C:/dir'), '///C:/dir') + self.assertEqual(fn('//?/unc/server/share/dir'), '//server/share/dir') # Round-tripping urls = ['///C:', '///folder/test/', diff --git a/Misc/NEWS.d/next/Library/2024-11-08-17-05-10.gh-issue-120423.7rdLVV.rst b/Misc/NEWS.d/next/Library/2024-11-08-17-05-10.gh-issue-120423.7rdLVV.rst new file mode 100644 index 00000000000000..b475257ceb6610 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-11-08-17-05-10.gh-issue-120423.7rdLVV.rst @@ -0,0 +1,2 @@ +Fix issue where :func:`urllib.request.pathname2url` mishandled Windows paths +with embedded forward slashes. From 5610860840aa71b186fc5639211dd268b817d65f Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Tue, 12 Nov 2024 20:53:58 +0000 Subject: [PATCH 158/219] gh-126688: Reinit import lock after fork (#126692) The PyMutex implementation supports unlocking after fork because we clear the list of waiters in parking_lot.c. This doesn't work as well for _PyRecursiveMutex because on some systems, such as SerenityOS, the thread id is not preserved across fork(). --- Include/internal/pycore_import.h | 1 + .../2024-11-11-17-02-48.gh-issue-126688.QiOXUi.rst | 2 ++ Modules/posixmodule.c | 1 + Python/import.c | 7 +++++++ 4 files changed, 11 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-11-11-17-02-48.gh-issue-126688.QiOXUi.rst diff --git a/Include/internal/pycore_import.h b/Include/internal/pycore_import.h index 290ba95e1a0ad7..318c712bdfa174 100644 --- a/Include/internal/pycore_import.h +++ b/Include/internal/pycore_import.h @@ -21,6 +21,7 @@ extern int _PyImport_SetModuleString(const char *name, PyObject* module); extern void _PyImport_AcquireLock(PyInterpreterState *interp); extern void _PyImport_ReleaseLock(PyInterpreterState *interp); +extern void _PyImport_ReInitLock(PyInterpreterState *interp); // This is used exclusively for the sys and builtins modules: extern int _PyImport_FixupBuiltin( diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-11-17-02-48.gh-issue-126688.QiOXUi.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-11-17-02-48.gh-issue-126688.QiOXUi.rst new file mode 100644 index 00000000000000..30aa5722f0ea02 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-11-17-02-48.gh-issue-126688.QiOXUi.rst @@ -0,0 +1,2 @@ +Fix a crash when calling :func:`os.fork` on some operating systems, +including SerenityOS. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 1ce2baecb8a964..da7399de86f213 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -678,6 +678,7 @@ PyOS_AfterFork_Child(void) _PyEval_StartTheWorldAll(&_PyRuntime); _PyThreadState_DeleteList(list); + _PyImport_ReInitLock(tstate->interp); _PyImport_ReleaseLock(tstate->interp); _PySignal_AfterFork(); diff --git a/Python/import.c b/Python/import.c index 29bd8bf68ff5e1..09fe95fa1fb647 100644 --- a/Python/import.c +++ b/Python/import.c @@ -122,6 +122,13 @@ _PyImport_ReleaseLock(PyInterpreterState *interp) _PyRecursiveMutex_Unlock(&IMPORT_LOCK(interp)); } +void +_PyImport_ReInitLock(PyInterpreterState *interp) +{ + // gh-126688: Thread id may change after fork() on some operating systems. + IMPORT_LOCK(interp).thread = PyThread_get_thread_ident_ex(); +} + /***************/ /* sys.modules */ From 4b00aba42e4d9440d22e399ec2122fe8601bbe54 Mon Sep 17 00:00:00 2001 From: Nice Zombies Date: Tue, 12 Nov 2024 22:18:03 +0100 Subject: [PATCH 159/219] gh-119826: Improved fallback for ntpath.abspath() on Windows (GH-119938) --- Lib/ntpath.py | 49 ++++++++++++------- Lib/test/test_ntpath.py | 3 ++ ...-06-02-11-48-19.gh-issue-119826.N1obGa.rst | 1 + 3 files changed, 35 insertions(+), 18 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-06-02-11-48-19.gh-issue-119826.N1obGa.rst diff --git a/Lib/ntpath.py b/Lib/ntpath.py index 1b1873f08b608b..5481bb8888ef59 100644 --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -553,28 +553,21 @@ def normpath(path): return prefix + sep.join(comps) -def _abspath_fallback(path): - """Return the absolute version of a path as a fallback function in case - `nt._getfullpathname` is not available or raises OSError. See bpo-31047 for - more. - - """ - - path = os.fspath(path) - if not isabs(path): - if isinstance(path, bytes): - cwd = os.getcwdb() - else: - cwd = os.getcwd() - path = join(cwd, path) - return normpath(path) - # Return an absolute path. try: from nt import _getfullpathname except ImportError: # not running on Windows - mock up something sensible - abspath = _abspath_fallback + def abspath(path): + """Return the absolute version of a path.""" + path = os.fspath(path) + if not isabs(path): + if isinstance(path, bytes): + cwd = os.getcwdb() + else: + cwd = os.getcwd() + path = join(cwd, path) + return normpath(path) else: # use native Windows method on Windows def abspath(path): @@ -582,7 +575,27 @@ def abspath(path): try: return _getfullpathname(normpath(path)) except (OSError, ValueError): - return _abspath_fallback(path) + # See gh-75230, handle outside for cleaner traceback + pass + path = os.fspath(path) + if not isabs(path): + if isinstance(path, bytes): + sep = b'\\' + getcwd = os.getcwdb + else: + sep = '\\' + getcwd = os.getcwd + drive, root, path = splitroot(path) + # Either drive or root can be nonempty, but not both. + if drive or root: + try: + path = join(_getfullpathname(drive + root), path) + except (OSError, ValueError): + # Drive "\0:" cannot exist; use the root directory. + path = drive + sep + path + else: + path = join(getcwd(), path) + return normpath(path) try: from nt import _findfirstfile, _getfinalpathname, readlink as _nt_readlink diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py index 64cbfaaaaa0690..4f59184dfcfdc7 100644 --- a/Lib/test/test_ntpath.py +++ b/Lib/test/test_ntpath.py @@ -806,6 +806,9 @@ def test_abspath(self): tester('ntpath.abspath("C:\\spam. . .")', "C:\\spam") tester('ntpath.abspath("C:/nul")', "\\\\.\\nul") tester('ntpath.abspath("C:\\nul")', "\\\\.\\nul") + self.assertTrue(ntpath.isabs(ntpath.abspath("C:spam"))) + self.assertEqual(ntpath.abspath("C:\x00"), ntpath.join(ntpath.abspath("C:"), "\x00")) + self.assertEqual(ntpath.abspath("\x00:spam"), "\x00:\\spam") tester('ntpath.abspath("//..")', "\\\\") tester('ntpath.abspath("//../")', "\\\\..\\") tester('ntpath.abspath("//../..")', "\\\\..\\") diff --git a/Misc/NEWS.d/next/Library/2024-06-02-11-48-19.gh-issue-119826.N1obGa.rst b/Misc/NEWS.d/next/Library/2024-06-02-11-48-19.gh-issue-119826.N1obGa.rst new file mode 100644 index 00000000000000..6901e7475dd082 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-06-02-11-48-19.gh-issue-119826.N1obGa.rst @@ -0,0 +1 @@ +Always return an absolute path for :func:`os.path.abspath` on Windows. From 8cc6e5c8751139e86b2a9fa5228795e6c5caaff9 Mon Sep 17 00:00:00 2001 From: Yuxuan Zhang Date: Tue, 12 Nov 2024 16:23:40 -0500 Subject: [PATCH 160/219] gh-126757: fix minor typo (GH-126758) --- Python/bltinmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index a3f41190261a05..85ebd5b00cc18b 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -3336,7 +3336,7 @@ _PyBuiltin_Init(PyInterpreterState *interp) SETBUILTIN("False", Py_False); SETBUILTIN("True", Py_True); SETBUILTIN("bool", &PyBool_Type); - SETBUILTIN("memoryview", &PyMemoryView_Type); + SETBUILTIN("memoryview", &PyMemoryView_Type); SETBUILTIN("bytearray", &PyByteArray_Type); SETBUILTIN("bytes", &PyBytes_Type); SETBUILTIN("classmethod", &PyClassMethod_Type); From 2e39d77ddeb51505d65fd54ccfcd72615c6b1927 Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Wed, 13 Nov 2024 05:24:33 +0100 Subject: [PATCH 161/219] bpo-46128: Strip IsolatedAsyncioTestCase frames from reported stacktraces (#30196) * Strip IsolatedAsyncioTestCase frames from reported stacktraces * Update Misc/NEWS.d/next/Library/2021-12-19-10-47-24.bpo-46128.Qv3EK1.rst Co-authored-by: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com> --------- Co-authored-by: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com> Co-authored-by: Ezio Melotti Co-authored-by: Hugo van Kemenade --- Lib/unittest/async_case.py | 1 + .../next/Library/2021-12-19-10-47-24.bpo-46128.Qv3EK1.rst | 2 ++ 2 files changed, 3 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2021-12-19-10-47-24.bpo-46128.Qv3EK1.rst diff --git a/Lib/unittest/async_case.py b/Lib/unittest/async_case.py index bd06eb3207697a..6000af1cef0a78 100644 --- a/Lib/unittest/async_case.py +++ b/Lib/unittest/async_case.py @@ -5,6 +5,7 @@ from .case import TestCase +__unittest = True class IsolatedAsyncioTestCase(TestCase): # Names intentionally have a long prefix diff --git a/Misc/NEWS.d/next/Library/2021-12-19-10-47-24.bpo-46128.Qv3EK1.rst b/Misc/NEWS.d/next/Library/2021-12-19-10-47-24.bpo-46128.Qv3EK1.rst new file mode 100644 index 00000000000000..7d11d20d94e8a3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2021-12-19-10-47-24.bpo-46128.Qv3EK1.rst @@ -0,0 +1,2 @@ +Strip :class:`unittest.IsolatedAsyncioTestCase` stack frames from reported +stacktraces. From 1e40c5ba47780ddd91868abb3aa064f5ba3015e4 Mon Sep 17 00:00:00 2001 From: Red4Ru <39802734+Red4Ru@users.noreply.github.com> Date: Wed, 13 Nov 2024 11:20:38 +0300 Subject: [PATCH 162/219] gh-104745: Limit starting a patcher more than once without stopping it (#126649) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, this would cause an `AttributeError` if the patch stopped more than once after this, and would also disrupt the original patched object. --------- Co-authored-by: Peter Bierma Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Lib/test/test_unittest/testmock/testpatch.py | 52 ++++++++++++++++++- Lib/unittest/mock.py | 9 ++++ ...-11-10-18-14-51.gh-issue-104745.zAa5Ke.rst | 3 ++ 3 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-11-10-18-14-51.gh-issue-104745.zAa5Ke.rst diff --git a/Lib/test/test_unittest/testmock/testpatch.py b/Lib/test/test_unittest/testmock/testpatch.py index f26e74ce0bc1ba..037c021e6eafcf 100644 --- a/Lib/test/test_unittest/testmock/testpatch.py +++ b/Lib/test/test_unittest/testmock/testpatch.py @@ -745,6 +745,54 @@ def test_stop_idempotent(self): self.assertIsNone(patcher.stop()) + def test_exit_idempotent(self): + patcher = patch(foo_name, 'bar', 3) + with patcher: + patcher.stop() + + + def test_second_start_failure(self): + patcher = patch(foo_name, 'bar', 3) + patcher.start() + try: + self.assertRaises(RuntimeError, patcher.start) + finally: + patcher.stop() + + + def test_second_enter_failure(self): + patcher = patch(foo_name, 'bar', 3) + with patcher: + self.assertRaises(RuntimeError, patcher.start) + + + def test_second_start_after_stop(self): + patcher = patch(foo_name, 'bar', 3) + patcher.start() + patcher.stop() + patcher.start() + patcher.stop() + + + def test_property_setters(self): + mock_object = Mock() + mock_bar = mock_object.bar + patcher = patch.object(mock_object, 'bar', 'x') + with patcher: + self.assertEqual(patcher.is_local, False) + self.assertIs(patcher.target, mock_object) + self.assertEqual(patcher.temp_original, mock_bar) + patcher.is_local = True + patcher.target = mock_bar + patcher.temp_original = mock_object + self.assertEqual(patcher.is_local, True) + self.assertIs(patcher.target, mock_bar) + self.assertEqual(patcher.temp_original, mock_object) + # if changes are left intact, they may lead to disruption as shown below (it might be what someone needs though) + self.assertEqual(mock_bar.bar, mock_object) + self.assertEqual(mock_object.bar, 'x') + + def test_patchobject_start_stop(self): original = something patcher = patch.object(PTModule, 'something', 'foo') @@ -1098,7 +1146,7 @@ def test_new_callable_patch(self): self.assertIsNot(m1, m2) for mock in m1, m2: - self.assertNotCallable(m1) + self.assertNotCallable(mock) def test_new_callable_patch_object(self): @@ -1111,7 +1159,7 @@ def test_new_callable_patch_object(self): self.assertIsNot(m1, m2) for mock in m1, m2: - self.assertNotCallable(m1) + self.assertNotCallable(mock) def test_new_callable_keyword_arguments(self): diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index 21ca061a77c26f..55cb4b1f6aff90 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -1360,6 +1360,7 @@ def __init__( self.autospec = autospec self.kwargs = kwargs self.additional_patchers = [] + self.is_started = False def copy(self): @@ -1472,6 +1473,9 @@ def get_original(self): def __enter__(self): """Perform the patch.""" + if self.is_started: + raise RuntimeError("Patch is already started") + new, spec, spec_set = self.new, self.spec, self.spec_set autospec, kwargs = self.autospec, self.kwargs new_callable = self.new_callable @@ -1603,6 +1607,7 @@ def __enter__(self): self.temp_original = original self.is_local = local self._exit_stack = contextlib.ExitStack() + self.is_started = True try: setattr(self.target, self.attribute, new_attr) if self.attribute_name is not None: @@ -1622,6 +1627,9 @@ def __enter__(self): def __exit__(self, *exc_info): """Undo the patch.""" + if not self.is_started: + return + if self.is_local and self.temp_original is not DEFAULT: setattr(self.target, self.attribute, self.temp_original) else: @@ -1638,6 +1646,7 @@ def __exit__(self, *exc_info): del self.target exit_stack = self._exit_stack del self._exit_stack + self.is_started = False return exit_stack.__exit__(*exc_info) diff --git a/Misc/NEWS.d/next/Library/2024-11-10-18-14-51.gh-issue-104745.zAa5Ke.rst b/Misc/NEWS.d/next/Library/2024-11-10-18-14-51.gh-issue-104745.zAa5Ke.rst new file mode 100644 index 00000000000000..c83a10769820cf --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-11-10-18-14-51.gh-issue-104745.zAa5Ke.rst @@ -0,0 +1,3 @@ +Limit starting a patcher (from :func:`unittest.mock.patch` or +:func:`unittest.mock.patch.object`) more than +once without stopping it From ba088c8f9cf7163b0f28c507cb1343befe21997e Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 13 Nov 2024 10:25:10 +0100 Subject: [PATCH 163/219] gh-71936: Fix race condition in multiprocessing.Pool (GH-124973) * gh-71936: Fix race condition in multiprocessing.Pool Proxes of shared objects register a Finalizer in BaseProxy._incref(), and it will call BaseProxy._decref() when it is GCed. This may cause a race condition with Pool(maxtasksperchild=None) on Windows. A connection would be closed and raised TypeError when a GC occurs between _ConnectionBase._check_writable() and _ConnectionBase._send_bytes() in _ConnectionBase.send() in the second or later task, and a new object is allocated that shares the id() of a previously deleted one. Instead of using the id() of the token (or the proxy), use a unique, non-reusable number. Co-Authored-By: Akinori Hattori --- Lib/multiprocessing/managers.py | 33 +++++++++++-------- Misc/ACKS | 1 + ...2-10-15-10-18-20.gh-issue-71936.MzJjc_.rst | 1 + 3 files changed, 22 insertions(+), 13 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-10-15-10-18-20.gh-issue-71936.MzJjc_.rst diff --git a/Lib/multiprocessing/managers.py b/Lib/multiprocessing/managers.py index a5d2f53613952e..040f4674d735c0 100644 --- a/Lib/multiprocessing/managers.py +++ b/Lib/multiprocessing/managers.py @@ -759,22 +759,29 @@ class BaseProxy(object): _address_to_local = {} _mutex = util.ForkAwareThreadLock() + # Each instance gets a `_serial` number. Unlike `id(...)`, this number + # is never reused. + _next_serial = 1 + def __init__(self, token, serializer, manager=None, authkey=None, exposed=None, incref=True, manager_owned=False): with BaseProxy._mutex: - tls_idset = BaseProxy._address_to_local.get(token.address, None) - if tls_idset is None: - tls_idset = util.ForkAwareLocal(), ProcessLocalSet() - BaseProxy._address_to_local[token.address] = tls_idset + tls_serials = BaseProxy._address_to_local.get(token.address, None) + if tls_serials is None: + tls_serials = util.ForkAwareLocal(), ProcessLocalSet() + BaseProxy._address_to_local[token.address] = tls_serials + + self._serial = BaseProxy._next_serial + BaseProxy._next_serial += 1 # self._tls is used to record the connection used by this # thread to communicate with the manager at token.address - self._tls = tls_idset[0] + self._tls = tls_serials[0] - # self._idset is used to record the identities of all shared - # objects for which the current process owns references and + # self._all_serials is a set used to record the identities of all + # shared objects for which the current process owns references and # which are in the manager at token.address - self._idset = tls_idset[1] + self._all_serials = tls_serials[1] self._token = token self._id = self._token.id @@ -857,20 +864,20 @@ def _incref(self): dispatch(conn, None, 'incref', (self._id,)) util.debug('INCREF %r', self._token.id) - self._idset.add(self._id) + self._all_serials.add(self._serial) state = self._manager and self._manager._state self._close = util.Finalize( self, BaseProxy._decref, - args=(self._token, self._authkey, state, - self._tls, self._idset, self._Client), + args=(self._token, self._serial, self._authkey, state, + self._tls, self._all_serials, self._Client), exitpriority=10 ) @staticmethod - def _decref(token, authkey, state, tls, idset, _Client): - idset.discard(token.id) + def _decref(token, serial, authkey, state, tls, idset, _Client): + idset.discard(serial) # check whether manager is still alive if state is None or state.value == State.STARTED: diff --git a/Misc/ACKS b/Misc/ACKS index dce322fc867743..08cd293eac3835 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -733,6 +733,7 @@ Larry Hastings Tim Hatch Zac Hatfield-Dodds Shane Hathaway +Akinori Hattori Michael Haubenwallner Janko Hauser Flavian Hautbois diff --git a/Misc/NEWS.d/next/Library/2022-10-15-10-18-20.gh-issue-71936.MzJjc_.rst b/Misc/NEWS.d/next/Library/2022-10-15-10-18-20.gh-issue-71936.MzJjc_.rst new file mode 100644 index 00000000000000..a0959cc086fa9e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-10-15-10-18-20.gh-issue-71936.MzJjc_.rst @@ -0,0 +1 @@ +Fix a race condition in :class:`multiprocessing.pool.Pool`. From a12690ef49e8fc8a3af4c5f1757eb3caffb35e03 Mon Sep 17 00:00:00 2001 From: Ritvik Pasham Date: Wed, 13 Nov 2024 06:51:01 -0500 Subject: [PATCH 164/219] gh-126341: add release check to `__iter__` method of `memoryview` (#126759) Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> Co-authored-by: Peter Bierma Co-authored-by: Victor Stinner Co-authored-by: sobolevn --- Lib/test/test_buffer.py | 2 ++ .../2024-11-12-19-24-00.gh-issue-126341.5SdAe1.rst | 1 + Objects/memoryobject.c | 1 + 3 files changed, 4 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-11-12-19-24-00.gh-issue-126341.5SdAe1.rst diff --git a/Lib/test/test_buffer.py b/Lib/test/test_buffer.py index ae938d12c9401b..cb38a69e390f3a 100644 --- a/Lib/test/test_buffer.py +++ b/Lib/test/test_buffer.py @@ -3910,6 +3910,8 @@ def test_memoryview_check_released(self): self.assertRaises(ValueError, memoryview, m) # memoryview.cast() self.assertRaises(ValueError, m.cast, 'c') + # memoryview.__iter__() + self.assertRaises(ValueError, m.__iter__) # getbuffer() self.assertRaises(ValueError, ndarray, m) # memoryview.tolist() diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-12-19-24-00.gh-issue-126341.5SdAe1.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-12-19-24-00.gh-issue-126341.5SdAe1.rst new file mode 100644 index 00000000000000..c2436d2ebf4d09 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-12-19-24-00.gh-issue-126341.5SdAe1.rst @@ -0,0 +1 @@ +Now :exc:`ValueError` is raised instead of :exc:`SystemError` when trying to iterate over a released :class:`memoryview` object. diff --git a/Objects/memoryobject.c b/Objects/memoryobject.c index d4672e8198cb24..25634f997ac66b 100644 --- a/Objects/memoryobject.c +++ b/Objects/memoryobject.c @@ -3356,6 +3356,7 @@ memory_iter(PyObject *seq) PyErr_BadInternalCall(); return NULL; } + CHECK_RELEASED(seq); PyMemoryViewObject *obj = (PyMemoryViewObject *)seq; int ndims = obj->view.ndim; if (ndims == 0) { From 29b5323c4567dc7772e1d30a7ba1cbad52fe10a9 Mon Sep 17 00:00:00 2001 From: Taneli Hukkinen <3275109+hukkin@users.noreply.github.com> Date: Wed, 13 Nov 2024 14:52:16 +0200 Subject: [PATCH 165/219] gh-126175: Add attributes to TOMLDecodeError. Deprecate free-form `__init__` args (GH-126428) --- Doc/library/tomllib.rst | 31 +++- Lib/test/test_tomllib/test_error.py | 34 +++- Lib/tomllib/_parser.py | 148 ++++++++++++------ ...-11-05-09-54-49.gh-issue-126175.spnjJr.rst | 2 + 4 files changed, 163 insertions(+), 52 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-11-05-09-54-49.gh-issue-126175.spnjJr.rst diff --git a/Doc/library/tomllib.rst b/Doc/library/tomllib.rst index 521a7a17fb3e8b..4b88b2e29e7822 100644 --- a/Doc/library/tomllib.rst +++ b/Doc/library/tomllib.rst @@ -60,9 +60,36 @@ This module defines the following functions: The following exceptions are available: -.. exception:: TOMLDecodeError +.. exception:: TOMLDecodeError(msg, doc, pos) - Subclass of :exc:`ValueError`. + Subclass of :exc:`ValueError` with the following additional attributes: + + .. attribute:: msg + + The unformatted error message. + + .. attribute:: doc + + The TOML document being parsed. + + .. attribute:: pos + + The index of *doc* where parsing failed. + + .. attribute:: lineno + + The line corresponding to *pos*. + + .. attribute:: colno + + The column corresponding to *pos*. + + .. versionchanged:: next + Added the *msg*, *doc* and *pos* parameters. + Added the :attr:`msg`, :attr:`doc`, :attr:`pos`, :attr:`lineno` and :attr:`colno` attributes. + + .. deprecated:: next + Passing free-form positional arguments is deprecated. Examples diff --git a/Lib/test/test_tomllib/test_error.py b/Lib/test/test_tomllib/test_error.py index d2ef59a29ca350..3a8587492859ca 100644 --- a/Lib/test/test_tomllib/test_error.py +++ b/Lib/test/test_tomllib/test_error.py @@ -49,7 +49,9 @@ def test_type_error(self): self.assertEqual(str(exc_info.exception), "Expected str object, not 'bool'") def test_module_name(self): - self.assertEqual(tomllib.TOMLDecodeError().__module__, tomllib.__name__) + self.assertEqual( + tomllib.TOMLDecodeError("", "", 0).__module__, tomllib.__name__ + ) def test_invalid_parse_float(self): def dict_returner(s: str) -> dict: @@ -64,3 +66,33 @@ def list_returner(s: str) -> list: self.assertEqual( str(exc_info.exception), "parse_float must not return dicts or lists" ) + + def test_deprecated_tomldecodeerror(self): + for args in [ + (), + ("err msg",), + (None,), + (None, "doc"), + ("err msg", None), + (None, "doc", None), + ("err msg", "doc", None), + ("one", "two", "three", "four"), + ("one", "two", 3, "four", "five"), + ]: + with self.assertWarns(DeprecationWarning): + e = tomllib.TOMLDecodeError(*args) # type: ignore[arg-type] + self.assertEqual(e.args, args) + + def test_tomldecodeerror(self): + msg = "error parsing" + doc = "v=1\n[table]\nv='val'" + pos = 13 + formatted_msg = "error parsing (at line 3, column 2)" + e = tomllib.TOMLDecodeError(msg, doc, pos) + self.assertEqual(e.args, (formatted_msg,)) + self.assertEqual(str(e), formatted_msg) + self.assertEqual(e.msg, msg) + self.assertEqual(e.doc, doc) + self.assertEqual(e.pos, pos) + self.assertEqual(e.lineno, 3) + self.assertEqual(e.colno, 2) diff --git a/Lib/tomllib/_parser.py b/Lib/tomllib/_parser.py index 5671326646ca5a..4d208bcfb4a9a6 100644 --- a/Lib/tomllib/_parser.py +++ b/Lib/tomllib/_parser.py @@ -8,6 +8,7 @@ import string from types import MappingProxyType from typing import Any, BinaryIO, NamedTuple +import warnings from ._re import ( RE_DATETIME, @@ -50,8 +51,68 @@ ) +class DEPRECATED_DEFAULT: + """Sentinel to be used as default arg during deprecation + period of TOMLDecodeError's free-form arguments.""" + + class TOMLDecodeError(ValueError): - """An error raised if a document is not valid TOML.""" + """An error raised if a document is not valid TOML. + + Adds the following attributes to ValueError: + msg: The unformatted error message + doc: The TOML document being parsed + pos: The index of doc where parsing failed + lineno: The line corresponding to pos + colno: The column corresponding to pos + """ + + def __init__( + self, + msg: str = DEPRECATED_DEFAULT, # type: ignore[assignment] + doc: str = DEPRECATED_DEFAULT, # type: ignore[assignment] + pos: Pos = DEPRECATED_DEFAULT, # type: ignore[assignment] + *args: Any, + ): + if ( + args + or not isinstance(msg, str) + or not isinstance(doc, str) + or not isinstance(pos, int) + ): + warnings.warn( + "Free-form arguments for TOMLDecodeError are deprecated. " + "Please set 'msg' (str), 'doc' (str) and 'pos' (int) arguments only.", + DeprecationWarning, + stacklevel=2, + ) + if pos is not DEPRECATED_DEFAULT: # type: ignore[comparison-overlap] + args = pos, *args + if doc is not DEPRECATED_DEFAULT: # type: ignore[comparison-overlap] + args = doc, *args + if msg is not DEPRECATED_DEFAULT: # type: ignore[comparison-overlap] + args = msg, *args + ValueError.__init__(self, *args) + return + + lineno = doc.count("\n", 0, pos) + 1 + if lineno == 1: + colno = pos + 1 + else: + colno = pos - doc.rindex("\n", 0, pos) + + if pos >= len(doc): + coord_repr = "end of document" + else: + coord_repr = f"line {lineno}, column {colno}" + errmsg = f"{msg} (at {coord_repr})" + ValueError.__init__(self, errmsg) + + self.msg = msg + self.doc = doc + self.pos = pos + self.lineno = lineno + self.colno = colno def load(fp: BinaryIO, /, *, parse_float: ParseFloat = float) -> dict[str, Any]: @@ -118,7 +179,7 @@ def loads(s: str, /, *, parse_float: ParseFloat = float) -> dict[str, Any]: # n pos, header = create_dict_rule(src, pos, out) pos = skip_chars(src, pos, TOML_WS) elif char != "#": - raise suffixed_err(src, pos, "Invalid statement") + raise TOMLDecodeError("Invalid statement", src, pos) # 3. Skip comment pos = skip_comment(src, pos) @@ -129,8 +190,8 @@ def loads(s: str, /, *, parse_float: ParseFloat = float) -> dict[str, Any]: # n except IndexError: break if char != "\n": - raise suffixed_err( - src, pos, "Expected newline or end of document after a statement" + raise TOMLDecodeError( + "Expected newline or end of document after a statement", src, pos ) pos += 1 @@ -256,12 +317,12 @@ def skip_until( except ValueError: new_pos = len(src) if error_on_eof: - raise suffixed_err(src, new_pos, f"Expected {expect!r}") from None + raise TOMLDecodeError(f"Expected {expect!r}", src, new_pos) from None if not error_on.isdisjoint(src[pos:new_pos]): while src[pos] not in error_on: pos += 1 - raise suffixed_err(src, pos, f"Found invalid character {src[pos]!r}") + raise TOMLDecodeError(f"Found invalid character {src[pos]!r}", src, pos) return new_pos @@ -292,15 +353,17 @@ def create_dict_rule(src: str, pos: Pos, out: Output) -> tuple[Pos, Key]: pos, key = parse_key(src, pos) if out.flags.is_(key, Flags.EXPLICIT_NEST) or out.flags.is_(key, Flags.FROZEN): - raise suffixed_err(src, pos, f"Cannot declare {key} twice") + raise TOMLDecodeError(f"Cannot declare {key} twice", src, pos) out.flags.set(key, Flags.EXPLICIT_NEST, recursive=False) try: out.data.get_or_create_nest(key) except KeyError: - raise suffixed_err(src, pos, "Cannot overwrite a value") from None + raise TOMLDecodeError("Cannot overwrite a value", src, pos) from None if not src.startswith("]", pos): - raise suffixed_err(src, pos, "Expected ']' at the end of a table declaration") + raise TOMLDecodeError( + "Expected ']' at the end of a table declaration", src, pos + ) return pos + 1, key @@ -310,7 +373,7 @@ def create_list_rule(src: str, pos: Pos, out: Output) -> tuple[Pos, Key]: pos, key = parse_key(src, pos) if out.flags.is_(key, Flags.FROZEN): - raise suffixed_err(src, pos, f"Cannot mutate immutable namespace {key}") + raise TOMLDecodeError(f"Cannot mutate immutable namespace {key}", src, pos) # Free the namespace now that it points to another empty list item... out.flags.unset_all(key) # ...but this key precisely is still prohibited from table declaration @@ -318,10 +381,12 @@ def create_list_rule(src: str, pos: Pos, out: Output) -> tuple[Pos, Key]: try: out.data.append_nest_to_list(key) except KeyError: - raise suffixed_err(src, pos, "Cannot overwrite a value") from None + raise TOMLDecodeError("Cannot overwrite a value", src, pos) from None if not src.startswith("]]", pos): - raise suffixed_err(src, pos, "Expected ']]' at the end of an array declaration") + raise TOMLDecodeError( + "Expected ']]' at the end of an array declaration", src, pos + ) return pos + 2, key @@ -336,22 +401,22 @@ def key_value_rule( for cont_key in relative_path_cont_keys: # Check that dotted key syntax does not redefine an existing table if out.flags.is_(cont_key, Flags.EXPLICIT_NEST): - raise suffixed_err(src, pos, f"Cannot redefine namespace {cont_key}") + raise TOMLDecodeError(f"Cannot redefine namespace {cont_key}", src, pos) # Containers in the relative path can't be opened with the table syntax or # dotted key/value syntax in following table sections. out.flags.add_pending(cont_key, Flags.EXPLICIT_NEST) if out.flags.is_(abs_key_parent, Flags.FROZEN): - raise suffixed_err( - src, pos, f"Cannot mutate immutable namespace {abs_key_parent}" + raise TOMLDecodeError( + f"Cannot mutate immutable namespace {abs_key_parent}", src, pos ) try: nest = out.data.get_or_create_nest(abs_key_parent) except KeyError: - raise suffixed_err(src, pos, "Cannot overwrite a value") from None + raise TOMLDecodeError("Cannot overwrite a value", src, pos) from None if key_stem in nest: - raise suffixed_err(src, pos, "Cannot overwrite a value") + raise TOMLDecodeError("Cannot overwrite a value", src, pos) # Mark inline table and array namespaces recursively immutable if isinstance(value, (dict, list)): out.flags.set(header + key, Flags.FROZEN, recursive=True) @@ -368,7 +433,7 @@ def parse_key_value_pair( except IndexError: char = None if char != "=": - raise suffixed_err(src, pos, "Expected '=' after a key in a key/value pair") + raise TOMLDecodeError("Expected '=' after a key in a key/value pair", src, pos) pos += 1 pos = skip_chars(src, pos, TOML_WS) pos, value = parse_value(src, pos, parse_float) @@ -406,7 +471,7 @@ def parse_key_part(src: str, pos: Pos) -> tuple[Pos, str]: return parse_literal_str(src, pos) if char == '"': return parse_one_line_basic_str(src, pos) - raise suffixed_err(src, pos, "Invalid initial character for a key part") + raise TOMLDecodeError("Invalid initial character for a key part", src, pos) def parse_one_line_basic_str(src: str, pos: Pos) -> tuple[Pos, str]: @@ -430,7 +495,7 @@ def parse_array(src: str, pos: Pos, parse_float: ParseFloat) -> tuple[Pos, list] if c == "]": return pos + 1, array if c != ",": - raise suffixed_err(src, pos, "Unclosed array") + raise TOMLDecodeError("Unclosed array", src, pos) pos += 1 pos = skip_comments_and_array_ws(src, pos) @@ -450,20 +515,20 @@ def parse_inline_table(src: str, pos: Pos, parse_float: ParseFloat) -> tuple[Pos pos, key, value = parse_key_value_pair(src, pos, parse_float) key_parent, key_stem = key[:-1], key[-1] if flags.is_(key, Flags.FROZEN): - raise suffixed_err(src, pos, f"Cannot mutate immutable namespace {key}") + raise TOMLDecodeError(f"Cannot mutate immutable namespace {key}", src, pos) try: nest = nested_dict.get_or_create_nest(key_parent, access_lists=False) except KeyError: - raise suffixed_err(src, pos, "Cannot overwrite a value") from None + raise TOMLDecodeError("Cannot overwrite a value", src, pos) from None if key_stem in nest: - raise suffixed_err(src, pos, f"Duplicate inline table key {key_stem!r}") + raise TOMLDecodeError(f"Duplicate inline table key {key_stem!r}", src, pos) nest[key_stem] = value pos = skip_chars(src, pos, TOML_WS) c = src[pos : pos + 1] if c == "}": return pos + 1, nested_dict.dict if c != ",": - raise suffixed_err(src, pos, "Unclosed inline table") + raise TOMLDecodeError("Unclosed inline table", src, pos) if isinstance(value, (dict, list)): flags.set(key, Flags.FROZEN, recursive=True) pos += 1 @@ -485,7 +550,7 @@ def parse_basic_str_escape( except IndexError: return pos, "" if char != "\n": - raise suffixed_err(src, pos, "Unescaped '\\' in a string") + raise TOMLDecodeError("Unescaped '\\' in a string", src, pos) pos += 1 pos = skip_chars(src, pos, TOML_WS_AND_NEWLINE) return pos, "" @@ -496,7 +561,7 @@ def parse_basic_str_escape( try: return pos, BASIC_STR_ESCAPE_REPLACEMENTS[escape_id] except KeyError: - raise suffixed_err(src, pos, "Unescaped '\\' in a string") from None + raise TOMLDecodeError("Unescaped '\\' in a string", src, pos) from None def parse_basic_str_escape_multiline(src: str, pos: Pos) -> tuple[Pos, str]: @@ -506,11 +571,13 @@ def parse_basic_str_escape_multiline(src: str, pos: Pos) -> tuple[Pos, str]: def parse_hex_char(src: str, pos: Pos, hex_len: int) -> tuple[Pos, str]: hex_str = src[pos : pos + hex_len] if len(hex_str) != hex_len or not HEXDIGIT_CHARS.issuperset(hex_str): - raise suffixed_err(src, pos, "Invalid hex value") + raise TOMLDecodeError("Invalid hex value", src, pos) pos += hex_len hex_int = int(hex_str, 16) if not is_unicode_scalar_value(hex_int): - raise suffixed_err(src, pos, "Escaped character is not a Unicode scalar value") + raise TOMLDecodeError( + "Escaped character is not a Unicode scalar value", src, pos + ) return pos, chr(hex_int) @@ -567,7 +634,7 @@ def parse_basic_str(src: str, pos: Pos, *, multiline: bool) -> tuple[Pos, str]: try: char = src[pos] except IndexError: - raise suffixed_err(src, pos, "Unterminated string") from None + raise TOMLDecodeError("Unterminated string", src, pos) from None if char == '"': if not multiline: return pos + 1, result + src[start_pos:pos] @@ -582,7 +649,7 @@ def parse_basic_str(src: str, pos: Pos, *, multiline: bool) -> tuple[Pos, str]: start_pos = pos continue if char in error_on: - raise suffixed_err(src, pos, f"Illegal character {char!r}") + raise TOMLDecodeError(f"Illegal character {char!r}", src, pos) pos += 1 @@ -630,7 +697,7 @@ def parse_value( # noqa: C901 try: datetime_obj = match_to_datetime(datetime_match) except ValueError as e: - raise suffixed_err(src, pos, "Invalid date or datetime") from e + raise TOMLDecodeError("Invalid date or datetime", src, pos) from e return datetime_match.end(), datetime_obj localtime_match = RE_LOCALTIME.match(src, pos) if localtime_match: @@ -651,24 +718,7 @@ def parse_value( # noqa: C901 if first_four in {"-inf", "+inf", "-nan", "+nan"}: return pos + 4, parse_float(first_four) - raise suffixed_err(src, pos, "Invalid value") - - -def suffixed_err(src: str, pos: Pos, msg: str) -> TOMLDecodeError: - """Return a `TOMLDecodeError` where error message is suffixed with - coordinates in source.""" - - def coord_repr(src: str, pos: Pos) -> str: - if pos >= len(src): - return "end of document" - line = src.count("\n", 0, pos) + 1 - if line == 1: - column = pos + 1 - else: - column = pos - src.rindex("\n", 0, pos) - return f"line {line}, column {column}" - - return TOMLDecodeError(f"{msg} (at {coord_repr(src, pos)})") + raise TOMLDecodeError("Invalid value", src, pos) def is_unicode_scalar_value(codepoint: int) -> bool: diff --git a/Misc/NEWS.d/next/Library/2024-11-05-09-54-49.gh-issue-126175.spnjJr.rst b/Misc/NEWS.d/next/Library/2024-11-05-09-54-49.gh-issue-126175.spnjJr.rst new file mode 100644 index 00000000000000..de7ce88c8d0f28 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-11-05-09-54-49.gh-issue-126175.spnjJr.rst @@ -0,0 +1,2 @@ +Add ``msg``, ``doc``, ``pos``, ``lineno`` and ``colno`` attributes to :exc:`tomllib.TOMLDecodeError`. +Deprecate instantiating with free-form arguments. From d00878b06a05ea04790813dba70b09cc1d11bf45 Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Wed, 13 Nov 2024 08:27:16 -0500 Subject: [PATCH 166/219] gh-123619: Add an unstable C API function for enabling deferred reference counting (GH-123635) Co-authored-by: Sam Gross --- Doc/c-api/object.rst | 24 ++++++++++ Doc/whatsnew/3.14.rst | 3 ++ Include/cpython/object.h | 7 +++ Lib/test/test_capi/test_object.py | 46 +++++++++++++++++++ ...-09-03-13-33-33.gh-issue-123619.HhgUUI.rst | 2 + Modules/_testcapi/object.c | 9 +++- Modules/_testinternalcapi.c | 9 ++++ Objects/object.c | 29 ++++++++++++ 8 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/C_API/2024-09-03-13-33-33.gh-issue-123619.HhgUUI.rst diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst index 630114a4339110..1e1cf6e6bfd7e9 100644 --- a/Doc/c-api/object.rst +++ b/Doc/c-api/object.rst @@ -575,3 +575,27 @@ Object Protocol has the :c:macro:`Py_TPFLAGS_MANAGED_DICT` flag set. .. versionadded:: 3.13 + +.. c:function:: int PyUnstable_Object_EnableDeferredRefcount(PyObject *obj) + + Enable `deferred reference counting `_ on *obj*, + if supported by the runtime. In the :term:`free-threaded ` build, + this allows the interpreter to avoid reference count adjustments to *obj*, + which may improve multi-threaded performance. The tradeoff is + that *obj* will only be deallocated by the tracing garbage collector. + + This function returns ``1`` if deferred reference counting is enabled on *obj* + (including when it was enabled before the call), + and ``0`` if deferred reference counting is not supported or if the hint was + ignored by the runtime. This function is thread-safe, and cannot fail. + + This function does nothing on builds with the :term:`GIL` enabled, which do + not support deferred reference counting. This also does nothing if *obj* is not + an object tracked by the garbage collector (see :func:`gc.is_tracked` and + :c:func:`PyObject_GC_IsTracked`). + + This function is intended to be used soon after *obj* is created, + by the code that creates it. + + .. versionadded:: next + diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index a98fe3f468b685..31754fb55fcf02 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -890,6 +890,9 @@ New features * Add :c:func:`PyType_Freeze` function to make a type immutable. (Contributed by Victor Stinner in :gh:`121654`.) +* Add :c:func:`PyUnstable_Object_EnableDeferredRefcount` for enabling + deferred reference counting, as outlined in :pep:`703`. + Porting to Python 3.14 ---------------------- diff --git a/Include/cpython/object.h b/Include/cpython/object.h index f0f61796cd3ec8..e4797029da431e 100644 --- a/Include/cpython/object.h +++ b/Include/cpython/object.h @@ -527,3 +527,10 @@ typedef enum { typedef int (*PyRefTracer)(PyObject *, PyRefTracerEvent event, void *); PyAPI_FUNC(int) PyRefTracer_SetTracer(PyRefTracer tracer, void *data); PyAPI_FUNC(PyRefTracer) PyRefTracer_GetTracer(void**); + +/* Enable PEP-703 deferred reference counting on the object. + * + * Returns 1 if deferred reference counting was successfully enabled, and + * 0 if the runtime ignored it. This function cannot fail. + */ +PyAPI_FUNC(int) PyUnstable_Object_EnableDeferredRefcount(PyObject *); diff --git a/Lib/test/test_capi/test_object.py b/Lib/test/test_capi/test_object.py index cc9c9b688f00e2..a38b203ed12fa2 100644 --- a/Lib/test/test_capi/test_object.py +++ b/Lib/test/test_capi/test_object.py @@ -1,10 +1,13 @@ import enum import unittest +from test import support from test.support import import_helper from test.support import os_helper +from test.support import threading_helper _testlimitedcapi = import_helper.import_module('_testlimitedcapi') _testcapi = import_helper.import_module('_testcapi') +_testinternalcapi = import_helper.import_module('_testinternalcapi') class Constant(enum.IntEnum): @@ -131,5 +134,48 @@ def test_ClearWeakRefsNoCallbacks_no_weakref_support(self): _testcapi.pyobject_clear_weakrefs_no_callbacks(obj) +class EnableDeferredRefcountingTest(unittest.TestCase): + """Test PyUnstable_Object_EnableDeferredRefcount""" + @support.requires_resource("cpu") + def test_enable_deferred_refcount(self): + from threading import Thread + + self.assertEqual(_testcapi.pyobject_enable_deferred_refcount("not tracked"), 0) + foo = [] + self.assertEqual(_testcapi.pyobject_enable_deferred_refcount(foo), int(support.Py_GIL_DISABLED)) + + # Make sure reference counting works on foo now + self.assertEqual(foo, []) + if support.Py_GIL_DISABLED: + self.assertTrue(_testinternalcapi.has_deferred_refcount(foo)) + + # Make sure that PyUnstable_Object_EnableDeferredRefcount is thread safe + def silly_func(obj): + self.assertIn( + _testcapi.pyobject_enable_deferred_refcount(obj), + (0, 1) + ) + + silly_list = [1, 2, 3] + threads = [ + Thread(target=silly_func, args=(silly_list,)) for _ in range(5) + ] + + with threading_helper.catch_threading_exception() as cm: + for t in threads: + t.start() + + for i in range(10): + silly_list.append(i) + + for t in threads: + t.join() + + self.assertIsNone(cm.exc_value) + + if support.Py_GIL_DISABLED: + self.assertTrue(_testinternalcapi.has_deferred_refcount(silly_list)) + + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/C_API/2024-09-03-13-33-33.gh-issue-123619.HhgUUI.rst b/Misc/NEWS.d/next/C_API/2024-09-03-13-33-33.gh-issue-123619.HhgUUI.rst new file mode 100644 index 00000000000000..ac821b5326026e --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2024-09-03-13-33-33.gh-issue-123619.HhgUUI.rst @@ -0,0 +1,2 @@ +Added the :c:func:`PyUnstable_Object_EnableDeferredRefcount` function for +enabling :pep:`703` deferred reference counting. diff --git a/Modules/_testcapi/object.c b/Modules/_testcapi/object.c index 1c76e766a790f0..3af5429ef00985 100644 --- a/Modules/_testcapi/object.c +++ b/Modules/_testcapi/object.c @@ -124,13 +124,20 @@ pyobject_clear_weakrefs_no_callbacks(PyObject *self, PyObject *obj) Py_RETURN_NONE; } +static PyObject * +pyobject_enable_deferred_refcount(PyObject *self, PyObject *obj) +{ + int result = PyUnstable_Object_EnableDeferredRefcount(obj); + return PyLong_FromLong(result); +} + static PyMethodDef test_methods[] = { {"call_pyobject_print", call_pyobject_print, METH_VARARGS}, {"pyobject_print_null", pyobject_print_null, METH_VARARGS}, {"pyobject_print_noref_object", pyobject_print_noref_object, METH_VARARGS}, {"pyobject_print_os_error", pyobject_print_os_error, METH_VARARGS}, {"pyobject_clear_weakrefs_no_callbacks", pyobject_clear_weakrefs_no_callbacks, METH_O}, - + {"pyobject_enable_deferred_refcount", pyobject_enable_deferred_refcount, METH_O}, {NULL}, }; diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index 2c1ebcbbfdf419..b02f794d27d5bd 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -2069,6 +2069,14 @@ identify_type_slot_wrappers(PyObject *self, PyObject *Py_UNUSED(ignored)) return _PyType_GetSlotWrapperNames(); } + +static PyObject * +has_deferred_refcount(PyObject *self, PyObject *op) +{ + return PyBool_FromLong(_PyObject_HasDeferredRefcount(op)); +} + + static PyMethodDef module_functions[] = { {"get_configs", get_configs, METH_NOARGS}, {"get_recursion_depth", get_recursion_depth, METH_NOARGS}, @@ -2165,6 +2173,7 @@ static PyMethodDef module_functions[] = { GH_119213_GETARGS_METHODDEF {"get_static_builtin_types", get_static_builtin_types, METH_NOARGS}, {"identify_type_slot_wrappers", identify_type_slot_wrappers, METH_NOARGS}, + {"has_deferred_refcount", has_deferred_refcount, METH_O}, {NULL, NULL} /* sentinel */ }; diff --git a/Objects/object.c b/Objects/object.c index 7cc74a8dc0d8eb..052dea9ad1feff 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -2519,6 +2519,35 @@ _PyObject_SetDeferredRefcount(PyObject *op) #endif } +int +PyUnstable_Object_EnableDeferredRefcount(PyObject *op) +{ +#ifdef Py_GIL_DISABLED + if (!PyType_IS_GC(Py_TYPE(op))) { + // Deferred reference counting doesn't work + // on untracked types. + return 0; + } + + uint8_t bits = _Py_atomic_load_uint8(&op->ob_gc_bits); + if ((bits & _PyGC_BITS_DEFERRED) != 0) + { + // Nothing to do. + return 0; + } + + if (_Py_atomic_compare_exchange_uint8(&op->ob_gc_bits, &bits, bits | _PyGC_BITS_DEFERRED) == 0) + { + // Someone beat us to it! + return 0; + } + _Py_atomic_add_ssize(&op->ob_ref_shared, _Py_REF_SHARED(_Py_REF_DEFERRED, 0)); + return 1; +#else + return 0; +#endif +} + void _Py_ResurrectReference(PyObject *op) { From b2bbdc56e3276f3b37ea5cf5f73f49c4cce6d9f6 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 13 Nov 2024 17:46:10 +0100 Subject: [PATCH 167/219] gh-126456: Fix _pyrepl curses tigetstr() (#126472) --- Lib/_pyrepl/_minimal_curses.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/_pyrepl/_minimal_curses.py b/Lib/_pyrepl/_minimal_curses.py index 849617bf7585e4..d884f880f50ac7 100644 --- a/Lib/_pyrepl/_minimal_curses.py +++ b/Lib/_pyrepl/_minimal_curses.py @@ -34,7 +34,7 @@ def _find_clib() -> str: clib.setupterm.restype = ctypes.c_int clib.tigetstr.argtypes = [ctypes.c_char_p] -clib.tigetstr.restype = ctypes.POINTER(ctypes.c_char) +clib.tigetstr.restype = ctypes.c_ssize_t clib.tparm.argtypes = [ctypes.c_char_p] + 9 * [ctypes.c_int] # type: ignore[operator] clib.tparm.restype = ctypes.c_char_p @@ -56,7 +56,7 @@ def tigetstr(cap): if not isinstance(cap, bytes): cap = cap.encode("ascii") result = clib.tigetstr(cap) - if ctypes.cast(result, ctypes.c_void_p).value == ERR: + if result == ERR: return None return ctypes.cast(result, ctypes.c_char_p).value From 8c9c6d3c1234e730c0beb2a6123e68fe98e57ede Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Thu, 14 Nov 2024 02:09:26 +0900 Subject: [PATCH 168/219] gh-123465: Ensure PyType_FromMetaclass avoids extra strcmp (GH-125460) use else --- Objects/typeobject.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 4af7f0273aae91..a6cf3da542b691 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -4761,10 +4761,10 @@ PyType_FromMetaclass( if (strcmp(memb->name, "__weaklistoffset__") == 0) { weaklistoffset_member = memb; } - if (strcmp(memb->name, "__dictoffset__") == 0) { + else if (strcmp(memb->name, "__dictoffset__") == 0) { dictoffset_member = memb; } - if (strcmp(memb->name, "__vectorcalloffset__") == 0) { + else if (strcmp(memb->name, "__vectorcalloffset__") == 0) { vectorcalloffset_member = memb; } } From 3c9996909402fadc98e6ca2a64e75a71a7427352 Mon Sep 17 00:00:00 2001 From: Seth Michael Larson Date: Wed, 13 Nov 2024 12:31:20 -0600 Subject: [PATCH 169/219] gh-126623: Update libexpat to 2.6.4, make future updates easier (GH-126792) Update libexpat to 2.6.4, make future updates easier. --- ...-11-13-11-09-12.gh-issue-126623.TO7NnR.rst | 1 + Misc/sbom.spdx.json | 22 +++---- Modules/expat/expat.h | 6 +- Modules/expat/expat_external.h | 9 ++- Modules/expat/refresh.sh | 57 +++++++++++++++++++ Modules/expat/xmlparse.c | 18 ++++-- Tools/build/generate_sbom.py | 28 +++++++++ 7 files changed, 119 insertions(+), 22 deletions(-) create mode 100644 Misc/NEWS.d/next/Security/2024-11-13-11-09-12.gh-issue-126623.TO7NnR.rst create mode 100755 Modules/expat/refresh.sh diff --git a/Misc/NEWS.d/next/Security/2024-11-13-11-09-12.gh-issue-126623.TO7NnR.rst b/Misc/NEWS.d/next/Security/2024-11-13-11-09-12.gh-issue-126623.TO7NnR.rst new file mode 100644 index 00000000000000..f09a158af2a475 --- /dev/null +++ b/Misc/NEWS.d/next/Security/2024-11-13-11-09-12.gh-issue-126623.TO7NnR.rst @@ -0,0 +1 @@ +Upgrade libexpat to 2.6.4 diff --git a/Misc/sbom.spdx.json b/Misc/sbom.spdx.json index cc73e93009b43f..583ad84e18fd4a 100644 --- a/Misc/sbom.spdx.json +++ b/Misc/sbom.spdx.json @@ -48,11 +48,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "6aaee1b194bea30f0a60d1cce71eada8b14d3526" + "checksumValue": "373cc00d87782a736970644d863ff2ebbd0e4886" }, { "algorithm": "SHA256", - "checksumValue": "7bd4e53a8015534b5bbb58afe1a131b3989d3d4fca29bca685c44d34bcaa2555" + "checksumValue": "0f750bc336e510d14ac9a3e63fc2399f60f3f04f0061c426e86751ed5fba90e4" } ], "fileName": "Modules/expat/expat.h" @@ -62,11 +62,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "b70ce53fdc25ae482681ae2f6623c3c8edc9c1b7" + "checksumValue": "9e615c6e5c3ba00670f674a6b071bb855b0b563d" }, { "algorithm": "SHA256", - "checksumValue": "86afb425ec9999eb4f1ec9ab2fb41c58c4aa5cb9bf934b8c94264670fc5a961d" + "checksumValue": "3d90a4b65c40a3f848c36100f4d73b933a015c7b7cd85c28e4331a6b845c1ad0" } ], "fileName": "Modules/expat/expat_external.h" @@ -128,18 +128,18 @@ "fileName": "Modules/expat/nametab.h" }, { - "SPDXID": "SPDXRef-FILE-Modules-expat-pyexpatns.h", + "SPDXID": "SPDXRef-FILE-Modules-expat-refresh.sh", "checksums": [ { "algorithm": "SHA1", - "checksumValue": "f50c899172acd93fc539007bfb43315b83d407e4" + "checksumValue": "a9b0a33b8359cfe94b23972a1605daf8dcc605d9" }, { "algorithm": "SHA256", - "checksumValue": "d571b8258cfaa067a20adef553e5fcedd6671ca4a8841483496de031bd904567" + "checksumValue": "19eb541460bc2ca8b87118acd3c048f6af77affbf8719ac29aa7b6c8d70f83fd" } ], - "fileName": "Modules/expat/pyexpatns.h" + "fileName": "Modules/expat/refresh.sh" }, { "SPDXID": "SPDXRef-FILE-Modules-expat-siphash.h", @@ -188,11 +188,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "b2ec0ad170ccc21e63fbcfc8d7404cdd756eedd3" + "checksumValue": "3199fbd38b6fb158f73d5c8de6b6e6e3812ef803" }, { "algorithm": "SHA256", - "checksumValue": "92159d4e17393e56ee85f47d9fb31348695a58589899aa01e7536cdc88f60b85" + "checksumValue": "c1518244dd5ea397e345d00e12cc45d42f43453ed208218559c981c97a0583e2" } ], "fileName": "Modules/expat/xmlparse.c" @@ -1749,7 +1749,7 @@ "spdxElementId": "SPDXRef-PACKAGE-expat" }, { - "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-pyexpatns.h", + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-refresh.sh", "relationshipType": "CONTAINS", "spdxElementId": "SPDXRef-PACKAGE-expat" }, diff --git a/Modules/expat/expat.h b/Modules/expat/expat.h index d0d6015a66283f..523b37d8d5787d 100644 --- a/Modules/expat/expat.h +++ b/Modules/expat/expat.h @@ -130,7 +130,9 @@ enum XML_Error { /* Added in 2.3.0. */ XML_ERROR_NO_BUFFER, /* Added in 2.4.0. */ - XML_ERROR_AMPLIFICATION_LIMIT_BREACH + XML_ERROR_AMPLIFICATION_LIMIT_BREACH, + /* Added in 2.6.4. */ + XML_ERROR_NOT_STARTED, }; enum XML_Content_Type { @@ -1066,7 +1068,7 @@ XML_SetReparseDeferralEnabled(XML_Parser parser, XML_Bool enabled); */ #define XML_MAJOR_VERSION 2 #define XML_MINOR_VERSION 6 -#define XML_MICRO_VERSION 3 +#define XML_MICRO_VERSION 4 #ifdef __cplusplus } diff --git a/Modules/expat/expat_external.h b/Modules/expat/expat_external.h index 12c560e14716ff..567872b09836e1 100644 --- a/Modules/expat/expat_external.h +++ b/Modules/expat/expat_external.h @@ -40,6 +40,10 @@ #ifndef Expat_External_INCLUDED #define Expat_External_INCLUDED 1 +/* Namespace external symbols to allow multiple libexpat version to + co-exist. */ +#include "pyexpatns.h" + /* External API definitions */ /* Expat tries very hard to make the API boundary very specifically @@ -64,11 +68,6 @@ compiled with the cdecl calling convention as the default since system headers may assume the cdecl convention. */ - -/* Namespace external symbols to allow multiple libexpat version to - co-exist. */ -#include "pyexpatns.h" - #ifndef XMLCALL # if defined(_MSC_VER) # define XMLCALL __cdecl diff --git a/Modules/expat/refresh.sh b/Modules/expat/refresh.sh new file mode 100755 index 00000000000000..82a9dbc23ad26b --- /dev/null +++ b/Modules/expat/refresh.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +# +# Use this script to update libexpat + +set -e +set -o pipefail + +if [[ "${BASH_VERSINFO[0]}" -lt 4 ]]; then + echo "A bash version >= 4 required. Got: $BASH_VERSION" >&2 + exit 1 +fi + +# Update this when updating to a new version after verifying that the changes +# the update brings in are good. These values are used for verifying the SBOM, too. +expected_libexpat_tag="R_2_6_4" +expected_libexpat_version="2.6.4" +expected_libexpat_sha256="fd03b7172b3bd7427a3e7a812063f74754f24542429b634e0db6511b53fb2278" + +expat_dir="$(realpath "$(dirname -- "${BASH_SOURCE[0]}")")" +cd ${expat_dir} + +# Step 1: download and copy files +curl --location "https://github.com/libexpat/libexpat/releases/download/${expected_libexpat_tag}/expat-${expected_libexpat_version}.tar.gz" > libexpat.tar.gz +echo "${expected_libexpat_sha256} libexpat.tar.gz" | sha256sum --check + +# Step 2: Pull files from the libexpat distribution +declare -a lib_files +lib_files=( + ascii.h + asciitab.h + expat.h + expat_external.h + iasciitab.h + internal.h + latin1tab.h + nametab.h + siphash.h + utf8tab.h + winconfig.h + xmlparse.c + xmlrole.c + xmlrole.h + xmltok.c + xmltok.h + xmltok_impl.c + xmltok_impl.h + xmltok_ns.c +) +for f in "${lib_files[@]}"; do + tar xzvf libexpat.tar.gz "expat-${expected_libexpat_version}/lib/${f}" --strip-components 2 +done +rm libexpat.tar.gz + +# Step 3: Add the namespacing include to expat_external.h +sed -i 's/#define Expat_External_INCLUDED 1/&\n\n\/* Namespace external symbols to allow multiple libexpat version to\n co-exist. \*\/\n#include "pyexpatns.h"/' expat_external.h + +echo "Updated; verify all is okay using git diff and git status." diff --git a/Modules/expat/xmlparse.c b/Modules/expat/xmlparse.c index d9285b213b38bd..a4e091e7c33c0a 100644 --- a/Modules/expat/xmlparse.c +++ b/Modules/expat/xmlparse.c @@ -1,4 +1,4 @@ -/* ba4cdf9bdb534f355a9def4c9e25d20ee8e72f95b0a4d930be52e563f5080196 (2.6.3+) +/* c5625880f4bf417c1463deee4eb92d86ff413f802048621c57e25fe483eb59e4 (2.6.4+) __ __ _ ___\ \/ /_ __ __ _| |_ / _ \\ /| '_ \ / _` | __| @@ -40,6 +40,7 @@ Copyright (c) 2023 Owain Davies Copyright (c) 2023-2024 Sony Corporation / Snild Dolkow Copyright (c) 2024 Berkay Eren Ürün + Copyright (c) 2024 Hanno Böck Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -2234,6 +2235,9 @@ XML_StopParser(XML_Parser parser, XML_Bool resumable) { if (parser == NULL) return XML_STATUS_ERROR; switch (parser->m_parsingStatus.parsing) { + case XML_INITIALIZED: + parser->m_errorCode = XML_ERROR_NOT_STARTED; + return XML_STATUS_ERROR; case XML_SUSPENDED: if (resumable) { parser->m_errorCode = XML_ERROR_SUSPENDED; @@ -2244,7 +2248,7 @@ XML_StopParser(XML_Parser parser, XML_Bool resumable) { case XML_FINISHED: parser->m_errorCode = XML_ERROR_FINISHED; return XML_STATUS_ERROR; - default: + case XML_PARSING: if (resumable) { #ifdef XML_DTD if (parser->m_isParamEntity) { @@ -2255,6 +2259,9 @@ XML_StopParser(XML_Parser parser, XML_Bool resumable) { parser->m_parsingStatus.parsing = XML_SUSPENDED; } else parser->m_parsingStatus.parsing = XML_FINISHED; + break; + default: + assert(0); } return XML_STATUS_OK; } @@ -2519,6 +2526,9 @@ XML_ErrorString(enum XML_Error code) { case XML_ERROR_AMPLIFICATION_LIMIT_BREACH: return XML_L( "limit on input amplification factor (from DTD and entities) breached"); + /* Added in 2.6.4. */ + case XML_ERROR_NOT_STARTED: + return XML_L("parser not started"); } return NULL; } @@ -7856,7 +7866,7 @@ accountingReportDiff(XML_Parser rootParser, assert(! rootParser->m_parentParser); fprintf(stderr, - " (+" EXPAT_FMT_PTRDIFF_T("6") " bytes %s|%d, xmlparse.c:%d) %*s\"", + " (+" EXPAT_FMT_PTRDIFF_T("6") " bytes %s|%u, xmlparse.c:%d) %*s\"", bytesMore, (account == XML_ACCOUNT_DIRECT) ? "DIR" : "EXP", levelsAwayFromRootParser, source_line, 10, ""); @@ -7969,7 +7979,7 @@ entityTrackingReportStats(XML_Parser rootParser, ENTITY *entity, fprintf( stderr, - "expat: Entities(%p): Count %9d, depth %2d/%2d %*s%s%s; %s length %d (xmlparse.c:%d)\n", + "expat: Entities(%p): Count %9u, depth %2u/%2u %*s%s%s; %s length %d (xmlparse.c:%d)\n", (void *)rootParser, rootParser->m_entity_stats.countEverOpened, rootParser->m_entity_stats.currentDepth, rootParser->m_entity_stats.maximumDepthSeen, diff --git a/Tools/build/generate_sbom.py b/Tools/build/generate_sbom.py index 020f874cffeaef..5c4a725102d79a 100644 --- a/Tools/build/generate_sbom.py +++ b/Tools/build/generate_sbom.py @@ -59,6 +59,8 @@ class PackageFiles(typing.NamedTuple): include=["Modules/expat/**"], exclude=[ "Modules/expat/expat_config.h", + "Modules/expat/pyexpatns.h", + "Modules/_hacl/refresh.sh", ] ), "macholib": PackageFiles( @@ -218,6 +220,32 @@ def check_sbom_packages(sbom_data: dict[str, typing.Any]) -> None: "HACL* SBOM version doesn't match value in 'Modules/_hacl/refresh.sh'" ) + # libexpat specifies its expected rev in a refresh script. + if package["name"] == "libexpat": + libexpat_refresh_sh = (CPYTHON_ROOT_DIR / "Modules/expat/refresh.sh").read_text() + libexpat_expected_version_match = re.search( + r"expected_libexpat_version=\"([0-9]+\.[0-9]+\.[0-9]+)\"", + libexpat_refresh_sh + ) + libexpat_expected_sha256_match = re.search( + r"expected_libexpat_sha256=\"[a-f0-9]{40}\"", + libexpat_refresh_sh + ) + libexpat_expected_version = libexpat_expected_version_match and libexpat_expected_version_match.group(1) + libexpat_expected_sha256 = libexpat_expected_sha256_match and libexpat_expected_sha256_match.group(1) + + error_if( + libexpat_expected_version != version, + "libexpat SBOM version doesn't match value in 'Modules/expat/refresh.sh'" + ) + error_if( + package["checksums"] != [{ + "algorithm": "SHA256", + "checksumValue": libexpat_expected_sha256 + }], + "libexpat SBOM checksum doesn't match value in 'Modules/expat/refresh.sh'" + ) + # License must be on the approved list for SPDX. license_concluded = package["licenseConcluded"] error_if( From 12ca7e622ff21ba3b7c90c62be6f6f82d543f25b Mon Sep 17 00:00:00 2001 From: sobolevn Date: Wed, 13 Nov 2024 22:29:28 +0300 Subject: [PATCH 170/219] gh-109413: Enable `strict_optional` for `libregrtest/main.py` (#126394) --- Lib/test/libregrtest/cmdline.py | 4 +--- Lib/test/libregrtest/main.py | 19 +++++++++++++++++-- Lib/test/libregrtest/mypy.ini | 4 +--- Lib/test/libregrtest/run_workers.py | 2 ++ 4 files changed, 21 insertions(+), 8 deletions(-) diff --git a/Lib/test/libregrtest/cmdline.py b/Lib/test/libregrtest/cmdline.py index 8bef04cba81138..0c94fcc1907071 100644 --- a/Lib/test/libregrtest/cmdline.py +++ b/Lib/test/libregrtest/cmdline.py @@ -148,7 +148,7 @@ def __init__(self, **kwargs) -> None: self.randomize = False self.fromfile = None self.fail_env_changed = False - self.use_resources = None + self.use_resources: list[str] = [] self.trace = False self.coverdir = 'coverage' self.runleaks = False @@ -403,8 +403,6 @@ def _parse_args(args, **kwargs): raise TypeError('%r is an invalid keyword argument ' 'for this function' % k) setattr(ns, k, v) - if ns.use_resources is None: - ns.use_resources = [] parser = _create_parser() # Issue #14191: argparse doesn't support "intermixed" positional and diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index 133eba8ffe8e69..49209b0cec756e 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -123,7 +123,7 @@ def __init__(self, ns: Namespace, _add_python_opts: bool = False): self.python_cmd = None self.coverage: bool = ns.trace self.coverage_dir: StrPath | None = ns.coverdir - self.tmp_dir: StrPath | None = ns.tempdir + self._tmp_dir: StrPath | None = ns.tempdir # Randomize self.randomize: bool = ns.randomize @@ -159,6 +159,8 @@ def log(self, line: str = '') -> None: self.logger.log(line) def find_tests(self, tests: TestList | None = None) -> tuple[TestTuple, TestList | None]: + if tests is None: + tests = [] if self.single_test_run: self.next_single_filename = os.path.join(self.tmp_dir, 'pynexttest') try: @@ -454,6 +456,11 @@ def finalize_tests(self, coverage: trace.CoverageResults | None) -> None: self.results.write_junit(self.junit_filename) def display_summary(self) -> None: + if self.first_runtests is None: + raise ValueError( + "Should never call `display_summary()` before calling `_run_test()`" + ) + duration = time.perf_counter() - self.logger.start_time filtered = bool(self.match_tests) @@ -708,7 +715,15 @@ def _init(self): strip_py_suffix(self.cmdline_args) - self.tmp_dir = get_temp_dir(self.tmp_dir) + self._tmp_dir = get_temp_dir(self._tmp_dir) + + @property + def tmp_dir(self) -> StrPath: + if self._tmp_dir is None: + raise ValueError( + "Should never use `.tmp_dir` before calling `.main()`" + ) + return self._tmp_dir def main(self, tests: TestList | None = None) -> NoReturn: if self.want_add_python_opts: diff --git a/Lib/test/libregrtest/mypy.ini b/Lib/test/libregrtest/mypy.ini index 22c7c7a9acef14..da75a27158a600 100644 --- a/Lib/test/libregrtest/mypy.ini +++ b/Lib/test/libregrtest/mypy.ini @@ -22,10 +22,8 @@ disallow_untyped_defs = False check_untyped_defs = False warn_return_any = False -disable_error_code = return - # Enable --strict-optional for these ASAP: -[mypy-Lib.test.libregrtest.main.*,Lib.test.libregrtest.run_workers.*] +[mypy-Lib.test.libregrtest.run_workers.*] strict_optional = False # Various internal modules that typeshed deliberately doesn't have stubs for: diff --git a/Lib/test/libregrtest/run_workers.py b/Lib/test/libregrtest/run_workers.py index 387ddf9614cf79..dcc817ae9aceb6 100644 --- a/Lib/test/libregrtest/run_workers.py +++ b/Lib/test/libregrtest/run_workers.py @@ -211,6 +211,7 @@ def _run_process(self, runtests: WorkerRunTests, output_fd: int, # on reading closed stdout raise ExitThread raise + return None except: self._kill() raise @@ -544,6 +545,7 @@ def _get_result(self) -> QueueOutput | None: running = get_running(self.workers) if running: self.log(running) + return None def display_result(self, mp_result: MultiprocessResult) -> None: result = mp_result.result From 35010b8cf2e6f5f2791fb336951c518e4f087a43 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 13 Nov 2024 22:50:46 +0200 Subject: [PATCH 171/219] gh-126390: Support for preserving order of options and nonoption arguments in gnu_getopt() (GH-126393) --- Doc/library/getopt.rst | 24 +++++++++++++++++++ Doc/whatsnew/3.14.rst | 3 +++ Lib/getopt.py | 18 ++++++++++---- Lib/test/test_getopt.py | 6 +++++ ...-11-04-13-16-18.gh-issue-126390.Cxvqa5.rst | 2 ++ 5 files changed, 48 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-11-04-13-16-18.gh-issue-126390.Cxvqa5.rst diff --git a/Doc/library/getopt.rst b/Doc/library/getopt.rst index def0ea357bceb2..891885d3afbf7a 100644 --- a/Doc/library/getopt.rst +++ b/Doc/library/getopt.rst @@ -85,6 +85,16 @@ exception: variable :envvar:`!POSIXLY_CORRECT` is set, then option processing stops as soon as a non-option argument is encountered. + If the first character of the option string is ``'-'``, non-option arguments + that are followed by options are added to the list of option-and-value pairs + as a pair that has ``None`` as its first element and the list of non-option + arguments as its second element. + The second element of the :func:`!gnu_getopt` result is a list of + program arguments after the last option. + + .. versionchanged:: 3.14 + Support for returning intermixed options and non-option arguments in order. + .. exception:: GetoptError @@ -144,6 +154,20 @@ Optional arguments should be specified explicitly: >>> args ['a1', 'a2'] +The order of options and non-option arguments can be preserved: + +.. doctest:: + + >>> s = 'a1 -x a2 a3 a4 --long a5 a6' + >>> args = s.split() + >>> args + ['a1', '-x', 'a2', 'a3', 'a4', '--long', 'a5', 'a6'] + >>> optlist, args = getopt.gnu_getopt(args, '-x:', ['long=']) + >>> optlist + [(None, ['a1']), ('-x', 'a2'), (None, ['a3', 'a4']), ('--long', 'a5')] + >>> args + ['a6'] + In a script, typical usage is something like this: .. testcode:: diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 31754fb55fcf02..d38188f0054754 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -331,6 +331,9 @@ getopt * Add support for options with optional arguments. (Contributed by Serhiy Storchaka in :gh:`126374`.) +* Add support for returning intermixed options and non-option arguments in order. + (Contributed by Serhiy Storchaka in :gh:`126390`.) + http ---- diff --git a/Lib/getopt.py b/Lib/getopt.py index c12c08b29c79c4..a9c452a601ee81 100644 --- a/Lib/getopt.py +++ b/Lib/getopt.py @@ -24,9 +24,6 @@ # TODO for gnu_getopt(): # # - GNU getopt_long_only mechanism -# - allow the caller to specify ordering -# - RETURN_IN_ORDER option -# - GNU extension with '-' as first character of option string # - an option string with a W followed by semicolon should # treat "-W foo" as "--foo" @@ -63,7 +60,7 @@ def getopt(args, shortopts, longopts = []): long options which should be supported. The leading '--' characters should not be included in the option name. Options which require an argument should be followed by an equal sign - ('='). Options which acept an optional argument should be + ('='). Options which accept an optional argument should be followed by an equal sign and question mark ('=?'). The return value consists of two elements: the first is a list of @@ -116,8 +113,13 @@ def gnu_getopt(args, shortopts, longopts = []): else: longopts = list(longopts) + return_in_order = False + if shortopts.startswith('-'): + shortopts = shortopts[1:] + all_options_first = False + return_in_order = True # Allow options after non-option arguments? - if shortopts.startswith('+'): + elif shortopts.startswith('+'): shortopts = shortopts[1:] all_options_first = True elif os.environ.get("POSIXLY_CORRECT"): @@ -131,8 +133,14 @@ def gnu_getopt(args, shortopts, longopts = []): break if args[0][:2] == '--': + if return_in_order and prog_args: + opts.append((None, prog_args)) + prog_args = [] opts, args = do_longs(opts, args[0][2:], longopts, args[1:]) elif args[0][:1] == '-' and args[0] != '-': + if return_in_order and prog_args: + opts.append((None, prog_args)) + prog_args = [] opts, args = do_shorts(opts, args[0][1:], shortopts, args[1:]) else: if all_options_first: diff --git a/Lib/test/test_getopt.py b/Lib/test/test_getopt.py index 0675bcbb4e8247..ed967ad27619ae 100644 --- a/Lib/test/test_getopt.py +++ b/Lib/test/test_getopt.py @@ -173,6 +173,12 @@ def test_gnu_getopt(self): self.assertEqual(args, ['-']) self.assertEqual(opts, [('-a', ''), ('-b', '-')]) + # Return positional arguments intermixed with options. + opts, args = getopt.gnu_getopt(cmdline, '-ab:', ['alpha', 'beta=']) + self.assertEqual(args, ['arg2']) + self.assertEqual(opts, [('-a', ''), (None, ['arg1']), ('-b', '1'), ('--alpha', ''), + ('--beta', '2'), ('--beta', '3')]) + # Posix style via + opts, args = getopt.gnu_getopt(cmdline, '+ab:', ['alpha', 'beta=']) self.assertEqual(opts, [('-a', '')]) diff --git a/Misc/NEWS.d/next/Library/2024-11-04-13-16-18.gh-issue-126390.Cxvqa5.rst b/Misc/NEWS.d/next/Library/2024-11-04-13-16-18.gh-issue-126390.Cxvqa5.rst new file mode 100644 index 00000000000000..3b32bb512f6556 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-11-04-13-16-18.gh-issue-126390.Cxvqa5.rst @@ -0,0 +1,2 @@ +Add support for returning intermixed options and non-option arguments in +order in :func:`getopt.gnu_getopt`. From 142104ce78a8717f99eb42ce030eb29775dd550c Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Wed, 13 Nov 2024 21:57:33 +0100 Subject: [PATCH 172/219] gh-89640: Pull in update to float word order detection in autoconf-archive (#126747) --- .github/workflows/build.yml | 2 +- Tools/build/regen-configure.sh | 2 +- aclocal.m4 | 96 ++++++++++++-- configure | 226 ++++++++++++++++----------------- configure.ac | 3 - 5 files changed, 195 insertions(+), 134 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b769bba72816d6..c854c13e12f922 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -46,7 +46,7 @@ jobs: # reproducible: to get the same tools versions (autoconf, aclocal, ...) runs-on: ubuntu-24.04 container: - image: ghcr.io/python/autoconf:2024.10.16.11360930377 + image: ghcr.io/python/autoconf:2024.11.11.11786316759 timeout-minutes: 60 needs: check_source if: needs.check_source.outputs.run_tests == 'true' diff --git a/Tools/build/regen-configure.sh b/Tools/build/regen-configure.sh index e1ecefddeb8732..d2a613b1e40dc1 100755 --- a/Tools/build/regen-configure.sh +++ b/Tools/build/regen-configure.sh @@ -5,7 +5,7 @@ set -e -x # The check_autoconf_regen job of .github/workflows/build.yml must kept in # sync with this script. Use the same container image than the job so the job # doesn't need to run autoreconf in a container. -IMAGE="ghcr.io/python/autoconf:2024.10.16.11360930377" +IMAGE="ghcr.io/python/autoconf:2024.11.11.11786316759" AUTORECONF="autoreconf -ivf -Werror" WORK_DIR="/src" diff --git a/aclocal.m4 b/aclocal.m4 index b082a5b1bc5e07..920c2b38560faa 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -91,7 +91,7 @@ m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun # and this notice are preserved. This file is offered as-is, without any # warranty. -#serial 12 +#serial 14 AC_DEFUN([AX_C_FLOAT_WORDS_BIGENDIAN], [AC_CACHE_CHECK(whether float word ordering is bigendian, @@ -112,10 +112,10 @@ int main (int argc, char *argv[]) ]])], [ -if grep noonsees conftest$EXEEXT >/dev/null ; then +if grep noonsees conftest* > /dev/null ; then ax_cv_c_float_words_bigendian=yes fi -if grep seesnoon conftest$EXEEXT >/dev/null ; then +if grep seesnoon conftest* >/dev/null ; then if test "$ax_cv_c_float_words_bigendian" = unknown; then ax_cv_c_float_words_bigendian=no else @@ -398,7 +398,7 @@ AC_DEFUN([AX_CHECK_OPENSSL], [ AC_SUBST([OPENSSL_LDFLAGS]) ]) -# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- +# pkg.m4 - Macros to locate and use pkg-config. -*- Autoconf -*- # serial 12 (pkg-config-0.29.2) dnl Copyright © 2004 Scott James Remnant . @@ -486,7 +486,7 @@ dnl Check to see whether a particular set of modules exists. Similar to dnl PKG_CHECK_MODULES(), but does not set variables or print errors. dnl dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) -dnl only at the first occurence in configure.ac, so if the first place +dnl only at the first occurrence in configure.ac, so if the first place dnl it's called might be skipped (such as if it is within an "if", you dnl have to call PKG_CHECK_EXISTS manually AC_DEFUN([PKG_CHECK_EXISTS], @@ -555,14 +555,14 @@ if test $pkg_failed = yes; then AC_MSG_RESULT([no]) _PKG_SHORT_ERRORS_SUPPORTED if test $_pkg_short_errors_supported = yes; then - $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` + $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` else - $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` + $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` fi - # Put the nasty error message in config.log where it belongs - echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD + # Put the nasty error message in config.log where it belongs + echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD - m4_default([$4], [AC_MSG_ERROR( + m4_default([$4], [AC_MSG_ERROR( [Package requirements ($2) were not met: $$1_PKG_ERRORS @@ -574,7 +574,7 @@ _PKG_TEXT])[]dnl ]) elif test $pkg_failed = untried; then AC_MSG_RESULT([no]) - m4_default([$4], [AC_MSG_FAILURE( + m4_default([$4], [AC_MSG_FAILURE( [The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. @@ -584,10 +584,10 @@ _PKG_TEXT To get pkg-config, see .])[]dnl ]) else - $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS - $1[]_LIBS=$pkg_cv_[]$1[]_LIBS + $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS + $1[]_LIBS=$pkg_cv_[]$1[]_LIBS AC_MSG_RESULT([yes]) - $3 + $3 fi[]dnl ])dnl PKG_CHECK_MODULES @@ -674,6 +674,74 @@ AS_VAR_COPY([$1], [pkg_cv_][$1]) AS_VAR_IF([$1], [""], [$5], [$4])dnl ])dnl PKG_CHECK_VAR +dnl PKG_WITH_MODULES(VARIABLE-PREFIX, MODULES, +dnl [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND], +dnl [DESCRIPTION], [DEFAULT]) +dnl ------------------------------------------ +dnl +dnl Prepare a "--with-" configure option using the lowercase +dnl [VARIABLE-PREFIX] name, merging the behaviour of AC_ARG_WITH and +dnl PKG_CHECK_MODULES in a single macro. +AC_DEFUN([PKG_WITH_MODULES], +[ +m4_pushdef([with_arg], m4_tolower([$1])) + +m4_pushdef([description], + [m4_default([$5], [build with ]with_arg[ support])]) + +m4_pushdef([def_arg], [m4_default([$6], [auto])]) +m4_pushdef([def_action_if_found], [AS_TR_SH([with_]with_arg)=yes]) +m4_pushdef([def_action_if_not_found], [AS_TR_SH([with_]with_arg)=no]) + +m4_case(def_arg, + [yes],[m4_pushdef([with_without], [--without-]with_arg)], + [m4_pushdef([with_without],[--with-]with_arg)]) + +AC_ARG_WITH(with_arg, + AS_HELP_STRING(with_without, description[ @<:@default=]def_arg[@:>@]),, + [AS_TR_SH([with_]with_arg)=def_arg]) + +AS_CASE([$AS_TR_SH([with_]with_arg)], + [yes],[PKG_CHECK_MODULES([$1],[$2],$3,$4)], + [auto],[PKG_CHECK_MODULES([$1],[$2], + [m4_n([def_action_if_found]) $3], + [m4_n([def_action_if_not_found]) $4])]) + +m4_popdef([with_arg]) +m4_popdef([description]) +m4_popdef([def_arg]) + +])dnl PKG_WITH_MODULES + +dnl PKG_HAVE_WITH_MODULES(VARIABLE-PREFIX, MODULES, +dnl [DESCRIPTION], [DEFAULT]) +dnl ----------------------------------------------- +dnl +dnl Convenience macro to trigger AM_CONDITIONAL after PKG_WITH_MODULES +dnl check._[VARIABLE-PREFIX] is exported as make variable. +AC_DEFUN([PKG_HAVE_WITH_MODULES], +[ +PKG_WITH_MODULES([$1],[$2],,,[$3],[$4]) + +AM_CONDITIONAL([HAVE_][$1], + [test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"]) +])dnl PKG_HAVE_WITH_MODULES + +dnl PKG_HAVE_DEFINE_WITH_MODULES(VARIABLE-PREFIX, MODULES, +dnl [DESCRIPTION], [DEFAULT]) +dnl ------------------------------------------------------ +dnl +dnl Convenience macro to run AM_CONDITIONAL and AC_DEFINE after +dnl PKG_WITH_MODULES check. HAVE_[VARIABLE-PREFIX] is exported as make +dnl and preprocessor variable. +AC_DEFUN([PKG_HAVE_DEFINE_WITH_MODULES], +[ +PKG_HAVE_WITH_MODULES([$1],[$2],[$3],[$4]) + +AS_IF([test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"], + [AC_DEFINE([HAVE_][$1], 1, [Enable ]m4_tolower([$1])[ support])]) +])dnl PKG_HAVE_DEFINE_WITH_MODULES + # AM_CONDITIONAL -*- Autoconf -*- # Copyright (C) 1997-2021 Free Software Foundation, Inc. diff --git a/configure b/configure index 7a9d9627e50dfc..b1ced3106618ba 100755 --- a/configure +++ b/configure @@ -13717,12 +13717,12 @@ else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then - LIBUUID_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "uuid >= 2.20" 2>&1` + LIBUUID_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "uuid >= 2.20" 2>&1` else - LIBUUID_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "uuid >= 2.20" 2>&1` + LIBUUID_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "uuid >= 2.20" 2>&1` fi - # Put the nasty error message in config.log where it belongs - echo "$LIBUUID_PKG_ERRORS" >&5 + # Put the nasty error message in config.log where it belongs + echo "$LIBUUID_PKG_ERRORS" >&5 save_CFLAGS=$CFLAGS @@ -13974,11 +13974,11 @@ LIBS=$save_LIBS else - LIBUUID_CFLAGS=$pkg_cv_LIBUUID_CFLAGS - LIBUUID_LIBS=$pkg_cv_LIBUUID_LIBS + LIBUUID_CFLAGS=$pkg_cv_LIBUUID_CFLAGS + LIBUUID_LIBS=$pkg_cv_LIBUUID_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } - have_uuid=yes + have_uuid=yes printf "%s\n" "#define HAVE_UUID_H 1" >>confdefs.h printf "%s\n" "#define HAVE_UUID_GENERATE_TIME_SAFE 1" >>confdefs.h @@ -14666,12 +14666,12 @@ else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then - LIBFFI_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libffi" 2>&1` + LIBFFI_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libffi" 2>&1` else - LIBFFI_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libffi" 2>&1` + LIBFFI_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libffi" 2>&1` fi - # Put the nasty error message in config.log where it belongs - echo "$LIBFFI_PKG_ERRORS" >&5 + # Put the nasty error message in config.log where it belongs + echo "$LIBFFI_PKG_ERRORS" >&5 save_CFLAGS=$CFLAGS @@ -14817,11 +14817,11 @@ LIBS=$save_LIBS else - LIBFFI_CFLAGS=$pkg_cv_LIBFFI_CFLAGS - LIBFFI_LIBS=$pkg_cv_LIBFFI_LIBS + LIBFFI_CFLAGS=$pkg_cv_LIBFFI_CFLAGS + LIBFFI_LIBS=$pkg_cv_LIBFFI_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } - have_libffi=yes + have_libffi=yes fi fi @@ -15143,25 +15143,25 @@ else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then - LIBMPDEC_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libmpdec >= 2.5.0" 2>&1` + LIBMPDEC_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libmpdec >= 2.5.0" 2>&1` else - LIBMPDEC_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libmpdec >= 2.5.0" 2>&1` + LIBMPDEC_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libmpdec >= 2.5.0" 2>&1` fi - # Put the nasty error message in config.log where it belongs - echo "$LIBMPDEC_PKG_ERRORS" >&5 + # Put the nasty error message in config.log where it belongs + echo "$LIBMPDEC_PKG_ERRORS" >&5 - LIBMPDEC_CFLAGS=${LIBMPDEC_CFLAGS-""} + LIBMPDEC_CFLAGS=${LIBMPDEC_CFLAGS-""} LIBMPDEC_LIBS=${LIBMPDEC_LIBS-"-lmpdec -lm"} LIBMPDEC_INTERNAL= elif test $pkg_failed = untried; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } - LIBMPDEC_CFLAGS=${LIBMPDEC_CFLAGS-""} + LIBMPDEC_CFLAGS=${LIBMPDEC_CFLAGS-""} LIBMPDEC_LIBS=${LIBMPDEC_LIBS-"-lmpdec -lm"} LIBMPDEC_INTERNAL= else - LIBMPDEC_CFLAGS=$pkg_cv_LIBMPDEC_CFLAGS - LIBMPDEC_LIBS=$pkg_cv_LIBMPDEC_LIBS + LIBMPDEC_CFLAGS=$pkg_cv_LIBMPDEC_CFLAGS + LIBMPDEC_LIBS=$pkg_cv_LIBMPDEC_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } @@ -15412,12 +15412,12 @@ else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then - LIBSQLITE3_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "sqlite3 >= 3.15.2" 2>&1` + LIBSQLITE3_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "sqlite3 >= 3.15.2" 2>&1` else - LIBSQLITE3_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "sqlite3 >= 3.15.2" 2>&1` + LIBSQLITE3_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "sqlite3 >= 3.15.2" 2>&1` fi - # Put the nasty error message in config.log where it belongs - echo "$LIBSQLITE3_PKG_ERRORS" >&5 + # Put the nasty error message in config.log where it belongs + echo "$LIBSQLITE3_PKG_ERRORS" >&5 LIBSQLITE3_CFLAGS=${LIBSQLITE3_CFLAGS-""} @@ -15433,8 +15433,8 @@ printf "%s\n" "no" >&6; } else - LIBSQLITE3_CFLAGS=$pkg_cv_LIBSQLITE3_CFLAGS - LIBSQLITE3_LIBS=$pkg_cv_LIBSQLITE3_LIBS + LIBSQLITE3_CFLAGS=$pkg_cv_LIBSQLITE3_CFLAGS + LIBSQLITE3_LIBS=$pkg_cv_LIBSQLITE3_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } @@ -16176,24 +16176,24 @@ else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then - TCLTK_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$_QUERY" 2>&1` + TCLTK_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$_QUERY" 2>&1` else - TCLTK_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$_QUERY" 2>&1` + TCLTK_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$_QUERY" 2>&1` fi - # Put the nasty error message in config.log where it belongs - echo "$TCLTK_PKG_ERRORS" >&5 + # Put the nasty error message in config.log where it belongs + echo "$TCLTK_PKG_ERRORS" >&5 - found_tcltk=no + found_tcltk=no elif test $pkg_failed = untried; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } - found_tcltk=no + found_tcltk=no else - TCLTK_CFLAGS=$pkg_cv_TCLTK_CFLAGS - TCLTK_LIBS=$pkg_cv_TCLTK_LIBS + TCLTK_CFLAGS=$pkg_cv_TCLTK_CFLAGS + TCLTK_LIBS=$pkg_cv_TCLTK_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } - found_tcltk=yes + found_tcltk=yes fi fi @@ -16273,14 +16273,14 @@ else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then - X11_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "x11" 2>&1` + X11_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "x11" 2>&1` else - X11_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "x11" 2>&1` + X11_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "x11" 2>&1` fi - # Put the nasty error message in config.log where it belongs - echo "$X11_PKG_ERRORS" >&5 + # Put the nasty error message in config.log where it belongs + echo "$X11_PKG_ERRORS" >&5 - as_fn_error $? "Package requirements (x11) were not met: + as_fn_error $? "Package requirements (x11) were not met: $X11_PKG_ERRORS @@ -16293,7 +16293,7 @@ See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full @@ -16306,8 +16306,8 @@ See the pkg-config man page for more details. To get pkg-config, see . See \`config.log' for more details" "$LINENO" 5; } else - X11_CFLAGS=$pkg_cv_X11_CFLAGS - X11_LIBS=$pkg_cv_X11_LIBS + X11_CFLAGS=$pkg_cv_X11_CFLAGS + X11_LIBS=$pkg_cv_X11_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } @@ -20712,12 +20712,12 @@ else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then - ZLIB_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "zlib >= 1.2.0" 2>&1` + ZLIB_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "zlib >= 1.2.0" 2>&1` else - ZLIB_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "zlib >= 1.2.0" 2>&1` + ZLIB_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "zlib >= 1.2.0" 2>&1` fi - # Put the nasty error message in config.log where it belongs - echo "$ZLIB_PKG_ERRORS" >&5 + # Put the nasty error message in config.log where it belongs + echo "$ZLIB_PKG_ERRORS" >&5 save_CFLAGS=$CFLAGS @@ -20975,8 +20975,8 @@ LIBS=$save_LIBS else - ZLIB_CFLAGS=$pkg_cv_ZLIB_CFLAGS - ZLIB_LIBS=$pkg_cv_ZLIB_LIBS + ZLIB_CFLAGS=$pkg_cv_ZLIB_CFLAGS + ZLIB_LIBS=$pkg_cv_ZLIB_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } @@ -21060,12 +21060,12 @@ else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then - BZIP2_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "bzip2" 2>&1` + BZIP2_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "bzip2" 2>&1` else - BZIP2_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "bzip2" 2>&1` + BZIP2_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "bzip2" 2>&1` fi - # Put the nasty error message in config.log where it belongs - echo "$BZIP2_PKG_ERRORS" >&5 + # Put the nasty error message in config.log where it belongs + echo "$BZIP2_PKG_ERRORS" >&5 save_CFLAGS=$CFLAGS @@ -21229,11 +21229,11 @@ LIBS=$save_LIBS else - BZIP2_CFLAGS=$pkg_cv_BZIP2_CFLAGS - BZIP2_LIBS=$pkg_cv_BZIP2_LIBS + BZIP2_CFLAGS=$pkg_cv_BZIP2_CFLAGS + BZIP2_LIBS=$pkg_cv_BZIP2_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } - have_bzip2=yes + have_bzip2=yes fi @@ -21288,12 +21288,12 @@ else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then - LIBLZMA_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "liblzma" 2>&1` + LIBLZMA_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "liblzma" 2>&1` else - LIBLZMA_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "liblzma" 2>&1` + LIBLZMA_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "liblzma" 2>&1` fi - # Put the nasty error message in config.log where it belongs - echo "$LIBLZMA_PKG_ERRORS" >&5 + # Put the nasty error message in config.log where it belongs + echo "$LIBLZMA_PKG_ERRORS" >&5 save_CFLAGS=$CFLAGS @@ -21457,11 +21457,11 @@ LIBS=$save_LIBS else - LIBLZMA_CFLAGS=$pkg_cv_LIBLZMA_CFLAGS - LIBLZMA_LIBS=$pkg_cv_LIBLZMA_LIBS + LIBLZMA_CFLAGS=$pkg_cv_LIBLZMA_CFLAGS + LIBLZMA_LIBS=$pkg_cv_LIBLZMA_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } - have_liblzma=yes + have_liblzma=yes fi @@ -24174,10 +24174,10 @@ if ac_fn_c_try_link "$LINENO" then : -if grep noonsees conftest$EXEEXT >/dev/null ; then +if grep noonsees conftest* > /dev/null ; then ax_cv_c_float_words_bigendian=yes fi -if grep seesnoon conftest$EXEEXT >/dev/null ; then +if grep seesnoon conftest* >/dev/null ; then if test "$ax_cv_c_float_words_bigendian" = unknown; then ax_cv_c_float_words_bigendian=no else @@ -24213,10 +24213,6 @@ printf "%s\n" "#define DOUBLE_IS_LITTLE_ENDIAN_IEEE754 1" >>confdefs.h # but if it's not big or little, then it must be this? printf "%s\n" "#define DOUBLE_IS_ARM_MIXED_ENDIAN_IEEE754 1" >>confdefs.h - ;; #( - wasm*) : - -printf "%s\n" "#define DOUBLE_IS_LITTLE_ENDIAN_IEEE754 1" >>confdefs.h ;; #( *) : as_fn_error $? "Unknown float word ordering. You need to manually preset ax_cv_c_float_words_bigendian=no (or yes) according to your system." "$LINENO" 5 ;; @@ -25296,12 +25292,12 @@ else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then - LIBREADLINE_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "readline" 2>&1` + LIBREADLINE_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "readline" 2>&1` else - LIBREADLINE_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "readline" 2>&1` + LIBREADLINE_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "readline" 2>&1` fi - # Put the nasty error message in config.log where it belongs - echo "$LIBREADLINE_PKG_ERRORS" >&5 + # Put the nasty error message in config.log where it belongs + echo "$LIBREADLINE_PKG_ERRORS" >&5 save_CFLAGS=$CFLAGS @@ -25459,8 +25455,8 @@ LIBS=$save_LIBS else - LIBREADLINE_CFLAGS=$pkg_cv_LIBREADLINE_CFLAGS - LIBREADLINE_LIBS=$pkg_cv_LIBREADLINE_LIBS + LIBREADLINE_CFLAGS=$pkg_cv_LIBREADLINE_CFLAGS + LIBREADLINE_LIBS=$pkg_cv_LIBREADLINE_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } @@ -25527,12 +25523,12 @@ else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then - LIBEDIT_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libedit" 2>&1` + LIBEDIT_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libedit" 2>&1` else - LIBEDIT_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libedit" 2>&1` + LIBEDIT_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libedit" 2>&1` fi - # Put the nasty error message in config.log where it belongs - echo "$LIBEDIT_PKG_ERRORS" >&5 + # Put the nasty error message in config.log where it belongs + echo "$LIBEDIT_PKG_ERRORS" >&5 save_CFLAGS=$CFLAGS @@ -25694,8 +25690,8 @@ LIBS=$save_LIBS else - LIBEDIT_CFLAGS=$pkg_cv_LIBEDIT_CFLAGS - LIBEDIT_LIBS=$pkg_cv_LIBEDIT_LIBS + LIBEDIT_CFLAGS=$pkg_cv_LIBEDIT_CFLAGS + LIBEDIT_LIBS=$pkg_cv_LIBEDIT_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } @@ -26556,21 +26552,21 @@ else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then - CURSES_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "ncursesw" 2>&1` + CURSES_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "ncursesw" 2>&1` else - CURSES_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "ncursesw" 2>&1` + CURSES_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "ncursesw" 2>&1` fi - # Put the nasty error message in config.log where it belongs - echo "$CURSES_PKG_ERRORS" >&5 + # Put the nasty error message in config.log where it belongs + echo "$CURSES_PKG_ERRORS" >&5 - have_curses=no + have_curses=no elif test $pkg_failed = untried; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } - have_curses=no + have_curses=no else - CURSES_CFLAGS=$pkg_cv_CURSES_CFLAGS - CURSES_LIBS=$pkg_cv_CURSES_LIBS + CURSES_CFLAGS=$pkg_cv_CURSES_CFLAGS + CURSES_LIBS=$pkg_cv_CURSES_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } @@ -26629,21 +26625,21 @@ else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then - PANEL_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "panelw" 2>&1` + PANEL_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "panelw" 2>&1` else - PANEL_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "panelw" 2>&1` + PANEL_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "panelw" 2>&1` fi - # Put the nasty error message in config.log where it belongs - echo "$PANEL_PKG_ERRORS" >&5 + # Put the nasty error message in config.log where it belongs + echo "$PANEL_PKG_ERRORS" >&5 - have_panel=no + have_panel=no elif test $pkg_failed = untried; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } - have_panel=no + have_panel=no else - PANEL_CFLAGS=$pkg_cv_PANEL_CFLAGS - PANEL_LIBS=$pkg_cv_PANEL_LIBS + PANEL_CFLAGS=$pkg_cv_PANEL_CFLAGS + PANEL_LIBS=$pkg_cv_PANEL_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } @@ -26710,21 +26706,21 @@ else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then - CURSES_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "ncurses" 2>&1` + CURSES_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "ncurses" 2>&1` else - CURSES_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "ncurses" 2>&1` + CURSES_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "ncurses" 2>&1` fi - # Put the nasty error message in config.log where it belongs - echo "$CURSES_PKG_ERRORS" >&5 + # Put the nasty error message in config.log where it belongs + echo "$CURSES_PKG_ERRORS" >&5 - have_curses=no + have_curses=no elif test $pkg_failed = untried; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } - have_curses=no + have_curses=no else - CURSES_CFLAGS=$pkg_cv_CURSES_CFLAGS - CURSES_LIBS=$pkg_cv_CURSES_LIBS + CURSES_CFLAGS=$pkg_cv_CURSES_CFLAGS + CURSES_LIBS=$pkg_cv_CURSES_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } @@ -26783,21 +26779,21 @@ else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then - PANEL_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "panel" 2>&1` + PANEL_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "panel" 2>&1` else - PANEL_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "panel" 2>&1` + PANEL_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "panel" 2>&1` fi - # Put the nasty error message in config.log where it belongs - echo "$PANEL_PKG_ERRORS" >&5 + # Put the nasty error message in config.log where it belongs + echo "$PANEL_PKG_ERRORS" >&5 - have_panel=no + have_panel=no elif test $pkg_failed = untried; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } - have_panel=no + have_panel=no else - PANEL_CFLAGS=$pkg_cv_PANEL_CFLAGS - PANEL_LIBS=$pkg_cv_PANEL_LIBS + PANEL_CFLAGS=$pkg_cv_PANEL_CFLAGS + PANEL_LIBS=$pkg_cv_PANEL_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } diff --git a/configure.ac b/configure.ac index bc3d2d0e63b77a..3a55cbc1320393 100644 --- a/configure.ac +++ b/configure.ac @@ -5918,9 +5918,6 @@ AX_C_FLOAT_WORDS_BIGENDIAN( AC_DEFINE([DOUBLE_IS_ARM_MIXED_ENDIAN_IEEE754], [1], [Define if C doubles are 64-bit IEEE 754 binary format, stored in ARM mixed-endian order (byte order 45670123)])], - [wasm*], [AC_DEFINE([DOUBLE_IS_LITTLE_ENDIAN_IEEE754], [1], - [Define if C doubles are 64-bit IEEE 754 binary format, - stored with the least significant byte first])], [AC_MSG_ERROR([m4_normalize([ Unknown float word ordering. You need to manually preset ax_cv_c_float_words_bigendian=no (or yes) From f6b0361c17552197f44be16435e4a5cb4b1d60ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Bidoul?= Date: Wed, 13 Nov 2024 21:58:57 +0100 Subject: [PATCH 173/219] gh-126188: Update bundled pip to 24.3.1 (gh-126805) Update bundled pip to 24.3.1 --- Lib/ensurepip/__init__.py | 2 +- ...ne-any.whl => pip-24.3.1-py3-none-any.whl} | Bin 1815170 -> 1822182 bytes ...-11-13-20-03-18.gh-issue-126188.RJLKk-.rst | 1 + 3 files changed, 2 insertions(+), 1 deletion(-) rename Lib/ensurepip/_bundled/{pip-24.2-py3-none-any.whl => pip-24.3.1-py3-none-any.whl} (84%) create mode 100644 Misc/NEWS.d/next/Library/2024-11-13-20-03-18.gh-issue-126188.RJLKk-.rst diff --git a/Lib/ensurepip/__init__.py b/Lib/ensurepip/__init__.py index 585afc85836c06..645ad998129348 100644 --- a/Lib/ensurepip/__init__.py +++ b/Lib/ensurepip/__init__.py @@ -10,7 +10,7 @@ __all__ = ["version", "bootstrap"] -_PIP_VERSION = "24.2" +_PIP_VERSION = "24.3.1" # Directory of system wheel packages. Some Linux distribution packaging # policies recommend against bundling dependencies. For example, Fedora diff --git a/Lib/ensurepip/_bundled/pip-24.2-py3-none-any.whl b/Lib/ensurepip/_bundled/pip-24.3.1-py3-none-any.whl similarity index 84% rename from Lib/ensurepip/_bundled/pip-24.2-py3-none-any.whl rename to Lib/ensurepip/_bundled/pip-24.3.1-py3-none-any.whl index 542cdd1e7284ae5318664cd6a0f43fe9458d1d6c..5f1d35be6dd56b0e9f5ff0b6f555d43cd1853dda 100644 GIT binary patch delta 242128 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 gx3|@AKQVU@M(P_h086>nKwi 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

B)!DlVs#jyfesv&)w-I0m&Ih%s<2A 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 PfT8F#cr;~5+)Di)oD8oq delta 235207 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 z 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

^2TP_ws~bFCRzM?n0-cJubqPgy>f?cg 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 BiE01< diff --git a/Misc/NEWS.d/next/Library/2024-11-13-20-03-18.gh-issue-126188.RJLKk-.rst b/Misc/NEWS.d/next/Library/2024-11-13-20-03-18.gh-issue-126188.RJLKk-.rst new file mode 100644 index 00000000000000..bb13662e6ae62c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-11-13-20-03-18.gh-issue-126188.RJLKk-.rst @@ -0,0 +1 @@ +Update bundled pip to 24.3.1 From c695e37a3f95c225ee08d1e882d23fa200b5ec34 Mon Sep 17 00:00:00 2001 From: CF Bolz-Tereick Date: Wed, 13 Nov 2024 22:39:10 +0100 Subject: [PATCH 174/219] GH-126606: don't write incomplete pyc files (GH-126627) Co-authored-by: Kirill Podoprigora Co-authored-by: Brett Cannon --- Lib/importlib/_bootstrap_external.py | 6 +++- Lib/test/test_importlib/test_util.py | 32 +++++++++++++++++++ ...-11-09-16-10-22.gh-issue-126066.9zs4m4.rst | 3 ++ 3 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-11-09-16-10-22.gh-issue-126066.9zs4m4.rst diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 1b76328429f63a..fa36159711846f 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -209,7 +209,11 @@ def _write_atomic(path, data, mode=0o666): # We first write data to a temporary file, and then use os.replace() to # perform an atomic rename. with _io.FileIO(fd, 'wb') as file: - file.write(data) + bytes_written = file.write(data) + if bytes_written != len(data): + # Raise an OSError so the 'except' below cleans up the partially + # written file. + raise OSError("os.write() didn't write the full pyc file") _os.replace(path_tmp, path) except OSError: try: diff --git a/Lib/test/test_importlib/test_util.py b/Lib/test/test_importlib/test_util.py index 668042782bdc5f..0bdd1b4b82e544 100644 --- a/Lib/test/test_importlib/test_util.py +++ b/Lib/test/test_importlib/test_util.py @@ -6,12 +6,14 @@ importlib_util = util.import_importlib('importlib.util') import importlib.util +from importlib import _bootstrap_external import os import pathlib import re import string import sys from test import support +from test.support import os_helper import textwrap import types import unittest @@ -775,5 +777,35 @@ def test_complete_multi_phase_init_module(self): self.run_with_own_gil(script) +class MiscTests(unittest.TestCase): + def test_atomic_write_should_notice_incomplete_writes(self): + import _pyio + + oldwrite = os.write + seen_write = False + + truncate_at_length = 100 + + # Emulate an os.write that only writes partial data. + def write(fd, data): + nonlocal seen_write + seen_write = True + return oldwrite(fd, data[:truncate_at_length]) + + # Need to patch _io to be _pyio, so that io.FileIO is affected by the + # os.write patch. + with (support.swap_attr(_bootstrap_external, '_io', _pyio), + support.swap_attr(os, 'write', write)): + with self.assertRaises(OSError): + # Make sure we write something longer than the point where we + # truncate. + content = b'x' * (truncate_at_length * 2) + _bootstrap_external._write_atomic(os_helper.TESTFN, content) + assert seen_write + + with self.assertRaises(OSError): + os.stat(support.os_helper.TESTFN) # Check that the file did not get written. + + if __name__ == '__main__': unittest.main() diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-09-16-10-22.gh-issue-126066.9zs4m4.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-09-16-10-22.gh-issue-126066.9zs4m4.rst new file mode 100644 index 00000000000000..9c0072304ded63 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-09-16-10-22.gh-issue-126066.9zs4m4.rst @@ -0,0 +1,3 @@ +Fix :mod:`importlib` to not write an incomplete .pyc files when a ulimit or some +other operating system mechanism is preventing the write to go through +fully. From fd4b5453df74e249987553b12c14ad75fafa4991 Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Wed, 13 Nov 2024 22:59:32 +0000 Subject: [PATCH 175/219] GH-118289: Fix handling of non-directories in `posixpath.realpath()` (#120127) In strict mode, raise `NotADirectoryError` if we encounter a non-directory while we still have path parts left to process. We use a `part_count` variable rather than `len(rest)` because the `rest` stack also contains markers for unresolved symlinks. --- Lib/posixpath.py | 18 ++++-- Lib/test/test_posixpath.py | 59 +++++++++++++++++++ ...-06-05-19-09-36.gh-issue-118289.moL9_d.rst | 2 + 3 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-06-05-19-09-36.gh-issue-118289.moL9_d.rst diff --git a/Lib/posixpath.py b/Lib/posixpath.py index fccca4e066b76f..db72ded8826056 100644 --- a/Lib/posixpath.py +++ b/Lib/posixpath.py @@ -412,6 +412,10 @@ def _realpath(filename, strict=False, sep=sep, curdir=curdir, pardir=pardir, # very fast way of spelling list(reversed(...)). rest = filename.split(sep)[::-1] + # Number of unprocessed parts in 'rest'. This can differ from len(rest) + # later, because 'rest' might contain markers for unresolved symlinks. + part_count = len(rest) + # The resolved path, which is absolute throughout this function. # Note: getcwd() returns a normalized and symlink-free path. path = sep if filename.startswith(sep) else getcwd() @@ -426,12 +430,13 @@ def _realpath(filename, strict=False, sep=sep, curdir=curdir, pardir=pardir, # by *maxlinks*, this is used instead of *seen* to detect symlink loops. link_count = 0 - while rest: + while part_count: name = rest.pop() if name is None: # resolved symlink target seen[rest.pop()] = path continue + part_count -= 1 if not name or name == curdir: # current dir continue @@ -444,8 +449,11 @@ def _realpath(filename, strict=False, sep=sep, curdir=curdir, pardir=pardir, else: newpath = path + sep + name try: - st = lstat(newpath) - if not stat.S_ISLNK(st.st_mode): + st_mode = lstat(newpath).st_mode + if not stat.S_ISLNK(st_mode): + if strict and part_count and not stat.S_ISDIR(st_mode): + raise OSError(errno.ENOTDIR, os.strerror(errno.ENOTDIR), + newpath) path = newpath continue elif maxlinks is not None: @@ -487,7 +495,9 @@ def _realpath(filename, strict=False, sep=sep, curdir=curdir, pardir=pardir, rest.append(newpath) rest.append(None) # Push the unresolved symlink target parts onto the stack. - rest.extend(target.split(sep)[::-1]) + target_parts = target.split(sep)[::-1] + rest.extend(target_parts) + part_count += len(target_parts) return path diff --git a/Lib/test/test_posixpath.py b/Lib/test/test_posixpath.py index ca5cf42f8fcd71..b39255ebc79ac1 100644 --- a/Lib/test/test_posixpath.py +++ b/Lib/test/test_posixpath.py @@ -695,6 +695,65 @@ def test_realpath_unreadable_symlink(self): os.chmod(ABSTFN, 0o755, follow_symlinks=False) os.unlink(ABSTFN) + @skip_if_ABSTFN_contains_backslash + def test_realpath_nonterminal_file(self): + try: + with open(ABSTFN, 'w') as f: + f.write('test_posixpath wuz ere') + self.assertEqual(realpath(ABSTFN, strict=False), ABSTFN) + self.assertEqual(realpath(ABSTFN, strict=True), ABSTFN) + self.assertEqual(realpath(ABSTFN + "/", strict=False), ABSTFN) + self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/", strict=True) + self.assertEqual(realpath(ABSTFN + "/.", strict=False), ABSTFN) + self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/.", strict=True) + self.assertEqual(realpath(ABSTFN + "/..", strict=False), dirname(ABSTFN)) + self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/..", strict=True) + self.assertEqual(realpath(ABSTFN + "/subdir", strict=False), ABSTFN + "/subdir") + self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/subdir", strict=True) + finally: + os_helper.unlink(ABSTFN) + + @os_helper.skip_unless_symlink + @skip_if_ABSTFN_contains_backslash + def test_realpath_nonterminal_symlink_to_file(self): + try: + with open(ABSTFN + "1", 'w') as f: + f.write('test_posixpath wuz ere') + os.symlink(ABSTFN + "1", ABSTFN) + self.assertEqual(realpath(ABSTFN, strict=False), ABSTFN + "1") + self.assertEqual(realpath(ABSTFN, strict=True), ABSTFN + "1") + self.assertEqual(realpath(ABSTFN + "/", strict=False), ABSTFN + "1") + self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/", strict=True) + self.assertEqual(realpath(ABSTFN + "/.", strict=False), ABSTFN + "1") + self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/.", strict=True) + self.assertEqual(realpath(ABSTFN + "/..", strict=False), dirname(ABSTFN)) + self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/..", strict=True) + self.assertEqual(realpath(ABSTFN + "/subdir", strict=False), ABSTFN + "1/subdir") + self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/subdir", strict=True) + finally: + os_helper.unlink(ABSTFN) + + @os_helper.skip_unless_symlink + @skip_if_ABSTFN_contains_backslash + def test_realpath_nonterminal_symlink_to_symlinks_to_file(self): + try: + with open(ABSTFN + "2", 'w') as f: + f.write('test_posixpath wuz ere') + os.symlink(ABSTFN + "2", ABSTFN + "1") + os.symlink(ABSTFN + "1", ABSTFN) + self.assertEqual(realpath(ABSTFN, strict=False), ABSTFN + "2") + self.assertEqual(realpath(ABSTFN, strict=True), ABSTFN + "2") + self.assertEqual(realpath(ABSTFN + "/", strict=False), ABSTFN + "2") + self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/", strict=True) + self.assertEqual(realpath(ABSTFN + "/.", strict=False), ABSTFN + "2") + self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/.", strict=True) + self.assertEqual(realpath(ABSTFN + "/..", strict=False), dirname(ABSTFN)) + self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/..", strict=True) + self.assertEqual(realpath(ABSTFN + "/subdir", strict=False), ABSTFN + "2/subdir") + self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/subdir", strict=True) + finally: + os_helper.unlink(ABSTFN) + def test_relpath(self): (real_getcwd, os.getcwd) = (os.getcwd, lambda: r"/home/user/bar") try: diff --git a/Misc/NEWS.d/next/Library/2024-06-05-19-09-36.gh-issue-118289.moL9_d.rst b/Misc/NEWS.d/next/Library/2024-06-05-19-09-36.gh-issue-118289.moL9_d.rst new file mode 100644 index 00000000000000..522572e160ba7b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-06-05-19-09-36.gh-issue-118289.moL9_d.rst @@ -0,0 +1,2 @@ +:func:`!posixpath.realpath` now raises :exc:`NotADirectoryError` when *strict* +mode is enabled and a non-directory path with a trailing slash is supplied. From 4ae50615d2beef0f93d904ccbce44bbf7500b94a Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Wed, 13 Nov 2024 15:45:08 -0800 Subject: [PATCH 176/219] Add Savannah to CODEOWNERS for argparse and the JIT (#126814) Add Savannah to CODEOWNERS --- .github/CODEOWNERS | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 9162f9c7bb1576..669844854b2fe5 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -16,6 +16,9 @@ configure* @erlend-aasland @corona10 Makefile.pre.in @erlend-aasland Modules/Setup* @erlend-aasland +# argparse +**/*argparse* @savannahostrowski + # asyncio **/*asyncio* @1st1 @asvetlov @kumaraditya303 @willingc @@ -23,7 +26,7 @@ Modules/Setup* @erlend-aasland **/*context* @1st1 **/*genobject* @markshannon **/*hamt* @1st1 -**/*jit* @brandtbucher +**/*jit* @brandtbucher @savannahostrowski Objects/set* @rhettinger Objects/dict* @methane @markshannon Objects/typevarobject.c @JelleZijlstra From 6a93a1adbb56a64ec6d20e8aab911439998502c9 Mon Sep 17 00:00:00 2001 From: Wulian Date: Thu, 14 Nov 2024 12:58:06 +0800 Subject: [PATCH 177/219] gh-126731: Update outdated project information in `pprint.pp` doc (#126732) --- Doc/library/pprint.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/pprint.rst b/Doc/library/pprint.rst index 1b3498e51f766d..2985f31bacb47a 100644 --- a/Doc/library/pprint.rst +++ b/Doc/library/pprint.rst @@ -267,7 +267,7 @@ let's fetch information about a project from `PyPI `_:: >>> import json >>> import pprint >>> from urllib.request import urlopen - >>> with urlopen('https://pypi.org/pypi/sampleproject/json') as resp: + >>> with urlopen('https://pypi.org/pypi/sampleproject/1.2.0/json') as resp: ... project_info = json.load(resp)['info'] In its basic form, :func:`~pprint.pp` shows the whole object:: From 73e34b680852794d110cd806505b3d74d9d593db Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Thu, 14 Nov 2024 05:01:35 +0000 Subject: [PATCH 178/219] Doc: Recommend shlex.quote alongside pipes removal (#126570) One of the most common reasons I see the old `pipes` module still in use when porting to Python 3.13 is for the undocumented `pipes.quote` function, which can easily be replaced with `shlex.quote`. I think it's worth specifically calling this out, since being directed to the `subprocess` module would be confusing in this case. --- Doc/whatsnew/3.13.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index de4c7fd4c0486b..664b1866172378 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -1568,6 +1568,8 @@ and are now removed: For audio playback, use the :pypi:`pygame` library from PyPI instead. * :mod:`!pipes`: Use the :mod:`subprocess` module instead. + Use :func:`shlex.quote` to replace the undocumented ``pipes.quote`` + function. * :mod:`!sndhdr`: The :pypi:`filetype`, :pypi:`puremagic`, or :pypi:`python-magic` libraries should be used as replacements. From e0692f11650acb6c2eed940eb94650b4703c072e Mon Sep 17 00:00:00 2001 From: John Marshall Date: Thu, 14 Nov 2024 20:47:24 +1300 Subject: [PATCH 179/219] Document that return-less user-defined functions return None (#126769) Co-authored-by: Andrew Svetlov Co-authored-by: Carol Willing --- Doc/reference/expressions.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst index decde0d297cf59..3eaceae41f7eaf 100644 --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -1156,7 +1156,8 @@ a user-defined function: first thing the code block will do is bind the formal parameters to the arguments; this is described in section :ref:`function`. When the code block executes a :keyword:`return` statement, this specifies the return value of the - function call. + function call. If execution reaches the end of the code block without + executing a :keyword:`return` statement, the return value is ``None``. a built-in function or method: .. index:: From ff0ef0a54bef26fc507fbf9b7a6009eb7d3f17f5 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Thu, 14 Nov 2024 09:31:14 +0100 Subject: [PATCH 180/219] gh-123832: Adjust `socket.getaddrinfo` docs for better POSIX compliance (GH-126182) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * gh-123832: Adjust `socket.getaddrinfo` docs for better POSIX compliance This changes nothing changes for CPython supported platforms, but hints how to deal with platforms that stick to the letter of the spec. It also marks `socket.getaddrinfo` as a wrapper around `getaddrinfo(3)`; specifically, workarounds to make the function work consistently across platforms are out of scope in its code. Include wording similar to the POSIX's “by providing options and by limiting the returned information”, which IMO suggests that the hints limit the resulting list compared to the defaults, *but* can be interpreted differently. Details are added in a note. Specifically say that this wraps the underlying C function. So, the details are in OS docs. The “full range of results” bit goes away. Use `AF_UNSPEC` rather than zero for the *family* default, although I don't think a system where it's nonzero would be very usable. Suggest setting proto and/or type (with examples, as the appropriate values aren't obvious). Say why you probably want to do that that on all systems; mention the behavior on the “letter of the spec” systems. Suggest that the results should be tried in order, which is, AFAIK best practice -- see RFC 6724 section 2, and its predecessor from 2003 (which are specific to IP, but indicate how people use this): > Well-behaved applications SHOULD iterate through the list of > addresses returned from `getaddrinfo()` until they find a working address. Co-authored-by: Carol Willing --- Doc/library/socket.rst | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index 0c7b9328648f66..6358d140484c78 100644 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -928,7 +928,9 @@ The :mod:`socket` module also offers various network-related services: .. versionadded:: 3.7 -.. function:: getaddrinfo(host, port, family=0, type=0, proto=0, flags=0) +.. function:: getaddrinfo(host, port, family=AF_UNSPEC, type=0, proto=0, flags=0) + + This function wraps the C function ``getaddrinfo`` of the underlying system. Translate the *host*/*port* argument into a sequence of 5-tuples that contain all the necessary arguments for creating a socket connected to that service. @@ -938,8 +940,10 @@ The :mod:`socket` module also offers various network-related services: and *port*, you can pass ``NULL`` to the underlying C API. The *family*, *type* and *proto* arguments can be optionally specified - in order to narrow the list of addresses returned. Passing zero as a - value for each of these arguments selects the full range of results. + in order to provide options and limit the list of addresses returned. + Pass their default values (:data:`AF_UNSPEC`, 0, and 0, respectively) + to not limit the results. See the note below for details. + The *flags* argument can be one or several of the ``AI_*`` constants, and will influence how results are computed and returned. For example, :const:`AI_NUMERICHOST` will disable domain name resolution @@ -959,6 +963,29 @@ The :mod:`socket` module also offers various network-related services: :const:`AF_INET6`), and is meant to be passed to the :meth:`socket.connect` method. + .. note:: + + If you intend to use results from :func:`!getaddrinfo` to create a socket + (rather than, for example, retrieve *canonname*), + consider limiting the results by *type* (e.g. :data:`SOCK_STREAM` or + :data:`SOCK_DGRAM`) and/or *proto* (e.g. :data:`IPPROTO_TCP` or + :data:`IPPROTO_UDP`) that your application can handle. + + The behavior with default values of *family*, *type*, *proto* + and *flags* is system-specific. + + Many systems (for example, most Linux configurations) will return a sorted + list of all matching addresses. + These addresses should generally be tried in order until a connection succeeds + (possibly tried in parallel, for example, using a `Happy Eyeballs`_ algorithm). + In these cases, limiting the *type* and/or *proto* can help eliminate + unsuccessful or unusable connecton attempts. + + Some systems will, however, only return a single address. + (For example, this was reported on Solaris and AIX configurations.) + On these systems, limiting the *type* and/or *proto* helps ensure that + this address is usable. + .. audit-event:: socket.getaddrinfo host,port,family,type,protocol socket.getaddrinfo The following example fetches address information for a hypothetical TCP @@ -978,6 +1005,8 @@ The :mod:`socket` module also offers various network-related services: for IPv6 multicast addresses, string representing an address will not contain ``%scope_id`` part. +.. _Happy Eyeballs: https://en.wikipedia.org/wiki/Happy_Eyeballs + .. function:: getfqdn([name]) Return a fully qualified domain name for *name*. If *name* is omitted or empty, From 3966d8d626fc9adcdf0663024d4ae6e6be7ef858 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Thu, 14 Nov 2024 09:50:00 +0000 Subject: [PATCH 181/219] GH-117759: Update GC docs for incremental collection (GH-126695) --- InternalDocs/garbage_collector.md | 138 +++++++++++++++++++----------- 1 file changed, 87 insertions(+), 51 deletions(-) diff --git a/InternalDocs/garbage_collector.md b/InternalDocs/garbage_collector.md index d624cf4befd31a..377a846428ae0c 100644 --- a/InternalDocs/garbage_collector.md +++ b/InternalDocs/garbage_collector.md @@ -108,7 +108,7 @@ As is explained later in the [Optimization: reusing fields to save memory](#optimization-reusing-fields-to-save-memory) section, these two extra fields are normally used to keep doubly linked lists of all the objects tracked by the garbage collector (these lists are the GC generations, more on -that in the [Optimization: generations](#Optimization-generations) section), but +that in the [Optimization: incremental collection](#Optimization-incremental-collection) section), but they are also reused to fulfill other purposes when the full doubly linked list structure is not needed as a memory optimization. @@ -351,38 +351,90 @@ follows these steps in order: the reference counts fall to 0, triggering the destruction of all unreachable objects. -Optimization: generations -========================= +Optimization: incremental collection +==================================== -In order to limit the time each garbage collection takes, the GC -implementation for the default build uses a popular optimization: -generations. The main idea behind this concept is the assumption that most -objects have a very short lifespan and can thus be collected soon after their -creation. This has proven to be very close to the reality of many Python +In order to bound the length of each garbage collection pause, the GC implementation +for the default build uses incremental collection with two generations. + +Generational garbage collection takes advantage of what is known as the weak +generational hypothesis: Most objects die young. +This has proven to be very close to the reality of many Python programs as many temporary objects are created and destroyed very quickly. To take advantage of this fact, all container objects are segregated into -three spaces/generations. Every new -object starts in the first generation (generation 0). The previous algorithm is -executed only over the objects of a particular generation and if an object -survives a collection of its generation it will be moved to the next one -(generation 1), where it will be surveyed for collection less often. If -the same object survives another GC round in this new generation (generation 1) -it will be moved to the last generation (generation 2) where it will be -surveyed the least often. - -The GC implementation for the free-threaded build does not use multiple -generations. Every collection operates on the entire heap. +two generations: young and old. Every new object starts in the young generation. +Each garbage collection scans the entire young generation and part of the old generation. + +The time taken to scan the young generation can be controlled by controlling its +size, but the size of the old generation cannot be controlled. +In order to keep pause times down, scanning of the old generation of the heap +occurs in increments. + +To keep track of what has been scanned, the old generation contains two lists: + +* Those objects that have not yet been scanned, referred to as the `pending` list. +* Those objects that have been scanned, referred to as the `visited` list. + +To detect and collect all unreachable objects in the heap, the garbage collector +must scan the whole heap. This whole heap scan is called a full scavenge. + +Increments +---------- + +Each full scavenge is performed in a series of increments. +For each full scavenge, the combined increments will cover the whole heap. + +Each increment is made up of: + +* The young generation +* The old generation's least recently scanned objects +* All objects reachable from those objects that have not yet been scanned this full scavenge + +The surviving objects (those that are not collected) are moved to the back of the +`visited` list in the old generation. + +When a full scavenge starts, no objects in the heap are considered to have been scanned, +so all objects in the old generation must be in the `pending` space. +When all objects in the heap have been scanned a cycle ends, and all objects are moved +to the `pending` list again. To avoid having to traverse the entire list, which list is +`pending` and which is `visited` is determined by a field in the `GCState` struct. +The `visited` and `pending` lists can be swapped by toggling this bit. + +Correctness +----------- + +The [algorithm for identifying cycles](#Identifying-reference-cycles) will find all +unreachable cycles in a list of objects, but will not find any cycles that are +even partly outside of that list. +Therefore, to be guaranteed that a full scavenge will find all unreachable cycles, +each cycle must be fully contained within a single increment. + +To make sure that no partial cycles are included in the increment we perform a +[transitive closure](https://en.wikipedia.org/wiki/Transitive_closure) +over reachable, unscanned objects from the initial increment. +Since the transitive closure of objects reachable from an object must be a (non-strict) +superset of any unreachable cycle including that object, we are guaranteed that a +transitive closure cannot contain any partial cycles. +We can exclude scanned objects, as they must have been reachable when scanned. +If a scanned object becomes part of an unreachable cycle after being scanned, it will +not be collected this at this time, but it will be collected in the next full scavenge. + +> [!NOTE] +> The GC implementation for the free-threaded build does not use incremental collection. +> Every collection operates on the entire heap. In order to decide when to run, the collector keeps track of the number of object allocations and deallocations since the last collection. When the number of -allocations minus the number of deallocations exceeds `threshold_0`, -collection starts. Initially only generation 0 is examined. If generation 0 has -been examined more than `threshold_1` times since generation 1 has been -examined, then generation 1 is examined as well. With generation 2, -things are a bit more complicated; see -[Collecting the oldest generation](#Collecting-the-oldest-generation) for -more information. These thresholds can be examined using the +allocations minus the number of deallocations exceeds `threshold0`, +collection starts. `threshold1` determines the fraction of the old +collection that is included in the increment. +The fraction is inversely proportional to `threshold1`, +as historically a larger `threshold1` meant that old generation +collections were performed less frequently. +`threshold2` is ignored. + +These thresholds can be examined using the [`gc.get_threshold()`](https://docs.python.org/3/library/gc.html#gc.get_threshold) function: @@ -402,8 +454,8 @@ specifically in a generation by calling `gc.collect(generation=NUM)`. ... pass ... - # Move everything to the last generation so it's easier to inspect - # the younger generations. + # Move everything to the old generation so it's easier to inspect + # the young generation. >>> gc.collect() 0 @@ -413,40 +465,24 @@ specifically in a generation by calling `gc.collect(generation=NUM)`. >>> x = MyObj() >>> x.self = x - # Initially the object is in the youngest generation. + # Initially the object is in the young generation. >>> gc.get_objects(generation=0) [..., <__main__.MyObj object at 0x7fbcc12a3400>, ...] # After a collection of the youngest generation the object - # moves to the next generation. + # moves to the old generation. >>> gc.collect(generation=0) 0 >>> gc.get_objects(generation=0) [] >>> gc.get_objects(generation=1) + [] + >>> gc.get_objects(generation=2) [..., <__main__.MyObj object at 0x7fbcc12a3400>, ...] ``` -Collecting the oldest generation --------------------------------- - -In addition to the various configurable thresholds, the GC only triggers a full -collection of the oldest generation if the ratio `long_lived_pending / long_lived_total` -is above a given value (hardwired to 25%). The reason is that, while "non-full" -collections (that is, collections of the young and middle generations) will always -examine roughly the same number of objects (determined by the aforementioned -thresholds) the cost of a full collection is proportional to the total -number of long-lived objects, which is virtually unbounded. Indeed, it has -been remarked that doing a full collection every of object -creations entails a dramatic performance degradation in workloads which consist -of creating and storing lots of long-lived objects (for example, building a large list -of GC-tracked objects would show quadratic performance, instead of linear as -expected). Using the above ratio, instead, yields amortized linear performance -in the total number of objects (the effect of which can be summarized thusly: -"each full garbage collection is more and more costly as the number of objects -grows, but we do fewer and fewer of them"). Optimization: reusing fields to save memory =========================================== @@ -588,9 +624,9 @@ heap. be more difficult. -> [!NOTE] +> [!NOTE] > **Document history** -> +> > Pablo Galindo Salgado - Original author -> +> > Irit Katriel - Convert to Markdown From 1e3497e745d6559d30dbb65c914d19c759d60dc5 Mon Sep 17 00:00:00 2001 From: "RUANG (James Roy)" Date: Thu, 14 Nov 2024 22:04:38 +0800 Subject: [PATCH 182/219] gh-126061: add new functions to `refcounts.dat` (#126788) --- Doc/data/refcounts.dat | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Doc/data/refcounts.dat b/Doc/data/refcounts.dat index 65d48f8bea7de8..6bfcc191b2270b 100644 --- a/Doc/data/refcounts.dat +++ b/Doc/data/refcounts.dat @@ -1284,6 +1284,19 @@ PyLong_FromUnsignedLong:unsignedlong:v:: PyLong_FromVoidPtr:PyObject*::+1: PyLong_FromVoidPtr:void*:p:: +PyLong_IsPositive:int::: +PyLong_IsPositive:PyObject*:obj:0: + +PyLong_IsNegative:int::: +PyLong_IsNegative:PyObject*:obj:0: + +PyLong_IsZero:int::: +PyLong_IsZero:PyObject*:obj:0: + +PyLong_GetSign:int::: +PyLong_GetSign:PyObject*:v:0: +PyLong_GetSign:int*:sign:: + PyMapping_Check:int::: PyMapping_Check:PyObject*:o:0: From 47cbf038850852cdcbe7a404ed7c64542340d58a Mon Sep 17 00:00:00 2001 From: Marc Culler Date: Thu, 14 Nov 2024 12:45:08 -0600 Subject: [PATCH 183/219] gh-124111: Update tkinter for compatibility with Tcl/Tk 9.0.0 (GH-124156) --- Lib/test/test_tkinter/test_misc.py | 8 +- Lib/test/test_tkinter/test_widgets.py | 197 ++++++++++++------ Lib/test/test_tkinter/widget_tests.py | 177 ++++++++-------- Lib/test/test_ttk/test_style.py | 3 +- Lib/test/test_ttk/test_widgets.py | 105 +++++++--- Lib/tkinter/ttk.py | 2 + ...-09-17-10-38-26.gh-issue-124111.Hd53VN.rst | 4 + Modules/_tkinter.c | 5 +- PCbuild/_tkinter.vcxproj | 10 +- PCbuild/build.bat | 4 +- PCbuild/tcltk.props | 18 +- 11 files changed, 338 insertions(+), 195 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-09-17-10-38-26.gh-issue-124111.Hd53VN.rst diff --git a/Lib/test/test_tkinter/test_misc.py b/Lib/test/test_tkinter/test_misc.py index b0b9ed60040443..579ce2af9fa0bf 100644 --- a/Lib/test/test_tkinter/test_misc.py +++ b/Lib/test/test_tkinter/test_misc.py @@ -66,9 +66,10 @@ def test_tk_busy(self): f.tk_busy_forget() self.assertFalse(f.tk_busy_status()) self.assertFalse(f.tk_busy_current()) - with self.assertRaisesRegex(TclError, "can't find busy window"): + errmsg = r"can(no|')t find busy window.*" + with self.assertRaisesRegex(TclError, errmsg): f.tk_busy_configure() - with self.assertRaisesRegex(TclError, "can't find busy window"): + with self.assertRaisesRegex(TclError, errmsg): f.tk_busy_forget() @requires_tk(8, 6, 6) @@ -87,7 +88,8 @@ def test_tk_busy_with_cursor(self): self.assertEqual(f.tk_busy_configure('cursor')[4], 'heart') f.tk_busy_forget() - with self.assertRaisesRegex(TclError, "can't find busy window"): + errmsg = r"can(no|')t find busy window.*" + with self.assertRaisesRegex(TclError, errmsg): f.tk_busy_cget('cursor') def test_tk_setPalette(self): diff --git a/Lib/test/test_tkinter/test_widgets.py b/Lib/test/test_tkinter/test_widgets.py index 9ea764ca2a39d8..f6e77973061956 100644 --- a/Lib/test/test_tkinter/test_widgets.py +++ b/Lib/test/test_tkinter/test_widgets.py @@ -7,9 +7,13 @@ from test.test_tkinter.support import (requires_tk, tk_version, get_tk_patchlevel, widget_eq, AbstractDefaultRootTest) + from test.test_tkinter.widget_tests import ( - add_standard_options, - AbstractWidgetTest, StandardOptionsTests, IntegerSizeTests, PixelSizeTests) + add_configure_tests, + AbstractWidgetTest, + StandardOptionsTests, + IntegerSizeTests, + PixelSizeTests) requires('gui') @@ -20,9 +24,17 @@ def float_round(x): return float(round(x)) - class AbstractToplevelTest(AbstractWidgetTest, PixelSizeTests): - _conv_pad_pixels = False + if tk_version < (9, 0): + _no_round = {'padx', 'pady'} + else: + _no_round = {'borderwidth', 'height', 'highlightthickness', 'padx', + 'pady', 'width'} + if tk_version < (9, 0): + _clipped = {'highlightthickness'} + else: + _clipped = {'borderwidth', 'height', 'highlightthickness', 'padx', + 'pady', 'width'} def test_configure_class(self): widget = self.create() @@ -58,7 +70,7 @@ def test_configure_visual(self): self.assertEqual(widget2['visual'], 'default') -@add_standard_options(StandardOptionsTests) +@add_configure_tests(StandardOptionsTests) class ToplevelTest(AbstractToplevelTest, unittest.TestCase): OPTIONS = ( 'background', 'backgroundimage', 'borderwidth', @@ -101,7 +113,7 @@ def test_configure_use(self): self.assertEqual(widget2['use'], wid) -@add_standard_options(StandardOptionsTests) +@add_configure_tests(StandardOptionsTests) class FrameTest(AbstractToplevelTest, unittest.TestCase): OPTIONS = ( 'background', 'backgroundimage', 'borderwidth', @@ -109,12 +121,17 @@ class FrameTest(AbstractToplevelTest, unittest.TestCase): 'highlightbackground', 'highlightcolor', 'highlightthickness', 'padx', 'pady', 'relief', 'takefocus', 'tile', 'visual', 'width', ) + if tk_version < (9, 0): + _no_round = {'padx', 'pady'} + else: + _no_round = {'borderwidth', 'height', 'highlightthickness', 'padx', + 'pady', 'width'} def create(self, **kwargs): return tkinter.Frame(self.root, **kwargs) -@add_standard_options(StandardOptionsTests) +@add_configure_tests(StandardOptionsTests) class LabelFrameTest(AbstractToplevelTest, unittest.TestCase): OPTIONS = ( 'background', 'borderwidth', @@ -124,6 +141,11 @@ class LabelFrameTest(AbstractToplevelTest, unittest.TestCase): 'labelanchor', 'labelwidget', 'padx', 'pady', 'relief', 'takefocus', 'text', 'visual', 'width', ) + if tk_version < (9, 0): + _no_round = {'padx', 'pady'} + else: + _no_round = {'borderwidth', 'height', 'highlightthickness', 'padx', + 'pady', 'width'} def create(self, **kwargs): return tkinter.LabelFrame(self.root, **kwargs) @@ -141,15 +163,16 @@ def test_configure_labelwidget(self): self.checkParam(widget, 'labelwidget', label, expected='.foo') label.destroy() - +# Label, Button, Checkbutton, Radiobutton, MenuButton class AbstractLabelTest(AbstractWidgetTest, IntegerSizeTests): - _conv_pixels = False - _clip_highlightthickness = tk_version >= (8, 7) - _clip_pad = tk_version >= (8, 7) - _clip_borderwidth = tk_version >= (8, 7) - - -@add_standard_options(StandardOptionsTests) + _rounds_pixels = False + if tk_version < (9, 0): + _clipped = {} + else: + _clipped = {'borderwidth', 'insertborderwidth', 'highlightthickness', + 'padx', 'pady'} + +@add_configure_tests(StandardOptionsTests) class LabelTest(AbstractLabelTest, unittest.TestCase): OPTIONS = ( 'activebackground', 'activeforeground', 'anchor', @@ -165,7 +188,7 @@ def create(self, **kwargs): return tkinter.Label(self.root, **kwargs) -@add_standard_options(StandardOptionsTests) +@add_configure_tests(StandardOptionsTests) class ButtonTest(AbstractLabelTest, unittest.TestCase): OPTIONS = ( 'activebackground', 'activeforeground', 'anchor', @@ -186,7 +209,7 @@ def test_configure_default(self): self.checkEnumParam(widget, 'default', 'active', 'disabled', 'normal') -@add_standard_options(StandardOptionsTests) +@add_configure_tests(StandardOptionsTests) class CheckbuttonTest(AbstractLabelTest, unittest.TestCase): OPTIONS = ( 'activebackground', 'activeforeground', 'anchor', @@ -240,8 +263,7 @@ def test_same_name(self): b2.deselect() self.assertEqual(v.get(), 0) - -@add_standard_options(StandardOptionsTests) +@add_configure_tests(StandardOptionsTests) class RadiobuttonTest(AbstractLabelTest, unittest.TestCase): OPTIONS = ( 'activebackground', 'activeforeground', 'anchor', @@ -264,7 +286,7 @@ def test_configure_value(self): self.checkParams(widget, 'value', 1, 2.3, '', 'any string') -@add_standard_options(StandardOptionsTests) +@add_configure_tests(StandardOptionsTests) class MenubuttonTest(AbstractLabelTest, unittest.TestCase): OPTIONS = ( 'activebackground', 'activeforeground', 'anchor', @@ -277,10 +299,11 @@ class MenubuttonTest(AbstractLabelTest, unittest.TestCase): 'takefocus', 'text', 'textvariable', 'underline', 'width', 'wraplength', ) - _conv_pixels = round - _clip_highlightthickness = True - _clip_pad = True - _clip_borderwidth = False + _rounds_pixels = (tk_version < (9, 0)) + if tk_version < (9, 0): + _clipped = {'highlightthickness', 'padx', 'pady'} + else: + _clipped ={ 'insertborderwidth', 'highlightthickness', 'padx', 'pady'} def create(self, **kwargs): return tkinter.Menubutton(self.root, **kwargs) @@ -298,7 +321,10 @@ def test_configure_image(self): widget = self.create() image = tkinter.PhotoImage(master=self.root, name='image1') self.checkParam(widget, 'image', image, conv=str) - errmsg = 'image "spam" doesn\'t exist' + if tk_version < (9, 0): + errmsg = 'image "spam" doesn\'t exist' + else: + errmsg = 'image "spam" does not exist' with self.assertRaises(tkinter.TclError) as cm: widget['image'] = 'spam' if errmsg is not None: @@ -328,9 +354,15 @@ def test_bad_kwarg(self): with self.assertRaisesRegex(TclError, r"^unknown option -image$"): tkinter.OptionMenu(self.root, None, 'b', image='') - -@add_standard_options(IntegerSizeTests, StandardOptionsTests) +@add_configure_tests(IntegerSizeTests, StandardOptionsTests) class EntryTest(AbstractWidgetTest, unittest.TestCase): + _rounds_pixels = (tk_version < (9, 0)) + if tk_version < (9, 0): + _clipped = {'highlightthickness'} + else: + _clipped = {'highlightthickness', 'borderwidth', 'insertborderwidth', + 'selectborderwidth'} + OPTIONS = ( 'background', 'borderwidth', 'cursor', 'disabledbackground', 'disabledforeground', @@ -355,16 +387,23 @@ def test_configure_disabledbackground(self): def test_configure_insertborderwidth(self): widget = self.create(insertwidth=100) self.checkPixelsParam(widget, 'insertborderwidth', - 0, 1.3, 2.6, 6, -2, '10p') + 0, 1.3, 2.6, 6, '10p') + self.checkParam(widget, 'insertborderwidth', -2) # insertborderwidth is bounded above by a half of insertwidth. - self.checkParam(widget, 'insertborderwidth', 60, expected=100//2) + expected = 100 // 2 if tk_version < (9, 0) else 60 + self.checkParam(widget, 'insertborderwidth', 60, expected=expected) def test_configure_insertwidth(self): widget = self.create() self.checkPixelsParam(widget, 'insertwidth', 1.3, 3.6, '10p') - self.checkParam(widget, 'insertwidth', 0.1, expected=2) - self.checkParam(widget, 'insertwidth', -2, expected=2) - self.checkParam(widget, 'insertwidth', 0.9, expected=1) + if tk_version < (9, 0): + self.checkParam(widget, 'insertwidth', 0.1, expected=2) + self.checkParam(widget, 'insertwidth', -2, expected=2) + self.checkParam(widget, 'insertwidth', 0.9, expected=1) + else: + self.checkParam(widget, 'insertwidth', 0.1) + self.checkParam(widget, 'insertwidth', -2, expected=0) + self.checkParam(widget, 'insertwidth', 0.9) def test_configure_invalidcommand(self): widget = self.create() @@ -422,7 +461,7 @@ def test_selection_methods(self): widget.selection_adjust(0) -@add_standard_options(StandardOptionsTests) +@add_configure_tests(StandardOptionsTests) class SpinboxTest(EntryTest, unittest.TestCase): OPTIONS = ( 'activebackground', 'background', 'borderwidth', @@ -559,7 +598,7 @@ def test_selection_element(self): self.assertEqual(widget.selection_element(), "buttondown") -@add_standard_options(StandardOptionsTests) +@add_configure_tests(StandardOptionsTests) class TextTest(AbstractWidgetTest, unittest.TestCase): OPTIONS = ( 'autoseparators', 'background', 'blockcursor', 'borderwidth', @@ -574,6 +613,9 @@ class TextTest(AbstractWidgetTest, unittest.TestCase): 'tabs', 'tabstyle', 'takefocus', 'undo', 'width', 'wrap', 'xscrollcommand', 'yscrollcommand', ) + _rounds_pixels = (tk_version < (9, 0)) + _no_round = {'selectborderwidth'} + _clipped = {'highlightthickness'} def create(self, **kwargs): return tkinter.Text(self.root, **kwargs) @@ -602,8 +644,10 @@ def test_configure_endline(self): def test_configure_height(self): widget = self.create() self.checkPixelsParam(widget, 'height', 100, 101.2, 102.6, '3c') - self.checkParam(widget, 'height', -100, expected=1) - self.checkParam(widget, 'height', 0, expected=1) + self.checkParam(widget, 'height', -100, + expected=1 if tk_version < (9, 0) else -100) + self.checkParam(widget, 'height', 0, + expected=1 if tk_version < (9, 0) else 0 ) def test_configure_maxundo(self): widget = self.create() @@ -696,7 +740,7 @@ def test_bbox(self): self.assertRaises(TypeError, widget.bbox, '1.1', 'end') -@add_standard_options(PixelSizeTests, StandardOptionsTests) +@add_configure_tests(PixelSizeTests, StandardOptionsTests) class CanvasTest(AbstractWidgetTest, unittest.TestCase): OPTIONS = ( 'background', 'borderwidth', @@ -710,8 +754,15 @@ class CanvasTest(AbstractWidgetTest, unittest.TestCase): 'xscrollcommand', 'xscrollincrement', 'yscrollcommand', 'yscrollincrement', 'width', ) - - _conv_pixels = round + _rounds_pixels = True + if tk_version < (9, 0): + _noround = {} + _clipped = {'highlightthickness'} + else: + _no_round = {'borderwidth', 'height', 'highlightthickness', 'width', + 'xscrollincrement', 'yscrollincrement'} + _clipped = {'borderwidth', 'height', 'highlightthickness', 'width', + 'xscrollincrement', 'yscrollincrement'} _stringify = True def create(self, **kwargs): @@ -953,7 +1004,7 @@ def test_moveto(self): self.assertEqual(y2_2 - y1_2, y2_3 - y1_3) -@add_standard_options(IntegerSizeTests, StandardOptionsTests) +@add_configure_tests(IntegerSizeTests, StandardOptionsTests) class ListboxTest(AbstractWidgetTest, unittest.TestCase): OPTIONS = ( 'activestyle', 'background', 'borderwidth', 'cursor', @@ -965,6 +1016,11 @@ class ListboxTest(AbstractWidgetTest, unittest.TestCase): 'selectmode', 'setgrid', 'state', 'takefocus', 'width', 'xscrollcommand', 'yscrollcommand', ) + _rounds_pixels = (tk_version < (9, 0)) + if tk_version < (9, 0): + _clipped = {'highlightthickness'} + else: + _clipped = { 'borderwidth', 'highlightthickness', 'selectborderwidth'} def create(self, **kwargs): return tkinter.Listbox(self.root, **kwargs) @@ -1091,7 +1147,7 @@ def test_get(self): self.assertRaises(TclError, lb.get, 2.4) -@add_standard_options(PixelSizeTests, StandardOptionsTests) +@add_configure_tests(PixelSizeTests, StandardOptionsTests) class ScaleTest(AbstractWidgetTest, unittest.TestCase): OPTIONS = ( 'activebackground', 'background', 'bigincrement', 'borderwidth', @@ -1102,6 +1158,8 @@ class ScaleTest(AbstractWidgetTest, unittest.TestCase): 'resolution', 'showvalue', 'sliderlength', 'sliderrelief', 'state', 'takefocus', 'tickinterval', 'to', 'troughcolor', 'variable', 'width', ) + _rounds_pixels = (tk_version < (9, 0)) + _clipped = {'highlightthickness'} default_orient = 'vertical' def create(self, **kwargs): @@ -1159,7 +1217,7 @@ def test_configure_to(self): conv=float_round) -@add_standard_options(PixelSizeTests, StandardOptionsTests) +@add_configure_tests(PixelSizeTests, StandardOptionsTests) class ScrollbarTest(AbstractWidgetTest, unittest.TestCase): OPTIONS = ( 'activebackground', 'activerelief', @@ -1170,7 +1228,14 @@ class ScrollbarTest(AbstractWidgetTest, unittest.TestCase): 'repeatdelay', 'repeatinterval', 'takefocus', 'troughcolor', 'width', ) - _conv_pixels = round + _rounds_pixels = True + if tk_version >= (9, 0): + _no_round = {'borderwidth', 'elementborderwidth', 'highlightthickness', + 'width'} + if tk_version < (9, 0): + _clipped = {'highlightthickness'} + else: + _clipped = {'borderwidth', 'highlightthickness', 'width'} _stringify = True default_orient = 'vertical' @@ -1208,7 +1273,7 @@ def test_set(self): self.assertRaises(TypeError, sb.set, 0.6, 0.7, 0.8) -@add_standard_options(StandardOptionsTests) +@add_configure_tests(StandardOptionsTests) class PanedWindowTest(AbstractWidgetTest, unittest.TestCase): OPTIONS = ( 'background', 'borderwidth', 'cursor', @@ -1219,6 +1284,15 @@ class PanedWindowTest(AbstractWidgetTest, unittest.TestCase): 'sashcursor', 'sashpad', 'sashrelief', 'sashwidth', 'showhandle', 'width', ) + _rounds_pixels = True + if tk_version < (9, 0): + _no_round = {'handlesize', 'height', 'proxyborderwidth', 'sashwidth', + 'selectborderwidth', 'width'} + else: + _no_round = {'borderwidth', 'handlepad', 'handlesize', 'height', + 'proxyborderwidth', 'sashpad', 'sashwidth', + 'selectborderwidth', 'width'} + _clipped = {} default_orient = 'horizontal' def create(self, **kwargs): @@ -1347,13 +1421,13 @@ def test_paneconfigure_minsize(self): def test_paneconfigure_padx(self): p, b, c = self.create2() - self.check_paneconfigure(p, b, 'padx', 1.3, 1) + self.check_paneconfigure(p, b, 'padx', 1.3, 1 if tk_version < (9, 0) else 1.3) self.check_paneconfigure_bad(p, b, 'padx', EXPECTED_SCREEN_DISTANCE_ERRMSG.format('badValue')) def test_paneconfigure_pady(self): p, b, c = self.create2() - self.check_paneconfigure(p, b, 'pady', 1.3, 1) + self.check_paneconfigure(p, b, 'pady', 1.3, 1 if tk_version < (9, 0) else 1.3) self.check_paneconfigure_bad(p, b, 'pady', EXPECTED_SCREEN_DISTANCE_ERRMSG.format('badValue')) @@ -1379,17 +1453,17 @@ def test_paneconfigure_width(self): EXPECTED_SCREEN_DISTANCE_OR_EMPTY_ERRMSG.format('badValue')) -@add_standard_options(StandardOptionsTests) +@add_configure_tests(StandardOptionsTests) class MenuTest(AbstractWidgetTest, unittest.TestCase): OPTIONS = ( 'activebackground', 'activeborderwidth', 'activeforeground', - 'activerelief', - 'background', 'borderwidth', 'cursor', + 'activerelief', 'background', 'borderwidth', 'cursor', 'disabledforeground', 'font', 'foreground', 'postcommand', 'relief', 'selectcolor', 'takefocus', 'tearoff', 'tearoffcommand', 'title', 'type', ) - _conv_pixels = False + _rounds_pixels = False + _clipped = {} def create(self, **kwargs): return tkinter.Menu(self.root, **kwargs) @@ -1458,7 +1532,7 @@ def test_entryconfigure_variable(self): self.assertEqual(str(m1.entrycget(1, 'variable')), str(v2)) -@add_standard_options(PixelSizeTests, StandardOptionsTests) +@add_configure_tests(PixelSizeTests, StandardOptionsTests) class MessageTest(AbstractWidgetTest, unittest.TestCase): OPTIONS = ( 'anchor', 'aspect', 'background', 'borderwidth', @@ -1467,11 +1541,12 @@ class MessageTest(AbstractWidgetTest, unittest.TestCase): 'justify', 'padx', 'pady', 'relief', 'takefocus', 'text', 'textvariable', 'width', ) - _conv_pad_pixels = False - if tk_version >= (8, 7): - _conv_pixels = False - _clip_pad = tk_version >= (8, 7) - _clip_borderwidth = tk_version >= (8, 7) + _rounds_pixels = (tk_version < (9, 0)) + _no_round = {'padx', 'pady'} + if tk_version < (9, 0): + _clipped = {'highlightthickness'} + else: + _clipped = {'borderwidth', 'highlightthickness', 'padx', 'pady'} def create(self, **kwargs): return tkinter.Message(self.root, **kwargs) @@ -1482,16 +1557,14 @@ def test_configure_aspect(self): def test_configure_padx(self): widget = self.create() - self.checkPixelsParam(widget, 'padx', 3, 4.4, 5.6, '12m', - conv=self._conv_pad_pixels) - expected = self._default_pixels if self._clip_pad else -2 + self.checkPixelsParam(widget, 'padx', 3, 4.4, 5.6, '12m') + expected = -2 if tk_version < (9, 0) else self._default_pixels self.checkParam(widget, 'padx', -2, expected=expected) def test_configure_pady(self): widget = self.create() - self.checkPixelsParam(widget, 'pady', 3, 4.4, 5.6, '12m', - conv=self._conv_pad_pixels) - expected = self._default_pixels if self._clip_pad else -2 + self.checkPixelsParam(widget, 'pady', 3, 4.4, 5.6, '12m') + expected = -2 if tk_version < (9, 0) else self._default_pixels self.checkParam(widget, 'pady', -2, expected=expected) def test_configure_width(self): diff --git a/Lib/test/test_tkinter/widget_tests.py b/Lib/test/test_tkinter/widget_tests.py index 8ab2f74245095d..ac7fb5977e04fc 100644 --- a/Lib/test/test_tkinter/widget_tests.py +++ b/Lib/test/test_tkinter/widget_tests.py @@ -6,17 +6,16 @@ pixels_conv, tcl_obj_eq) import test.support - _sentinel = object() +# Options which accept all values allowed by Tk_GetPixels +# borderwidth = bd + class AbstractWidgetTest(AbstractTkTest): - _default_pixels = '' if tk_version >= (9, 0) else -1 if tk_version >= (8, 7) else '' - _conv_pixels = round - _conv_pad_pixels = None - _stringify = False - _clip_highlightthickness = True - _clip_pad = False - _clip_borderwidth = False + _default_pixels = '' # Value for unset pixel options. + _rounds_pixels = True # True if some pixel options are rounded. + _no_round = {} # Pixel options which are not rounded nonetheless + _stringify = False # Whether to convert tuples to strings _allow_empty_justify = False @property @@ -44,6 +43,9 @@ def checkParam(self, widget, name, value, *, expected=_sentinel, widget[name] = value if expected is _sentinel: expected = value + if name in self._clipped: + if not isinstance(expected, str): + expected = max(expected, 0) if conv: expected = conv(expected) if self._stringify or not self.wantobjects: @@ -140,14 +142,17 @@ def checkEnumParam(self, widget, name, *values, errmsg = 'bad' + errmsg2 self.checkInvalidParam(widget, name, 'spam', errmsg=errmsg) - def checkPixelsParam(self, widget, name, *values, - conv=None, **kwargs): - if conv is None: - conv = self._conv_pixels + def checkPixelsParam(self, widget, name, *values, conv=None, **kwargs): + if not self._rounds_pixels or name in self._no_round: + conv = False + elif conv != str: + conv = round for value in values: expected = _sentinel conv1 = conv if isinstance(value, str): + if not getattr(self, '_converts_pixels', True): + conv1 = str if conv1 and conv1 is not str: expected = pixels_conv(value) * self.scaling conv1 = round @@ -172,8 +177,12 @@ def checkReliefParam(self, widget, name, *, allow_empty=False): def checkImageParam(self, widget, name): image = tkinter.PhotoImage(master=self.root, name='image1') self.checkParam(widget, name, image, conv=str) + if tk_version < (9, 0): + errmsg = 'image "spam" doesn\'t exist' + else: + errmsg = 'image "spam" does not exist' self.checkInvalidParam(widget, name, 'spam', - errmsg='image "spam" doesn\'t exist') + errmsg=errmsg) widget[name] = '' def checkVariableParam(self, widget, name, var): @@ -215,31 +224,80 @@ def test_keys(self): print('%s.OPTIONS doesn\'t contain "%s"' % (self.__class__.__name__, k)) +class PixelOptionsTests: + """Standard options that accept all formats acceptable to Tk_GetPixels. -class StandardOptionsTests: - STANDARD_OPTIONS = ( - 'activebackground', 'activeborderwidth', 'activeforeground', 'anchor', - 'background', 'bitmap', 'borderwidth', 'compound', 'cursor', - 'disabledforeground', 'exportselection', 'font', 'foreground', - 'highlightbackground', 'highlightcolor', 'highlightthickness', - 'image', 'insertbackground', 'insertborderwidth', - 'insertofftime', 'insertontime', 'insertwidth', - 'jump', 'justify', 'orient', 'padx', 'pady', 'relief', - 'repeatdelay', 'repeatinterval', - 'selectbackground', 'selectborderwidth', 'selectforeground', - 'setgrid', 'takefocus', 'text', 'textvariable', 'troughcolor', - 'underline', 'wraplength', 'xscrollcommand', 'yscrollcommand', - ) - - def test_configure_activebackground(self): - widget = self.create() - self.checkColorParam(widget, 'activebackground') + In addition to numbers, these options can be set with distances + specified as a string consisting of a number followed by a single + character giving the unit of distance. The allowed units are: + millimeters ('m'), centimeters ('c'), inches ('i') or points ('p'). + In Tk 9 a cget call for one of these options returns a Tcl_Obj of + type "pixels", whose string representation is the distance string + passed to configure. + """ + PIXEL_OPTIONS = ('activeborderwidth', 'borderwidth', 'highlightthickness', + 'insertborderwidth', 'insertwidth', 'padx', 'pady', 'selectborderwidth') def test_configure_activeborderwidth(self): widget = self.create() self.checkPixelsParam(widget, 'activeborderwidth', 0, 1.3, 2.9, 6, -2, '10p') + def test_configure_borderwidth(self): + widget = self.create() + self.checkPixelsParam(widget, 'borderwidth', + 0, 1.3, 2.6, 6, '10p') + self.checkParam(widget, 'borderwidth', -2) + if 'bd' in self.OPTIONS: + self.checkPixelsParam(widget, 'bd', 0, 1.3, 2.6, 6, '10p') + self.checkParam(widget, 'bd', -2, expected=expected) + + def test_configure_highlightthickness(self): + widget = self.create() + self.checkPixelsParam(widget, 'highlightthickness', + 0, 1.3, 2.6, 6, '10p') + self.checkParam(widget, 'highlightthickness', -2) + + def test_configure_insertborderwidth(self): + widget = self.create() + self.checkPixelsParam(widget, 'insertborderwidth', + 0, 1.3, 2.6, 6, '10p') + self.checkParam(widget, 'insertborderwidth', -2) + + def test_configure_insertwidth(self): + widget = self.create() + self.checkPixelsParam(widget, 'insertwidth', 1.3, 2.6, -2, '10p') + + def test_configure_padx(self): + widget = self.create() + self.checkPixelsParam(widget, 'padx', 3, 4.4, 5.6, '12m') + self.checkParam(widget, 'padx', -2) + + def test_configure_pady(self): + widget = self.create() + self.checkPixelsParam(widget, 'pady', 3, 4.4, 5.6, '12m') + self.checkParam(widget, 'pady', -2) + + def test_configure_selectborderwidth(self): + widget = self.create() + self.checkPixelsParam(widget, 'selectborderwidth', 1.3, 2.6, -2, '10p') + +class StandardOptionsTests(PixelOptionsTests): + + STANDARD_OPTIONS = ( 'activebackground', 'activeforeground', + 'anchor', 'background', 'bitmap', 'compound', 'cursor', + 'disabledforeground', 'exportselection', 'font', 'foreground', + 'highlightbackground', 'highlightcolor', 'image', + 'insertbackground', 'insertofftime', 'insertontime', 'jump', + 'justify', 'orient', 'relief', 'repeatdelay', 'repeatinterval', + 'selectbackground', 'selectforeground', 'setgrid', 'takefocus', + 'text', 'textvariable', 'troughcolor', 'underline', 'wraplength', + 'xscrollcommand', 'yscrollcommand', ) + PixelOptionsTests.PIXEL_OPTIONS + + def test_configure_activebackground(self): + widget = self.create() + self.checkColorParam(widget, 'activebackground') + def test_configure_activeforeground(self): widget = self.create() self.checkColorParam(widget, 'activeforeground') @@ -277,18 +335,6 @@ def test_configure_bitmap(self): self.checkInvalidParam(widget, 'bitmap', 'spam', errmsg='bitmap "spam" not defined') - def test_configure_borderwidth(self): - widget = self.create() - self.checkPixelsParam(widget, 'borderwidth', - 0, 1.3, 2.6, 6, '10p') - expected = 0 if self._clip_borderwidth else -2 - self.checkParam(widget, 'borderwidth', -2, expected=expected, - conv=self._conv_pixels) - if 'bd' in self.OPTIONS: - self.checkPixelsParam(widget, 'bd', 0, 1.3, 2.6, 6, '10p') - self.checkParam(widget, 'bd', -2, expected=expected, - conv=self._conv_pixels) - def test_configure_compound(self): widget = self.create() self.checkEnumParam(widget, 'compound', @@ -312,8 +358,8 @@ def test_configure_font(self): '-Adobe-Helvetica-Medium-R-Normal--*-120-*-*-*-*-*-*') is_ttk = widget.__class__.__module__ == 'tkinter.ttk' if not is_ttk: - self.checkInvalidParam(widget, 'font', '', - errmsg='font "" doesn\'t exist') + errmsg = 'font "" does ?n[o\']t exist' + self.checkInvalidParam(widget, 'font', '', errmsg=errmsg) def test_configure_foreground(self): widget = self.create() @@ -329,14 +375,6 @@ def test_configure_highlightcolor(self): widget = self.create() self.checkColorParam(widget, 'highlightcolor') - def test_configure_highlightthickness(self): - widget = self.create() - self.checkPixelsParam(widget, 'highlightthickness', - 0, 1.3, 2.6, 6, '10p') - expected = 0 if self._clip_highlightthickness else -2 - self.checkParam(widget, 'highlightthickness', -2, expected=expected, - conv=self._conv_pixels) - def test_configure_image(self): widget = self.create() self.checkImageParam(widget, 'image') @@ -345,11 +383,6 @@ def test_configure_insertbackground(self): widget = self.create() self.checkColorParam(widget, 'insertbackground') - def test_configure_insertborderwidth(self): - widget = self.create() - self.checkPixelsParam(widget, 'insertborderwidth', - 0, 1.3, 2.6, 6, -2, '10p') - def test_configure_insertofftime(self): widget = self.create() self.checkIntegerParam(widget, 'insertofftime', 100) @@ -358,10 +391,6 @@ def test_configure_insertontime(self): widget = self.create() self.checkIntegerParam(widget, 'insertontime', 100) - def test_configure_insertwidth(self): - widget = self.create() - self.checkPixelsParam(widget, 'insertwidth', 1.3, 2.6, -2, '10p') - def test_configure_jump(self): widget = self.create() self.checkBooleanParam(widget, 'jump') @@ -379,22 +408,6 @@ def test_configure_orient(self): self.assertEqual(str(widget['orient']), self.default_orient) self.checkEnumParam(widget, 'orient', 'horizontal', 'vertical') - def test_configure_padx(self): - widget = self.create() - self.checkPixelsParam(widget, 'padx', 3, 4.4, 5.6, '12m', - conv=self._conv_pad_pixels) - expected = 0 if self._clip_pad else -2 - self.checkParam(widget, 'padx', -2, expected=expected, - conv=self._conv_pad_pixels) - - def test_configure_pady(self): - widget = self.create() - self.checkPixelsParam(widget, 'pady', 3, 4.4, 5.6, '12m', - conv=self._conv_pad_pixels) - expected = 0 if self._clip_pad else -2 - self.checkParam(widget, 'pady', -2, expected=expected, - conv=self._conv_pad_pixels) - @requires_tk(8, 7) def test_configure_placeholder(self): widget = self.create() @@ -421,10 +434,6 @@ def test_configure_selectbackground(self): widget = self.create() self.checkColorParam(widget, 'selectbackground') - def test_configure_selectborderwidth(self): - widget = self.create() - self.checkPixelsParam(widget, 'selectborderwidth', 1.3, 2.6, -2, '10p') - def test_configure_selectforeground(self): widget = self.create() self.checkColorParam(widget, 'selectforeground') @@ -534,6 +543,7 @@ def test_configure_variable(self): class IntegerSizeTests: + """ Tests widgets which only accept integral width and height.""" def test_configure_height(self): widget = self.create() self.checkIntegerParam(widget, 'height', 100, -100, 0) @@ -544,6 +554,7 @@ def test_configure_width(self): class PixelSizeTests: + """ Tests widgets which accept screen distances for width and height.""" def test_configure_height(self): widget = self.create() self.checkPixelsParam(widget, 'height', 100, 101.2, 102.6, -100, 0, '3c') @@ -553,7 +564,7 @@ def test_configure_width(self): self.checkPixelsParam(widget, 'width', 402, 403.4, 404.6, -402, 0, '5i') -def add_standard_options(*source_classes): +def add_configure_tests(*source_classes): # This decorator adds test_configure_xxx methods from source classes for # every xxx option in the OPTIONS class attribute if they are not defined # explicitly. diff --git a/Lib/test/test_ttk/test_style.py b/Lib/test/test_ttk/test_style.py index eeaf5de2e303f6..19918772514ad4 100644 --- a/Lib/test/test_ttk/test_style.py +++ b/Lib/test/test_ttk/test_style.py @@ -205,7 +205,8 @@ def test_element_create_from_errors(self): style = self.style with self.assertRaises(IndexError): style.element_create('plain.newelem', 'from') - with self.assertRaisesRegex(TclError, 'theme "spam" doesn\'t exist'): + with self.assertRaisesRegex(TclError, + 'theme "spam" (does not|doesn\'t) exist'): style.element_create('plain.newelem', 'from', 'spam') def test_element_create_image(self): diff --git a/Lib/test/test_ttk/test_widgets.py b/Lib/test/test_ttk/test_widgets.py index 10bec33be617a1..d5620becfa7187 100644 --- a/Lib/test/test_ttk/test_widgets.py +++ b/Lib/test/test_ttk/test_widgets.py @@ -8,7 +8,7 @@ from test.test_tkinter.support import ( AbstractTkTest, requires_tk, tk_version, get_tk_patchlevel, simulate_mouse_click, AbstractDefaultRootTest) -from test.test_tkinter.widget_tests import (add_standard_options, +from test.test_tkinter.widget_tests import (add_configure_tests, AbstractWidgetTest, StandardOptionsTests, IntegerSizeTests, PixelSizeTests) requires('gui') @@ -125,10 +125,11 @@ def test_cb(arg1, **kw): class AbstractToplevelTest(AbstractWidgetTest, PixelSizeTests): - _conv_pixels = False + _rounds_pixels = False + _clipped = {} -@add_standard_options(StandardTtkOptionsTests) +@add_configure_tests(StandardTtkOptionsTests) class FrameTest(AbstractToplevelTest, unittest.TestCase): OPTIONS = ( 'borderwidth', 'class', 'cursor', 'height', @@ -140,7 +141,7 @@ def create(self, **kwargs): return ttk.Frame(self.root, **kwargs) -@add_standard_options(StandardTtkOptionsTests) +@add_configure_tests(StandardTtkOptionsTests) class LabelFrameTest(AbstractToplevelTest, unittest.TestCase): OPTIONS = ( 'borderwidth', 'class', 'cursor', 'height', @@ -168,6 +169,8 @@ def test_configure_labelwidget(self): class AbstractLabelTest(AbstractWidgetTest): _allow_empty_justify = True + _rounds_pixels = False + _clipped = {} def checkImageParam(self, widget, name): image = tkinter.PhotoImage(master=self.root, name='image1') @@ -179,8 +182,11 @@ def checkImageParam(self, widget, name): expected=('image1', 'active', 'image2')) self.checkParam(widget, name, 'image1 active image2', expected=('image1', 'active', 'image2')) - self.checkInvalidParam(widget, name, 'spam', - errmsg='image "spam" doesn\'t exist') + if tk_version < (9, 0): + errmsg = 'image "spam" doesn\'t exist' + else: + errmsg = 'image "spam" does not exist' + self.checkInvalidParam(widget, name, 'spam', errmsg=errmsg) def test_configure_compound(self): values = ('none', 'text', 'image', 'center', 'top', 'bottom', 'left', 'right') @@ -196,7 +202,7 @@ def test_configure_width(self): self.checkParams(widget, 'width', 402, -402, 0) -@add_standard_options(StandardTtkOptionsTests) +@add_configure_tests(StandardTtkOptionsTests) class LabelTest(AbstractLabelTest, unittest.TestCase): OPTIONS = ( 'anchor', 'background', 'borderwidth', @@ -214,7 +220,7 @@ def create(self, **kwargs): test_configure_justify = StandardOptionsTests.test_configure_justify -@add_standard_options(StandardTtkOptionsTests) +@add_configure_tests(StandardTtkOptionsTests) class ButtonTest(AbstractLabelTest, unittest.TestCase): OPTIONS = ( 'class', 'command', 'compound', 'cursor', 'default', @@ -239,7 +245,7 @@ def test_invoke(self): self.assertTrue(success) -@add_standard_options(StandardTtkOptionsTests) +@add_configure_tests(StandardTtkOptionsTests) class CheckbuttonTest(AbstractLabelTest, unittest.TestCase): OPTIONS = ( 'class', 'command', 'compound', 'cursor', @@ -326,7 +332,7 @@ def test_unique_variables2(self): self.assertEqual(len(set(variables)), len(buttons), variables) -@add_standard_options(IntegerSizeTests, StandardTtkOptionsTests) +@add_configure_tests(IntegerSizeTests, StandardTtkOptionsTests) class EntryTest(AbstractWidgetTest, unittest.TestCase): OPTIONS = ( 'background', 'class', 'cursor', @@ -336,6 +342,8 @@ class EntryTest(AbstractWidgetTest, unittest.TestCase): 'show', 'state', 'style', 'takefocus', 'textvariable', 'validate', 'validatecommand', 'width', 'xscrollcommand', ) + _rounds_pixels = False + _clipped = {} # bpo-27313: macOS Tk/Tcl may or may not report 'Entry.field'. IDENTIFY_AS = {'Entry.field', 'textarea'} @@ -371,8 +379,12 @@ def test_bbox(self): self.assertRaises(tkinter.TclError, self.entry.bbox, None) def test_identify(self): + if (tk_version >= (9, 0) and sys.platform == 'darwin' + and isinstance(self.entry, ttk.Combobox)): + self.skipTest('Test does not work on macOS Tk 9.') + # https://core.tcl-lang.org/tk/tktview/8b49e9cfa6 self.entry.pack() - self.entry.update() + self.root.update() self.assertIn(self.entry.identify(5, 5), self.IDENTIFY_AS) self.assertEqual(self.entry.identify(-1, -1), "") @@ -450,7 +462,7 @@ def validate(content): self.assertEqual(self.entry.state(), ()) -@add_standard_options(IntegerSizeTests, StandardTtkOptionsTests) +@add_configure_tests(IntegerSizeTests, StandardTtkOptionsTests) class ComboboxTest(EntryTest, unittest.TestCase): OPTIONS = ( 'background', 'class', 'cursor', 'exportselection', @@ -479,11 +491,14 @@ def _show_drop_down_listbox(self): x, y = width - 5, 5 if sys.platform != 'darwin': # there's no down arrow on macOS self.assertRegex(self.combo.identify(x, y), r'.*downarrow\Z') - self.combo.event_generate('', x=x, y=y) + self.combo.event_generate('', x=x, y=y) self.combo.event_generate('', x=x, y=y) - self.combo.update_idletasks() def test_virtual_event(self): + if (tk_version >= (9, 0) and sys.platform == 'darwin' + and isinstance(self.entry, ttk.Combobox)): + self.skipTest('Test does not work on macOS Tk 9.') + # https://core.tcl-lang.org/tk/tktview/8b49e9cfa6 success = [] self.combo['values'] = [1] @@ -501,6 +516,10 @@ def test_virtual_event(self): self.assertTrue(success) def test_configure_postcommand(self): + if (tk_version >= (9, 0) and sys.platform == 'darwin' + and isinstance(self.entry, ttk.Combobox)): + self.skipTest('Test does not work on macOS Tk 9.') + # https://core.tcl-lang.org/tk/tktview/8b49e9cfa6 success = [] self.combo['postcommand'] = lambda: success.append(True) @@ -576,12 +595,14 @@ def check_get_current(getval, currval): combo2.destroy() -@add_standard_options(IntegerSizeTests, StandardTtkOptionsTests) +@add_configure_tests(IntegerSizeTests, StandardTtkOptionsTests) class PanedWindowTest(AbstractWidgetTest, unittest.TestCase): OPTIONS = ( 'class', 'cursor', 'height', 'orient', 'style', 'takefocus', 'width', ) + _rounds_pixels = False + _clipped = {} def setUp(self): super().setUp() @@ -712,7 +733,7 @@ def test_sashpos(self): self.assertIsInstance(self.paned.sashpos(0), int) -@add_standard_options(StandardTtkOptionsTests) +@add_configure_tests(StandardTtkOptionsTests) class RadiobuttonTest(AbstractLabelTest, unittest.TestCase): OPTIONS = ( 'class', 'command', 'compound', 'cursor', @@ -791,13 +812,14 @@ def test_configure_menu(self): menu.destroy() -@add_standard_options(StandardTtkOptionsTests) +@add_configure_tests(StandardTtkOptionsTests) class ScaleTest(AbstractWidgetTest, unittest.TestCase): OPTIONS = ( 'class', 'command', 'cursor', 'from', 'length', 'orient', 'state', 'style', 'takefocus', 'to', 'value', 'variable', ) - _conv_pixels = False + _rounds_pixels = False + _clipped = {} default_orient = 'horizontal' def setUp(self): @@ -899,7 +921,7 @@ def test_set(self): self.assertRaises(tkinter.TclError, self.scale.set, None) -@add_standard_options(StandardTtkOptionsTests) +@add_configure_tests(StandardTtkOptionsTests) class ProgressbarTest(AbstractWidgetTest, unittest.TestCase): OPTIONS = ( 'anchor', 'class', 'cursor', 'font', 'foreground', 'justify', @@ -907,7 +929,8 @@ class ProgressbarTest(AbstractWidgetTest, unittest.TestCase): 'mode', 'maximum', 'phase', 'text', 'wraplength', 'style', 'takefocus', 'value', 'variable', ) - _conv_pixels = False + _rounds_pixels = False + _clipped = {} _allow_empty_justify = True default_orient = 'horizontal' @@ -952,24 +975,27 @@ def test_configure_value(self): @unittest.skipIf(sys.platform == 'darwin', 'ttk.Scrollbar is special on MacOSX') -@add_standard_options(StandardTtkOptionsTests) +@add_configure_tests(StandardTtkOptionsTests) class ScrollbarTest(AbstractWidgetTest, unittest.TestCase): OPTIONS = ( 'class', 'command', 'cursor', 'orient', 'style', 'takefocus', ) + _rounds_pixels = False + _clipped = {} default_orient = 'vertical' def create(self, **kwargs): return ttk.Scrollbar(self.root, **kwargs) -@add_standard_options(StandardTtkOptionsTests) +@add_configure_tests(StandardTtkOptionsTests) class NotebookTest(AbstractWidgetTest, unittest.TestCase): OPTIONS = ( 'class', 'cursor', 'height', 'padding', 'style', 'takefocus', 'width', ) - if tk_version >= (8, 7): - _conv_pixels = False + _rounds_pixels = (tk_version < (9,0)) + _converts_pixels = False + _clipped = {} def setUp(self): super().setUp() @@ -987,14 +1013,14 @@ def test_configure_height(self): if get_tk_patchlevel(self.root) < (8, 6, 15): self.checkIntegerParam(widget, 'height', 402, -402, 0) else: - self.checkPixelsParam(widget, 'height', '10c', 402, -402, 0, conv=False) + self.checkPixelsParam(widget, 'height', '10c', 402, -402, 0) def test_configure_width(self): widget = self.create() if get_tk_patchlevel(self.root) < (8, 6, 15): self.checkIntegerParam(widget, 'width', 402, -402, 0) else: - self.checkPixelsParam(widget, 'width', '10c', 402, -402, 0, conv=False) + self.checkPixelsParam(widget, 'width', '10c', 402, -402, 0) def test_tab_identifiers(self): self.nb.forget(0) @@ -1160,7 +1186,12 @@ def test_traversal(self): self.nb.select(0) - focus_identify_as = 'focus' if sys.platform != 'darwin' else '' + if sys.platform == 'darwin': + focus_identify_as = '' + elif sys.platform == 'win32': + focus_identify_as = 'focus' + else: + focus_identify_as = 'focus' if tk_version < (9,0) else 'padding' self.assertEqual(self.nb.identify(5, 5), focus_identify_as) simulate_mouse_click(self.nb, 5, 5) self.nb.focus_force() @@ -1193,7 +1224,7 @@ def test_traversal(self): self.assertEqual(self.nb.select(), str(self.child2)) -@add_standard_options(IntegerSizeTests, StandardTtkOptionsTests) +@add_configure_tests(IntegerSizeTests, StandardTtkOptionsTests) class SpinboxTest(EntryTest, unittest.TestCase): OPTIONS = ( 'background', 'class', 'command', 'cursor', 'exportselection', @@ -1370,7 +1401,7 @@ def test_configure_values(self): spin2.destroy() -@add_standard_options(StandardTtkOptionsTests) +@add_configure_tests(StandardTtkOptionsTests) class TreeviewTest(AbstractWidgetTest, unittest.TestCase): OPTIONS = ( 'class', 'columns', 'cursor', 'displaycolumns', @@ -1378,6 +1409,8 @@ class TreeviewTest(AbstractWidgetTest, unittest.TestCase): 'style', 'takefocus', 'titlecolumns', 'titleitems', 'xscrollcommand', 'yscrollcommand', ) + _rounds_pixels = False + _clipped = {} def setUp(self): super().setUp() @@ -1413,8 +1446,10 @@ def test_configure_displaycolumns(self): def test_configure_height(self): widget = self.create() - self.checkPixelsParam(widget, 'height', 100, -100, 0, '3c', conv=False) - self.checkPixelsParam(widget, 'height', 101.2, 102.6, conv=False) + self.checkPixelsParam(widget, 'height', 100, -100, 0, '3c', + conv=False) + self.checkPixelsParam(widget, 'height', 101.2, 102.6, '3c', + conv=False) def test_configure_selectmode(self): widget = self.create() @@ -1936,24 +1971,28 @@ def test_tag_has(self): self.assertEqual(self.tv.tag_has('tag3'), ()) -@add_standard_options(StandardTtkOptionsTests) +@add_configure_tests(StandardTtkOptionsTests) class SeparatorTest(AbstractWidgetTest, unittest.TestCase): OPTIONS = ( 'class', 'cursor', 'orient', 'style', 'takefocus', # 'state'? ) + _rounds_pixels = False + _clipped = {} default_orient = 'horizontal' def create(self, **kwargs): return ttk.Separator(self.root, **kwargs) -@add_standard_options(StandardTtkOptionsTests) +@add_configure_tests(StandardTtkOptionsTests) class SizegripTest(AbstractWidgetTest, unittest.TestCase): OPTIONS = ( 'class', 'cursor', 'style', 'takefocus', # 'state'? ) + _rounds_pixels = False + _clipped = {} def create(self, **kwargs): return ttk.Sizegrip(self.root, **kwargs) diff --git a/Lib/tkinter/ttk.py b/Lib/tkinter/ttk.py index 073b3ae20797c3..8ddb7f97e3b233 100644 --- a/Lib/tkinter/ttk.py +++ b/Lib/tkinter/ttk.py @@ -321,6 +321,8 @@ def _tclobj_to_py(val): elif hasattr(val, 'typename'): # some other (single) Tcl object val = _convert_stringval(val) + if isinstance(val, tuple) and len(val) == 0: + return '' return val def tclobjs_to_py(adict): diff --git a/Misc/NEWS.d/next/Library/2024-09-17-10-38-26.gh-issue-124111.Hd53VN.rst b/Misc/NEWS.d/next/Library/2024-09-17-10-38-26.gh-issue-124111.Hd53VN.rst new file mode 100644 index 00000000000000..aba082a7ac1ad4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-09-17-10-38-26.gh-issue-124111.Hd53VN.rst @@ -0,0 +1,4 @@ +The tkinter module can now be built to use either the new version 9.0.0 of +Tcl/Tk or the latest release 8.6.15 of Tcl/Tk 8. Tcl/Tk 9 includes many +improvements, both to the Tcl language and to the appearance and utility of +the graphical user interface provided by Tk. diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c index b0b70ccb8cc3d3..45897817a56051 100644 --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -325,6 +325,7 @@ typedef struct { const Tcl_ObjType *ListType; const Tcl_ObjType *StringType; const Tcl_ObjType *UTF32StringType; + const Tcl_ObjType *PixelType; } TkappObject; #define Tkapp_Interp(v) (((TkappObject *) (v))->interp) @@ -637,6 +638,7 @@ Tkapp_New(const char *screenName, const char *className, v->ListType = Tcl_GetObjType("list"); v->StringType = Tcl_GetObjType("string"); v->UTF32StringType = Tcl_GetObjType("utf32string"); + v->PixelType = Tcl_GetObjType("pixel"); /* Delete the 'exit' command, which can screw things up */ Tcl_DeleteCommand(v->interp, "exit"); @@ -1236,7 +1238,8 @@ FromObj(TkappObject *tkapp, Tcl_Obj *value) } if (value->typePtr == tkapp->StringType || - value->typePtr == tkapp->UTF32StringType) + value->typePtr == tkapp->UTF32StringType || + value->typePtr == tkapp->PixelType) { return unicodeFromTclObj(tkapp, value); } diff --git a/PCbuild/_tkinter.vcxproj b/PCbuild/_tkinter.vcxproj index 117488a01621cc..87f6005fffc7c5 100644 --- a/PCbuild/_tkinter.vcxproj +++ b/PCbuild/_tkinter.vcxproj @@ -94,6 +94,7 @@ $(tcltkDir)include;%(AdditionalIncludeDirectories) + TCL_WITH_EXTERNAL_TOMMATH;%(PreprocessorDefinitions) WITH_APPINIT;%(PreprocessorDefinitions) Py_TCLTK_DIR="$(tcltkDir.TrimEnd('\').Replace('\', '\\'))";%(PreprocessorDefinitions) @@ -109,9 +110,10 @@ - <_TclTkDLL Include="$(tcltkdir)\bin\$(tclDllName)" /> - <_TclTkDLL Include="$(tcltkdir)\bin\$(tkDllName)" /> - <_TclTkDLL Include="$(tcltkdir)\bin\$(tclZlibDllName)" /> + <_TclTkDLL Include="$(tcltkdir)\bin\$(tclDLLName)" /> + <_TclTkDLL Include="$(tcltkdir)\bin\$(tkDLLName)" /> + <_TclTkDLL Include="$(tcltkdir)\bin\$(tclZlibDLLName)" /> + <_TclTkDLL Include="$(tcltkdir)\bin\$(tommathDLLName)" Condition="$(tommathDLLName) != ''"/> @@ -134,4 +136,4 @@ - \ No newline at end of file + diff --git a/PCbuild/build.bat b/PCbuild/build.bat index abe649553756a7..6d3ce81651ade5 100644 --- a/PCbuild/build.bat +++ b/PCbuild/build.bat @@ -11,7 +11,7 @@ echo.directly to MSBuild may be passed. If the argument contains an '=', the echo.entire argument must be quoted (e.g. `%~nx0 "/p:PlatformToolset=v141"`). echo.Alternatively you can put extra flags for MSBuild in a file named echo.`msbuild.rsp` in the `PCbuild` directory, one flag per line. This file -echo.will be picked automatically by MSBuild. Flags put in this file does not +echo.will be picked automatically by MSBuild. Flags put in this file do not echo.need to be quoted. You can still use environment variables inside the echo.response file. echo. @@ -196,4 +196,4 @@ rem Display the current build version information call "%dir%find_msbuild.bat" %MSBUILD% if ERRORLEVEL 1 (echo Cannot locate MSBuild.exe on PATH or as MSBUILD variable & exit /b 2) %MSBUILD% "%dir%pythoncore.vcxproj" /t:ShowVersionInfo /v:m /nologo %1 %2 %3 %4 %5 %6 %7 %8 %9 -if ERRORLEVEL 1 exit /b 3 \ No newline at end of file +if ERRORLEVEL 1 exit /b 3 diff --git a/PCbuild/tcltk.props b/PCbuild/tcltk.props index b4cb401609d409..d26b36ba98e493 100644 --- a/PCbuild/tcltk.props +++ b/PCbuild/tcltk.props @@ -17,15 +17,21 @@ $(ExternalsDir)tcltk-$(TclVersion)\$(ArchName)\ $(tcltkDir)\bin\tclsh$(TclMajorVersion)$(TclMinorVersion)t.exe $(tcltkDir)\..\win32\bin\tclsh$(TclMajorVersion)$(TclMinorVersion)t.exe + TCL_WITH_EXTERNAL_TOMMATH; - tcl$(TclMajorVersion)$(TclMinorVersion)t$(TclDebugExt).dll - tcl$(TclMajorVersion)$(TclMinorVersion)t$(TclDebugExt).lib - tclsh$(TclMajorVersion)$(TclMinorVersion)t$(TclDebugExt).exe - tk$(TkMajorVersion)$(TkMinorVersion)t$(TclDebugExt).dll - tk$(TkMajorVersion)$(TkMinorVersion)t$(TclDebugExt).lib + t + tcl9 + tcl$(TclMajorVersion)$(TclMinorVersion)$(tcltkSuffix)$(TclDebugExt).dll + tcl$(TclMajorVersion)$(TclMinorVersion)$(tcltkSuffix)$(TclDebugExt).lib + tclsh$(TclMajorVersion)$(TclMinorVersion)$(tcltkSuffix)$(TclDebugExt).exe + $(tkPrefix)tk$(TkMajorVersion)$(TkMinorVersion)$(tcltkSuffix)$(TclDebugExt).dll + $(tkPrefix)tk$(TclMajorVersion)$(TclMinorVersion)$(tcltkSuffix)$(TclDebugExt).lib zlib1.dll - $(tcltkDir)lib\tcl$(TclMajorVersion)$(TclMinorVersion)t$(TclDebugExt).lib;$(tcltkDir)lib\tk$(TkMajorVersion)$(TkMinorVersion)t$(TclDebugExt).lib + libtommath.dll + tommath.lib + $(tcltkDir)lib\$(TclLibName);$(tcltkDir)lib\$(TkLibName); + $(tcltkLib);$(tcltkDir)lib\$(tommathLibName) IX86 AMD64 ARM64 From cae9d9d20f61cdbde0765efa340b6b596c31b67f Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Thu, 14 Nov 2024 20:22:14 +0000 Subject: [PATCH 184/219] GH-126766: `url2pathname()`: handle empty authority section. (#126767) Discard two leading slashes from the beginning of a `file:` URI if they introduce an empty authority section. As a result, file URIs like `///etc/hosts` are correctly parsed as `/etc/hosts`. --- Lib/nturl2path.py | 7 +++---- Lib/test/test_urllib.py | 10 +++++----- Lib/urllib/request.py | 4 ++++ .../2024-11-12-21-43-12.gh-issue-126766.oi2KJ7.rst | 2 ++ 4 files changed, 14 insertions(+), 9 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-11-12-21-43-12.gh-issue-126766.oi2KJ7.rst diff --git a/Lib/nturl2path.py b/Lib/nturl2path.py index 9ecabff21c33e1..255eb2f547c2ce 100644 --- a/Lib/nturl2path.py +++ b/Lib/nturl2path.py @@ -19,10 +19,9 @@ def url2pathname(url): url = url.replace(':', '|') if not '|' in url: # No drive specifier, just convert slashes - if url[:4] == '////': - # path is something like ////host/path/on/remote/host - # convert this to \\host\path\on\remote\host - # (notice halving of slashes at the start of the path) + if url[:3] == '///': + # URL has an empty authority section, so the path begins on the + # third character. url = url[2:] # make sure not to convert quoted slashes :-) return urllib.parse.unquote(url.replace('/', '\\')) diff --git a/Lib/test/test_urllib.py b/Lib/test/test_urllib.py index 66e948fc3a06be..2c53ce3f99e675 100644 --- a/Lib/test/test_urllib.py +++ b/Lib/test/test_urllib.py @@ -1549,7 +1549,7 @@ def test_pathname2url_win(self): self.assertEqual(fn('//?/unc/server/share/dir'), '//server/share/dir') # Round-tripping urls = ['///C:', - '///folder/test/', + '/folder/test/', '///C:/foo/bar/spam.foo'] for url in urls: self.assertEqual(fn(urllib.request.url2pathname(url)), url) @@ -1573,7 +1573,7 @@ def test_url2pathname_win(self): self.assertEqual(fn('/C|//'), 'C:\\\\') self.assertEqual(fn('///C|/path'), 'C:\\path') # No DOS drive - self.assertEqual(fn("///C/test/"), '\\\\\\C\\test\\') + self.assertEqual(fn("///C/test/"), '\\C\\test\\') self.assertEqual(fn("////C/test/"), '\\\\C\\test\\') # DOS drive paths self.assertEqual(fn('C:/path/to/file'), 'C:\\path\\to\\file') @@ -1597,7 +1597,7 @@ def test_url2pathname_win(self): self.assertEqual(fn('//server/share/foo%2fbar'), '\\\\server\\share\\foo/bar') # Round-tripping paths = ['C:', - r'\\\C\test\\', + r'\C\test\\', r'C:\foo\bar\spam.foo'] for path in paths: self.assertEqual(fn(urllib.request.pathname2url(path)), path) @@ -1608,8 +1608,8 @@ def test_url2pathname_posix(self): fn = urllib.request.url2pathname self.assertEqual(fn('/foo/bar'), '/foo/bar') self.assertEqual(fn('//foo/bar'), '//foo/bar') - self.assertEqual(fn('///foo/bar'), '///foo/bar') - self.assertEqual(fn('////foo/bar'), '////foo/bar') + self.assertEqual(fn('///foo/bar'), '/foo/bar') + self.assertEqual(fn('////foo/bar'), '//foo/bar') self.assertEqual(fn('//localhost/foo/bar'), '//localhost/foo/bar') class Utility_Tests(unittest.TestCase): diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py index bc35d8a80e5d03..18a837dd57ed59 100644 --- a/Lib/urllib/request.py +++ b/Lib/urllib/request.py @@ -1656,6 +1656,10 @@ def data_open(self, req): def url2pathname(pathname): """OS-specific conversion from a relative URL of the 'file' scheme to a file system path; not recommended for general use.""" + if pathname[:3] == '///': + # URL has an empty authority section, so the path begins on the + # third character. + pathname = pathname[2:] return unquote(pathname) def pathname2url(pathname): diff --git a/Misc/NEWS.d/next/Library/2024-11-12-21-43-12.gh-issue-126766.oi2KJ7.rst b/Misc/NEWS.d/next/Library/2024-11-12-21-43-12.gh-issue-126766.oi2KJ7.rst new file mode 100644 index 00000000000000..e3936305164883 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-11-12-21-43-12.gh-issue-126766.oi2KJ7.rst @@ -0,0 +1,2 @@ +Fix issue where :func:`urllib.request.url2pathname` failed to discard two +leading slashes introducing an empty authority section. From 9a456383bed52010b90bd491277ea855626a7bba Mon Sep 17 00:00:00 2001 From: "Tomas R." Date: Thu, 14 Nov 2024 23:17:42 +0100 Subject: [PATCH 185/219] gh-126807: pygettext: Do not attempt to extract messages from function definitions. (GH-126808) Fixes a bug where pygettext would attempt to extract a message from a code like this: def _(x): pass This is because pygettext only looks at one token at a time and '_(x)' looks like a function call. However, since 'x' is not a string literal, it would erroneously issue a warning. --- Lib/test/test_tools/test_i18n.py | 33 ++++++++++++++++--- ...-11-13-22-23-36.gh-issue-126807.vpaWuN.rst | 2 ++ Tools/i18n/pygettext.py | 6 ++++ 3 files changed, 36 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Tools-Demos/2024-11-13-22-23-36.gh-issue-126807.vpaWuN.rst diff --git a/Lib/test/test_tools/test_i18n.py b/Lib/test/test_tools/test_i18n.py index 21dead8f943bb7..6f71f0976819f1 100644 --- a/Lib/test/test_tools/test_i18n.py +++ b/Lib/test/test_tools/test_i18n.py @@ -87,17 +87,23 @@ def assert_POT_equal(self, expected, actual): self.maxDiff = None self.assertEqual(normalize_POT_file(expected), normalize_POT_file(actual)) - def extract_docstrings_from_str(self, module_content): - """ utility: return all msgids extracted from module_content """ - filename = 'test_docstrings.py' - with temp_cwd(None) as cwd: + def extract_from_str(self, module_content, *, args=(), strict=True): + """Return all msgids extracted from module_content.""" + filename = 'test.py' + with temp_cwd(None): with open(filename, 'w', encoding='utf-8') as fp: fp.write(module_content) - assert_python_ok('-Xutf8', self.script, '-D', filename) + res = assert_python_ok('-Xutf8', self.script, *args, filename) + if strict: + self.assertEqual(res.err, b'') with open('messages.pot', encoding='utf-8') as fp: data = fp.read() return self.get_msgids(data) + def extract_docstrings_from_str(self, module_content): + """Return all docstrings extracted from module_content.""" + return self.extract_from_str(module_content, args=('--docstrings',), strict=False) + def test_header(self): """Make sure the required fields are in the header, according to: http://www.gnu.org/software/gettext/manual/gettext.html#Header-Entry @@ -344,6 +350,23 @@ def test_calls_in_fstring_with_partially_wrong_expression(self): self.assertNotIn('foo', msgids) self.assertIn('bar', msgids) + def test_function_and_class_names(self): + """Test that function and class names are not mistakenly extracted.""" + msgids = self.extract_from_str(dedent('''\ + def _(x): + pass + + def _(x="foo"): + pass + + async def _(x): + pass + + class _(object): + pass + ''')) + self.assertEqual(msgids, ['']) + def test_pygettext_output(self): """Test that the pygettext output exactly matches snapshots.""" for input_file in DATA_DIR.glob('*.py'): diff --git a/Misc/NEWS.d/next/Tools-Demos/2024-11-13-22-23-36.gh-issue-126807.vpaWuN.rst b/Misc/NEWS.d/next/Tools-Demos/2024-11-13-22-23-36.gh-issue-126807.vpaWuN.rst new file mode 100644 index 00000000000000..310286ce8319ea --- /dev/null +++ b/Misc/NEWS.d/next/Tools-Demos/2024-11-13-22-23-36.gh-issue-126807.vpaWuN.rst @@ -0,0 +1,2 @@ +Fix extraction warnings in :program:`pygettext.py` caused by mistaking +function definitions for function calls. diff --git a/Tools/i18n/pygettext.py b/Tools/i18n/pygettext.py index 3a0b27ba420e7a..0d16e8f7da0071 100755 --- a/Tools/i18n/pygettext.py +++ b/Tools/i18n/pygettext.py @@ -341,6 +341,9 @@ def __waiting(self, ttype, tstring, lineno): if ttype == tokenize.NAME and tstring in ('class', 'def'): self.__state = self.__suiteseen return + if ttype == tokenize.NAME and tstring in ('class', 'def'): + self.__state = self.__ignorenext + return if ttype == tokenize.NAME and tstring in opts.keywords: self.__state = self.__keywordseen return @@ -448,6 +451,9 @@ def __openseen(self, ttype, tstring, lineno): }, file=sys.stderr) self.__state = self.__waiting + def __ignorenext(self, ttype, tstring, lineno): + self.__state = self.__waiting + def __addentry(self, msg, lineno=None, isdocstring=0): if lineno is None: lineno = self.__lineno From 3fecbe9255391be1ac3c3b52dfe0254ee5c665bd Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 15 Nov 2024 01:22:50 +0100 Subject: [PATCH 186/219] gh-126433: Update hacl-star (GH-126791) Retrieve the change: "Lib_Memzero0.c: Fix compiler warning on 32-bit Windows". --- Misc/sbom.spdx.json | 40 +++++++++++------------ Modules/_hacl/Hacl_Hash_Blake2b.c | 3 +- Modules/_hacl/Hacl_Hash_Blake2b_Simd256.c | 3 +- Modules/_hacl/Hacl_Hash_Blake2s.c | 3 +- Modules/_hacl/Hacl_Hash_Blake2s_Simd128.c | 3 +- Modules/_hacl/Hacl_Hash_SHA2.c | 6 ++-- Modules/_hacl/Hacl_Hash_SHA3.c | 3 +- Modules/_hacl/Lib_Memzero0.c | 2 +- Modules/_hacl/internal/Hacl_Hash_SHA2.h | 2 ++ Modules/_hacl/refresh.sh | 2 +- 10 files changed, 37 insertions(+), 30 deletions(-) diff --git a/Misc/sbom.spdx.json b/Misc/sbom.spdx.json index 583ad84e18fd4a..739e005646ba97 100644 --- a/Misc/sbom.spdx.json +++ b/Misc/sbom.spdx.json @@ -300,11 +300,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "a34e821b68ef5334eccf4f729b28bb7bb65b965e" + "checksumValue": "1cd3cda98e0e6882a13a59268b88640c542350fd" }, { "algorithm": "SHA256", - "checksumValue": "4582db9143c0810b98838a5357c577e0b32ae77f3018486159df4e0dfd3fce3c" + "checksumValue": "41a420bc9355e451720e60e9536e66f04dc6e416ca9217c4ab18d827887a2e08" } ], "fileName": "Modules/_hacl/Hacl_Hash_Blake2b.c" @@ -328,11 +328,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "0ffe60c6d5eed5dd222515e820d461d319d16b1f" + "checksumValue": "0ceef306590ec12251db03a31fc08ecba697486d" }, { "algorithm": "SHA256", - "checksumValue": "4804cb3ce68bfdcf98853d6f1d77b4a844a3c2796f776b39770ba327e400d402" + "checksumValue": "1575a23b21319e55e670f74194fc2dfd1777eb5a3816cad43750e03da6e44db9" } ], "fileName": "Modules/_hacl/Hacl_Hash_Blake2b_Simd256.c" @@ -370,11 +370,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "cf035ffeff875bc74345a47373ce25dc408ea9dc" + "checksumValue": "9616a9f8d795d64487bf86a96719f943729621e2" }, { "algorithm": "SHA256", - "checksumValue": "579059b002c45fab0fed6381e85c3f5eaf1d959400ca64b103542ac6c35bade3" + "checksumValue": "5ecde5ddc8ec073cffe64d60e868535d995f33fb0f87f9b50e68bd2a694b7434" } ], "fileName": "Modules/_hacl/Hacl_Hash_Blake2s.c" @@ -398,11 +398,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "9bb53022d158a9c349edb52a8def8aac7d098a4e" + "checksumValue": "5b950ce0a5c8f0c2c56b4ac96e1943b504255d45" }, { "algorithm": "SHA256", - "checksumValue": "2abde0c6b5da0402e91b4bedfe786c24b908fbdc04e08e74651c7624729254d9" + "checksumValue": "5a5f5d8e376dc30d89fd6c6c435157fe9ffa5308030e7abb1256afaee0765536" } ], "fileName": "Modules/_hacl/Hacl_Hash_Blake2s_Simd128.c" @@ -496,11 +496,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "f2aa3ed6acce621c162bc3a0592780ce5aa3bc4d" + "checksumValue": "b0aa8810339adb09623ffa429246b4324fac4565" }, { "algorithm": "SHA256", - "checksumValue": "30638efb75c8b185bb09c3df6977e3f3c5d21a1e696218cf7ade6bc4d5201b31" + "checksumValue": "2288f8f860efe80eed4f1e14ef570079b7459aeb41f87e94e691d7cf5e0e7adb" } ], "fileName": "Modules/_hacl/Hacl_Hash_SHA2.c" @@ -524,11 +524,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "fc2c3ef83a71bef42eb3f73b78e4ef6642a4634e" + "checksumValue": "ef374b9d0951ebb38006af944dd4b38a6cf3abb2" }, { "algorithm": "SHA256", - "checksumValue": "e4f3ed9d1e8f661482cbd2d04b197e15cc3b698c5ef2ddedf0eb65df320dbbc4" + "checksumValue": "164df19f229143006c5f9a3c0bd771415f152bfbc7efb61c337fa0f903003eb3" } ], "fileName": "Modules/_hacl/Hacl_Hash_SHA3.c" @@ -566,11 +566,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "47ce34375d43a27312e1fffb96b8965610b05855" + "checksumValue": "2e08072c0c57dac02b67f3f71d77068c537ac02e" }, { "algorithm": "SHA256", - "checksumValue": "8affd767d7644150064d8bccd05d7bf4c4ae41fd4bb5bf5b8e943eabf09f3d74" + "checksumValue": "e69fd3e84f77873ecb414f5300761b686321d01f5710ccf2517765236b08fc25" } ], "fileName": "Modules/_hacl/Lib_Memzero0.c" @@ -748,11 +748,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "0018e084339058dd454b4e49d10d236b4f896bf8" + "checksumValue": "2e9ae174142fc491f20567ab8b5c08cef9b07cfe" }, { "algorithm": "SHA256", - "checksumValue": "10e959a92b3288a6165a404c8fae2bbcd7fb00a9abbae2b7809fa55d6fe9068d" + "checksumValue": "07100964adcf4b5f8bd4773e25f475b34cd180b90df8b1c0052e55c008b7cc49" } ], "fileName": "Modules/_hacl/internal/Hacl_Hash_SHA2.h" @@ -1640,14 +1640,14 @@ "checksums": [ { "algorithm": "SHA256", - "checksumValue": "935ae51d0ff0bf1403f0ecc1ff02b8f685d09053618558c07fbe4bd2abbc5dd1" + "checksumValue": "40de5297b032d2676fc0039049b4e8dab1f2730eebb5ecff6a40c04fa0356339" } ], - "downloadLocation": "https://github.com/hacl-star/hacl-star/archive/315a9e491d2bc347b9dae99e0ea506995ea84d9d.zip", + "downloadLocation": "https://github.com/hacl-star/hacl-star/archive/f218923ef2417d963d7efc7951593ae6aef613f7.zip", "externalRefs": [ { "referenceCategory": "SECURITY", - "referenceLocator": "cpe:2.3:a:hacl-star:hacl-star:315a9e491d2bc347b9dae99e0ea506995ea84d9d:*:*:*:*:*:*:*", + "referenceLocator": "cpe:2.3:a:hacl-star:hacl-star:f218923ef2417d963d7efc7951593ae6aef613f7:*:*:*:*:*:*:*", "referenceType": "cpe23Type" } ], @@ -1655,7 +1655,7 @@ "name": "hacl-star", "originator": "Organization: HACL* Developers", "primaryPackagePurpose": "SOURCE", - "versionInfo": "315a9e491d2bc347b9dae99e0ea506995ea84d9d" + "versionInfo": "f218923ef2417d963d7efc7951593ae6aef613f7" }, { "SPDXID": "SPDXRef-PACKAGE-macholib", diff --git a/Modules/_hacl/Hacl_Hash_Blake2b.c b/Modules/_hacl/Hacl_Hash_Blake2b.c index cd3b9777e09f6c..1bab75e6aaf2ab 100644 --- a/Modules/_hacl/Hacl_Hash_Blake2b.c +++ b/Modules/_hacl/Hacl_Hash_Blake2b.c @@ -1029,7 +1029,8 @@ Hacl_Hash_Blake2b_reset_with_key_and_params( uint8_t *k ) { - index_of_state(s); + Hacl_Hash_Blake2b_index i1 = index_of_state(s); + KRML_MAYBE_UNUSED_VAR(i1); reset_raw(s, ((Hacl_Hash_Blake2b_params_and_key){ .fst = p, .snd = k })); } diff --git a/Modules/_hacl/Hacl_Hash_Blake2b_Simd256.c b/Modules/_hacl/Hacl_Hash_Blake2b_Simd256.c index 92b2e8f539041b..19234ab9d7f9b2 100644 --- a/Modules/_hacl/Hacl_Hash_Blake2b_Simd256.c +++ b/Modules/_hacl/Hacl_Hash_Blake2b_Simd256.c @@ -855,7 +855,8 @@ Hacl_Hash_Blake2b_Simd256_reset_with_key_and_params( uint8_t *k ) { - index_of_state(s); + Hacl_Hash_Blake2b_index i1 = index_of_state(s); + KRML_MAYBE_UNUSED_VAR(i1); reset_raw(s, ((Hacl_Hash_Blake2b_params_and_key){ .fst = p, .snd = k })); } diff --git a/Modules/_hacl/Hacl_Hash_Blake2s.c b/Modules/_hacl/Hacl_Hash_Blake2s.c index e5e0ecd0bfde7e..ceb7385072e048 100644 --- a/Modules/_hacl/Hacl_Hash_Blake2s.c +++ b/Modules/_hacl/Hacl_Hash_Blake2s.c @@ -1011,7 +1011,8 @@ Hacl_Hash_Blake2s_reset_with_key_and_params( uint8_t *k ) { - index_of_state(s); + Hacl_Hash_Blake2b_index i1 = index_of_state(s); + KRML_MAYBE_UNUSED_VAR(i1); reset_raw(s, ((Hacl_Hash_Blake2b_params_and_key){ .fst = p, .snd = k })); } diff --git a/Modules/_hacl/Hacl_Hash_Blake2s_Simd128.c b/Modules/_hacl/Hacl_Hash_Blake2s_Simd128.c index f675a7f14f192f..3b68783bfad9b4 100644 --- a/Modules/_hacl/Hacl_Hash_Blake2s_Simd128.c +++ b/Modules/_hacl/Hacl_Hash_Blake2s_Simd128.c @@ -842,7 +842,8 @@ Hacl_Hash_Blake2s_Simd128_reset_with_key_and_params( uint8_t *k ) { - index_of_state(s); + Hacl_Hash_Blake2b_index i1 = index_of_state(s); + KRML_MAYBE_UNUSED_VAR(i1); reset_raw(s, ((Hacl_Hash_Blake2b_params_and_key){ .fst = p, .snd = k })); } diff --git a/Modules/_hacl/Hacl_Hash_SHA2.c b/Modules/_hacl/Hacl_Hash_SHA2.c index 4b6af5fc78c680..cc930bbc89e8ad 100644 --- a/Modules/_hacl/Hacl_Hash_SHA2.c +++ b/Modules/_hacl/Hacl_Hash_SHA2.c @@ -211,7 +211,7 @@ void Hacl_Hash_SHA2_sha224_init(uint32_t *hash) os[i] = x;); } -static inline void sha224_update_nblocks(uint32_t len, uint8_t *b, uint32_t *st) +void Hacl_Hash_SHA2_sha224_update_nblocks(uint32_t len, uint8_t *b, uint32_t *st) { Hacl_Hash_SHA2_sha256_update_nblocks(len, b, st); } @@ -825,7 +825,7 @@ void Hacl_Hash_SHA2_digest_224(Hacl_Streaming_MD_state_32 *state, uint8_t *outpu } uint8_t *buf_last = buf_1 + r - ite; uint8_t *buf_multi = buf_1; - sha224_update_nblocks(0U, buf_multi, tmp_block_state); + Hacl_Hash_SHA2_sha224_update_nblocks(0U, buf_multi, tmp_block_state); uint64_t prev_len_last = total_len - (uint64_t)r; Hacl_Hash_SHA2_sha224_update_last(prev_len_last + (uint64_t)r, r, buf_last, tmp_block_state); Hacl_Hash_SHA2_sha224_finish(tmp_block_state, output); @@ -847,7 +847,7 @@ void Hacl_Hash_SHA2_hash_224(uint8_t *output, uint8_t *input, uint32_t input_len Hacl_Hash_SHA2_sha224_init(st); uint32_t rem = input_len % 64U; uint64_t len_ = (uint64_t)input_len; - sha224_update_nblocks(input_len, ib, st); + Hacl_Hash_SHA2_sha224_update_nblocks(input_len, ib, st); uint32_t rem1 = input_len % 64U; uint8_t *b0 = ib; uint8_t *lb = b0 + input_len - rem1; diff --git a/Modules/_hacl/Hacl_Hash_SHA3.c b/Modules/_hacl/Hacl_Hash_SHA3.c index 9cf5abb330b180..b964e1d9c0aa69 100644 --- a/Modules/_hacl/Hacl_Hash_SHA3.c +++ b/Modules/_hacl/Hacl_Hash_SHA3.c @@ -251,7 +251,8 @@ Hacl_Hash_SHA3_update_multi_sha3( uint8_t *bl0 = b_; uint8_t *uu____0 = b0 + i * block_len(a); memcpy(bl0, uu____0, block_len(a) * sizeof (uint8_t)); - block_len(a); + uint32_t unused = block_len(a); + KRML_MAYBE_UNUSED_VAR(unused); absorb_inner_32(b_, s); } } diff --git a/Modules/_hacl/Lib_Memzero0.c b/Modules/_hacl/Lib_Memzero0.c index 5b1a2f7797db76..5c269d231de82f 100644 --- a/Modules/_hacl/Lib_Memzero0.c +++ b/Modules/_hacl/Lib_Memzero0.c @@ -36,7 +36,7 @@ void Lib_Memzero0_memzero0(void *dst, uint64_t len) { size_t len_ = (size_t) len; #ifdef _WIN32 - SecureZeroMemory(dst, len); + SecureZeroMemory(dst, len_); #elif defined(__APPLE__) && defined(__MACH__) memset_s(dst, len_, 0, len_); #elif (defined(__linux__) && !defined(LINUX_NO_EXPLICIT_BZERO)) || defined(__FreeBSD__) diff --git a/Modules/_hacl/internal/Hacl_Hash_SHA2.h b/Modules/_hacl/internal/Hacl_Hash_SHA2.h index 0127f4373fb1a1..cb60f9e9bd4df6 100644 --- a/Modules/_hacl/internal/Hacl_Hash_SHA2.h +++ b/Modules/_hacl/internal/Hacl_Hash_SHA2.h @@ -123,6 +123,8 @@ void Hacl_Hash_SHA2_sha256_finish(uint32_t *st, uint8_t *h); void Hacl_Hash_SHA2_sha224_init(uint32_t *hash); +void Hacl_Hash_SHA2_sha224_update_nblocks(uint32_t len, uint8_t *b, uint32_t *st); + void Hacl_Hash_SHA2_sha224_update_last(uint64_t totlen, uint32_t len, uint8_t *b, uint32_t *st); diff --git a/Modules/_hacl/refresh.sh b/Modules/_hacl/refresh.sh index 6234fea9f17bc7..4147ab302fe146 100755 --- a/Modules/_hacl/refresh.sh +++ b/Modules/_hacl/refresh.sh @@ -22,7 +22,7 @@ fi # Update this when updating to a new version after verifying that the changes # the update brings in are good. -expected_hacl_star_rev=315a9e491d2bc347b9dae99e0ea506995ea84d9d +expected_hacl_star_rev=f218923ef2417d963d7efc7951593ae6aef613f7 hacl_dir="$(realpath "$1")" cd "$(dirname "$0")" From d9e251223e8314ca726fc0be8b834362184b0aad Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Fri, 15 Nov 2024 11:03:38 +0300 Subject: [PATCH 187/219] gh-103951: enable optimization for fast attribute access on module subclasses (GH-126264) Co-authored-by: Nicolas Tessore --- .../2024-11-01-09-58-06.gh-issue-103951.6qduwj.rst | 2 ++ Python/bytecodes.c | 2 +- Python/executor_cases.c.h | 2 +- Python/generated_cases.c.h | 2 +- Python/specialize.c | 2 +- 5 files changed, 6 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-11-01-09-58-06.gh-issue-103951.6qduwj.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-01-09-58-06.gh-issue-103951.6qduwj.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-01-09-58-06.gh-issue-103951.6qduwj.rst new file mode 100644 index 00000000000000..39b54e0b72556e --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-01-09-58-06.gh-issue-103951.6qduwj.rst @@ -0,0 +1,2 @@ +Relax optimization requirements to allow fast attribute access to module +subclasses. diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 04983fd861ec59..c85b49842daf44 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2132,7 +2132,7 @@ dummy_func( op(_CHECK_ATTR_MODULE, (dict_version/2, owner -- owner)) { PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); - DEOPT_IF(!PyModule_CheckExact(owner_o)); + DEOPT_IF(Py_TYPE(owner_o)->tp_getattro != PyModule_Type.tp_getattro); PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner_o)->md_dict; assert(dict != NULL); DEOPT_IF(dict->ma_keys->dk_version != dict_version); diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 494ace1bd85822..2c2a09adf281a7 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -2602,7 +2602,7 @@ owner = stack_pointer[-1]; uint32_t dict_version = (uint32_t)CURRENT_OPERAND0(); PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); - if (!PyModule_CheckExact(owner_o)) { + if (Py_TYPE(owner_o)->tp_getattro != PyModule_Type.tp_getattro) { UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 77bf6ad3781f17..15308d6f1f7146 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -5561,7 +5561,7 @@ owner = stack_pointer[-1]; uint32_t dict_version = read_u32(&this_instr[2].cache); PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); - DEOPT_IF(!PyModule_CheckExact(owner_o), LOAD_ATTR); + DEOPT_IF(Py_TYPE(owner_o)->tp_getattro != PyModule_Type.tp_getattro, LOAD_ATTR); PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner_o)->md_dict; assert(dict != NULL); DEOPT_IF(dict->ma_keys->dk_version != dict_version, LOAD_ATTR); diff --git a/Python/specialize.c b/Python/specialize.c index 0699e7be5e6b9c..4c8cf8534b3dc7 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -1219,7 +1219,7 @@ _Py_Specialize_LoadAttr(_PyStackRef owner_st, _Py_CODEUNIT *instr, PyObject *nam SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OTHER); fail = true; } - else if (PyModule_CheckExact(owner)) { + else if (Py_TYPE(owner)->tp_getattro == PyModule_Type.tp_getattro) { fail = specialize_module_load_attr(owner, instr, name); } else if (PyType_Check(owner)) { From c0f045f7fd3bb7ccf9828f4bfad55347d097fd41 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 15 Nov 2024 08:59:01 +0000 Subject: [PATCH 188/219] GH-124567: Reduce overhead of debug build for GC. Should help CI performance (GH-126777) --- Python/gc.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Python/gc.c b/Python/gc.c index 028657eb8999c1..fe81ca5989c621 100644 --- a/Python/gc.c +++ b/Python/gc.c @@ -23,6 +23,10 @@ typedef struct _gc_runtime_state GCState; # define GC_DEBUG #endif +// Define this when debugging the GC +// #define GC_EXTRA_DEBUG + + #define GC_NEXT _PyGCHead_NEXT #define GC_PREV _PyGCHead_PREV @@ -421,6 +425,11 @@ validate_list(PyGC_Head *head, enum flagstates flags) assert(prev == GC_PREV(head)); } +#else +#define validate_list(x, y) do{}while(0) +#endif + +#ifdef GC_EXTRA_DEBUG static void validate_old(GCState *gcstate) { @@ -464,7 +473,6 @@ gc_list_validate_space(PyGC_Head *head, int space) { } #else -#define validate_list(x, y) do{}while(0) #define validate_old(g) do{}while(0) #define validate_consistent_old_space(l) do{}while(0) #define gc_list_validate_space(l, s) do{}while(0) From 8717f792f7cc343599dc60daf80630cceb66145b Mon Sep 17 00:00:00 2001 From: George Alexopoulos Date: Fri, 15 Nov 2024 12:05:51 +0200 Subject: [PATCH 189/219] gh-126554: ctypes: Correctly handle NULL dlsym values (GH-126555) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For dlsym(), a return value of NULL does not necessarily indicate an error [1]. Therefore, to avoid using stale (or NULL) dlerror() values, we must: 1. clear the previous error state by calling dlerror() 2. call dlsym() 3. call dlerror() If the return value of dlerror() is not NULL, an error occured. In ctypes we choose to treat a NULL return value from dlsym() as a "not found" error. This is the same as the fallback message we use on Windows, Cygwin or when getting/formatting the error reason fails. [1]: https://man7.org/linux/man-pages/man3/dlsym.3.html Signed-off-by: Georgios Alexopoulos Signed-off-by: Georgios Alexopoulos Co-authored-by: Peter Bierma Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> Co-authored-by: Petr Viktorin --- Lib/test/test_ctypes/test_dlerror.py | 123 ++++++++++++++++++ ...-11-07-20-24-58.gh-issue-126554.ri12eb.rst | 2 + Modules/_ctypes/_ctypes.c | 90 +++++++++---- Modules/_ctypes/callproc.c | 36 ++++- 4 files changed, 220 insertions(+), 31 deletions(-) create mode 100644 Lib/test/test_ctypes/test_dlerror.py create mode 100644 Misc/NEWS.d/next/C_API/2024-11-07-20-24-58.gh-issue-126554.ri12eb.rst diff --git a/Lib/test/test_ctypes/test_dlerror.py b/Lib/test/test_ctypes/test_dlerror.py new file mode 100644 index 00000000000000..4441e30cd7a2a7 --- /dev/null +++ b/Lib/test/test_ctypes/test_dlerror.py @@ -0,0 +1,123 @@ +import os +import sys +import unittest +import platform + +FOO_C = r""" +#include + +/* This is a 'GNU indirect function' (IFUNC) that will be called by + dlsym() to resolve the symbol "foo" to an address. Typically, such + a function would return the address of an actual function, but it + can also just return NULL. For some background on IFUNCs, see + https://willnewton.name/uncategorized/using-gnu-indirect-functions. + + Adapted from Michael Kerrisk's answer: https://stackoverflow.com/a/53590014. +*/ + +asm (".type foo STT_GNU_IFUNC"); + +void *foo(void) +{ + write($DESCRIPTOR, "OK", 2); + return NULL; +} +""" + + +@unittest.skipUnless(sys.platform.startswith('linux'), + 'Test only valid for Linux') +class TestNullDlsym(unittest.TestCase): + """GH-126554: Ensure that we catch NULL dlsym return values + + In rare cases, such as when using GNU IFUNCs, dlsym(), + the C function that ctypes' CDLL uses to get the address + of symbols, can return NULL. + + The objective way of telling if an error during symbol + lookup happened is to call glibc's dlerror() and check + for a non-NULL return value. + + However, there can be cases where dlsym() returns NULL + and dlerror() is also NULL, meaning that glibc did not + encounter any error. + + In the case of ctypes, we subjectively treat that as + an error, and throw a relevant exception. + + This test case ensures that we correctly enforce + this 'dlsym returned NULL -> throw Error' rule. + """ + + def test_null_dlsym(self): + import subprocess + import tempfile + + # To avoid ImportErrors on Windows, where _ctypes does not have + # dlopen and dlsym, + # import here, i.e., inside the test function. + # The skipUnless('linux') decorator ensures that we're on linux + # if we're executing these statements. + from ctypes import CDLL, c_int + from _ctypes import dlopen, dlsym + + retcode = subprocess.call(["gcc", "--version"], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL) + if retcode != 0: + self.skipTest("gcc is missing") + + pipe_r, pipe_w = os.pipe() + self.addCleanup(os.close, pipe_r) + self.addCleanup(os.close, pipe_w) + + with tempfile.TemporaryDirectory() as d: + # Create a C file with a GNU Indirect Function (FOO_C) + # and compile it into a shared library. + srcname = os.path.join(d, 'foo.c') + dstname = os.path.join(d, 'libfoo.so') + with open(srcname, 'w') as f: + f.write(FOO_C.replace('$DESCRIPTOR', str(pipe_w))) + args = ['gcc', '-fPIC', '-shared', '-o', dstname, srcname] + p = subprocess.run(args, capture_output=True) + + if p.returncode != 0: + # IFUNC is not supported on all architectures. + if platform.machine() == 'x86_64': + # It should be supported here. Something else went wrong. + p.check_returncode() + else: + # IFUNC might not be supported on this machine. + self.skipTest(f"could not compile indirect function: {p}") + + # Case #1: Test 'PyCFuncPtr_FromDll' from Modules/_ctypes/_ctypes.c + L = CDLL(dstname) + with self.assertRaisesRegex(AttributeError, "function 'foo' not found"): + # Try accessing the 'foo' symbol. + # It should resolve via dlsym() to NULL, + # and since we subjectively treat NULL + # addresses as errors, we should get + # an error. + L.foo + + # Assert that the IFUNC was called + self.assertEqual(os.read(pipe_r, 2), b'OK') + + # Case #2: Test 'CDataType_in_dll_impl' from Modules/_ctypes/_ctypes.c + with self.assertRaisesRegex(ValueError, "symbol 'foo' not found"): + c_int.in_dll(L, "foo") + + # Assert that the IFUNC was called + self.assertEqual(os.read(pipe_r, 2), b'OK') + + # Case #3: Test 'py_dl_sym' from Modules/_ctypes/callproc.c + L = dlopen(dstname) + with self.assertRaisesRegex(OSError, "symbol 'foo' not found"): + dlsym(L, "foo") + + # Assert that the IFUNC was called + self.assertEqual(os.read(pipe_r, 2), b'OK') + + +if __name__ == "__main__": + unittest.main() diff --git a/Misc/NEWS.d/next/C_API/2024-11-07-20-24-58.gh-issue-126554.ri12eb.rst b/Misc/NEWS.d/next/C_API/2024-11-07-20-24-58.gh-issue-126554.ri12eb.rst new file mode 100644 index 00000000000000..6af89c7d4709ec --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2024-11-07-20-24-58.gh-issue-126554.ri12eb.rst @@ -0,0 +1,2 @@ +Fix error handling in :class:`ctypes.CDLL` objects +which could result in a crash in rare situations. diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index eae69e484e1660..34529bce496d88 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -956,32 +956,48 @@ CDataType_in_dll_impl(PyObject *type, PyTypeObject *cls, PyObject *dll, return NULL; } +#undef USE_DLERROR #ifdef MS_WIN32 Py_BEGIN_ALLOW_THREADS address = (void *)GetProcAddress(handle, name); Py_END_ALLOW_THREADS - if (!address) { - PyErr_Format(PyExc_ValueError, - "symbol '%s' not found", - name); - return NULL; - } #else + #ifdef __CYGWIN__ + // dlerror() isn't very helpful on cygwin + #else + #define USE_DLERROR + /* dlerror() always returns the latest error. + * + * Clear the previous value before calling dlsym(), + * to ensure we can tell if our call resulted in an error. + */ + (void)dlerror(); + #endif address = (void *)dlsym(handle, name); - if (!address) { -#ifdef __CYGWIN__ -/* dlerror() isn't very helpful on cygwin */ - PyErr_Format(PyExc_ValueError, - "symbol '%s' not found", - name); -#else - PyErr_SetString(PyExc_ValueError, dlerror()); #endif - return NULL; + + if (address) { + ctypes_state *st = get_module_state_by_def(Py_TYPE(type)); + return PyCData_AtAddress(st, type, address); } -#endif - ctypes_state *st = get_module_state_by_def(Py_TYPE(type)); - return PyCData_AtAddress(st, type, address); + + #ifdef USE_DLERROR + const char *dlerr = dlerror(); + if (dlerr) { + PyObject *message = PyUnicode_DecodeLocale(dlerr, "surrogateescape"); + if (message) { + PyErr_SetObject(PyExc_ValueError, message); + Py_DECREF(message); + return NULL; + } + // Ignore errors from PyUnicode_DecodeLocale, + // fall back to the generic error below. + PyErr_Clear(); + } + #endif +#undef USE_DLERROR + PyErr_Format(PyExc_ValueError, "symbol '%s' not found", name); + return NULL; } /*[clinic input] @@ -3759,6 +3775,7 @@ PyCFuncPtr_FromDll(PyTypeObject *type, PyObject *args, PyObject *kwds) return NULL; } +#undef USE_DLERROR #ifdef MS_WIN32 address = FindAddress(handle, name, (PyObject *)type); if (!address) { @@ -3774,20 +3791,41 @@ PyCFuncPtr_FromDll(PyTypeObject *type, PyObject *args, PyObject *kwds) return NULL; } #else + #ifdef __CYGWIN__ + //dlerror() isn't very helpful on cygwin */ + #else + #define USE_DLERROR + /* dlerror() always returns the latest error. + * + * Clear the previous value before calling dlsym(), + * to ensure we can tell if our call resulted in an error. + */ + (void)dlerror(); + #endif address = (PPROC)dlsym(handle, name); + if (!address) { -#ifdef __CYGWIN__ -/* dlerror() isn't very helpful on cygwin */ - PyErr_Format(PyExc_AttributeError, - "function '%s' not found", - name); -#else - PyErr_SetString(PyExc_AttributeError, dlerror()); -#endif + #ifdef USE_DLERROR + const char *dlerr = dlerror(); + if (dlerr) { + PyObject *message = PyUnicode_DecodeLocale(dlerr, "surrogateescape"); + if (message) { + PyErr_SetObject(PyExc_AttributeError, message); + Py_DECREF(ftuple); + Py_DECREF(message); + return NULL; + } + // Ignore errors from PyUnicode_DecodeLocale, + // fall back to the generic error below. + PyErr_Clear(); + } + #endif + PyErr_Format(PyExc_AttributeError, "function '%s' not found", name); Py_DECREF(ftuple); return NULL; } #endif +#undef USE_DLERROR ctypes_state *st = get_module_state_by_def(Py_TYPE(type)); if (!_validate_paramflags(st, type, paramflags)) { Py_DECREF(ftuple); diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index 5ac9cf16681645..218c3a9c81e05f 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -1623,13 +1623,39 @@ static PyObject *py_dl_sym(PyObject *self, PyObject *args) if (PySys_Audit("ctypes.dlsym/handle", "O", args) < 0) { return NULL; } +#undef USE_DLERROR + #ifdef __CYGWIN__ + // dlerror() isn't very helpful on cygwin + #else + #define USE_DLERROR + /* dlerror() always returns the latest error. + * + * Clear the previous value before calling dlsym(), + * to ensure we can tell if our call resulted in an error. + */ + (void)dlerror(); + #endif ptr = dlsym((void*)handle, name); - if (!ptr) { - PyErr_SetString(PyExc_OSError, - dlerror()); - return NULL; + if (ptr) { + return PyLong_FromVoidPtr(ptr); + } + #ifdef USE_DLERROR + const char *dlerr = dlerror(); + if (dlerr) { + PyObject *message = PyUnicode_DecodeLocale(dlerr, "surrogateescape"); + if (message) { + PyErr_SetObject(PyExc_OSError, message); + Py_DECREF(message); + return NULL; + } + // Ignore errors from PyUnicode_DecodeLocale, + // fall back to the generic error below. + PyErr_Clear(); } - return PyLong_FromVoidPtr(ptr); + #endif + #undef USE_DLERROR + PyErr_Format(PyExc_OSError, "symbol '%s' not found", name); + return NULL; } #endif From d4c72fed8cba8e15ab7bb6c30a92bc9f2c8f0a2c Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Fri, 15 Nov 2024 05:21:30 -0500 Subject: [PATCH 190/219] gh-126312: Don't traverse frozen objects on the free-threaded build (#126338) Also, _PyGC_Freeze() no longer freezes unreachable objects. Co-authored-by: Sergey B Kirpichev --- Lib/test/test_gc.py | 38 +++++++++++++++++++ ...-11-02-14-43-46.gh-issue-126312.LMHzLT.rst | 2 + Python/gc_free_threading.c | 19 +++++++--- 3 files changed, 54 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-11-02-14-43-46.gh-issue-126312.LMHzLT.rst diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py index 2b3c0d3baddeaf..0372815b9bfd27 100644 --- a/Lib/test/test_gc.py +++ b/Lib/test/test_gc.py @@ -1082,6 +1082,44 @@ def __del__(self): gc.collect() self.assertTrue(collected) + def test_traverse_frozen_objects(self): + # See GH-126312: Objects that were not frozen could traverse over + # a frozen object on the free-threaded build, which would cause + # a negative reference count. + x = [1, 2, 3] + gc.freeze() + y = [x] + y.append(y) + del y + gc.collect() + gc.unfreeze() + + def test_deferred_refcount_frozen(self): + # Also from GH-126312: objects that use deferred reference counting + # weren't ignored if they were frozen. Unfortunately, it's pretty + # difficult to come up with a case that triggers this. + # + # Calling gc.collect() while the garbage collector is frozen doesn't + # trigger this normally, but it *does* if it's inside unittest for whatever + # reason. We can't call unittest from inside a test, so it has to be + # in a subprocess. + source = textwrap.dedent(""" + import gc + import unittest + + + class Test(unittest.TestCase): + def test_something(self): + gc.freeze() + gc.collect() + gc.unfreeze() + + + if __name__ == "__main__": + unittest.main() + """) + assert_python_ok("-c", source) + class IncrementalGCTests(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-02-14-43-46.gh-issue-126312.LMHzLT.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-02-14-43-46.gh-issue-126312.LMHzLT.rst new file mode 100644 index 00000000000000..19c8f0a348731c --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-02-14-43-46.gh-issue-126312.LMHzLT.rst @@ -0,0 +1,2 @@ +Fix crash during garbage collection on an object frozen by :func:`gc.freeze` on the +free-threaded build. diff --git a/Python/gc_free_threading.c b/Python/gc_free_threading.c index 986d80c18d36c8..499ee51fdb2cd4 100644 --- a/Python/gc_free_threading.c +++ b/Python/gc_free_threading.c @@ -113,6 +113,12 @@ worklist_remove(struct worklist_iter *iter) iter->next = iter->ptr; } +static inline int +gc_is_frozen(PyObject *op) +{ + return (op->ob_gc_bits & _PyGC_BITS_FROZEN) != 0; +} + static inline int gc_is_unreachable(PyObject *op) { @@ -277,7 +283,7 @@ op_from_block(void *block, void *arg, bool include_frozen) if (!_PyObject_GC_IS_TRACKED(op)) { return NULL; } - if (!include_frozen && (op->ob_gc_bits & _PyGC_BITS_FROZEN) != 0) { + if (!include_frozen && gc_is_frozen(op)) { return NULL; } return op; @@ -358,7 +364,7 @@ gc_visit_stackref(_PyStackRef stackref) // being dead already. if (PyStackRef_IsDeferred(stackref) && !PyStackRef_IsNull(stackref)) { PyObject *obj = PyStackRef_AsPyObjectBorrow(stackref); - if (_PyObject_GC_IS_TRACKED(obj)) { + if (_PyObject_GC_IS_TRACKED(obj) && !gc_is_frozen(obj)) { gc_add_refs(obj, 1); } } @@ -439,7 +445,10 @@ process_delayed_frees(PyInterpreterState *interp) static int visit_decref(PyObject *op, void *arg) { - if (_PyObject_GC_IS_TRACKED(op) && !_Py_IsImmortal(op)) { + if (_PyObject_GC_IS_TRACKED(op) + && !_Py_IsImmortal(op) + && !gc_is_frozen(op)) + { // If update_refs hasn't reached this object yet, mark it // as (tentatively) unreachable and initialize ob_tid to zero. gc_maybe_init_refs(op); @@ -1539,7 +1548,7 @@ visit_freeze(const mi_heap_t *heap, const mi_heap_area_t *area, void *block, size_t block_size, void *args) { PyObject *op = op_from_block(block, args, true); - if (op != NULL) { + if (op != NULL && !gc_is_unreachable(op)) { op->ob_gc_bits |= _PyGC_BITS_FROZEN; } return true; @@ -1584,7 +1593,7 @@ visit_count_frozen(const mi_heap_t *heap, const mi_heap_area_t *area, void *block, size_t block_size, void *args) { PyObject *op = op_from_block(block, args, true); - if (op != NULL && (op->ob_gc_bits & _PyGC_BITS_FROZEN) != 0) { + if (op != NULL && gc_is_frozen(op)) { struct count_frozen_args *arg = (struct count_frozen_args *)args; arg->count++; } From 9332a6f82506f819f591466eb03213be2c8d1808 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Fri, 15 Nov 2024 12:22:56 +0000 Subject: [PATCH 191/219] gh-118973: Add _tkinter to freethreaded MSI (GH-126768) --- .../Windows/2024-11-12-22-31-13.gh-issue-118973._lfxW6.rst | 3 +++ Tools/msi/freethreaded/freethreaded_files.wxs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Windows/2024-11-12-22-31-13.gh-issue-118973._lfxW6.rst diff --git a/Misc/NEWS.d/next/Windows/2024-11-12-22-31-13.gh-issue-118973._lfxW6.rst b/Misc/NEWS.d/next/Windows/2024-11-12-22-31-13.gh-issue-118973._lfxW6.rst new file mode 100644 index 00000000000000..c173c3ee081cc2 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2024-11-12-22-31-13.gh-issue-118973._lfxW6.rst @@ -0,0 +1,3 @@ +Ensures the experimental free-threaded install includes the ``_tkinter`` +module. The optional Tcl/Tk component must also be installed in order for +the module to work. diff --git a/Tools/msi/freethreaded/freethreaded_files.wxs b/Tools/msi/freethreaded/freethreaded_files.wxs index 367fd978efd484..b3ce28e7aedc84 100644 --- a/Tools/msi/freethreaded/freethreaded_files.wxs +++ b/Tools/msi/freethreaded/freethreaded_files.wxs @@ -103,7 +103,7 @@ - + From e17486982ca7b40cc8fbc1c76ad25facc5e76349 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Fri, 15 Nov 2024 13:26:38 +0100 Subject: [PATCH 192/219] gh-89083: small docs fixup for UUIDv8 (#126857) --- Doc/library/uuid.rst | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Doc/library/uuid.rst b/Doc/library/uuid.rst index 6166c22caedf81..9be12edd36b9a8 100644 --- a/Doc/library/uuid.rst +++ b/Doc/library/uuid.rst @@ -11,9 +11,9 @@ -------------- This module provides immutable :class:`UUID` objects (the :class:`UUID` class) -and the functions :func:`uuid1`, :func:`uuid3`, :func:`uuid4`, :func:`uuid5` for -generating version 1, 3, 4, 5, and 8 UUIDs as specified in :rfc:`9562` (which -supersedes :rfc:`4122`). +and the functions :func:`uuid1`, :func:`uuid3`, :func:`uuid4`, :func:`uuid5`, +and :func:`uuid.uuid8` for generating version 1, 3, 4, 5, and 8 UUIDs as +specified in :rfc:`9562` (which supersedes :rfc:`4122`). If all you want is a unique ID, you should probably call :func:`uuid1` or :func:`uuid4`. Note that :func:`uuid1` may compromise privacy since it creates @@ -323,6 +323,9 @@ The following options are accepted: Specify the function name to use to generate the uuid. By default :func:`uuid4` is used. + .. versionadded:: next + Allow generating UUID version 8. + .. option:: -n --namespace From d00f7b1b9d12dd6f29d7616217900785c4f6674d Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 15 Nov 2024 13:48:57 +0100 Subject: [PATCH 193/219] gh-125063: marshal: Add version 5, improve documentation (GH-126829) * Document that slices can be marshalled * Deduplicate and organize the list of supported types in docs * Organize the type code list in marshal.c, to make it more obvious that this is a versioned format * Back-fill some historical info Co-authored-by: Michael Droettboom --- Doc/c-api/marshal.rst | 9 +-- Doc/library/marshal.rst | 64 +++++++++++++------ Include/marshal.h | 2 +- Lib/test/test_marshal.py | 27 +++++++- ...-11-14-13-16-20.gh-issue-125063.kJ-WnH.rst | 2 + Programs/_freeze_module.c | 1 + Python/marshal.c | 46 ++++++++----- 7 files changed, 110 insertions(+), 41 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-11-14-13-16-20.gh-issue-125063.kJ-WnH.rst diff --git a/Doc/c-api/marshal.rst b/Doc/c-api/marshal.rst index b9085ad3ec361d..61218a1bf6f171 100644 --- a/Doc/c-api/marshal.rst +++ b/Doc/c-api/marshal.rst @@ -13,11 +13,12 @@ binary mode. Numeric values are stored with the least significant byte first. -The module supports two versions of the data format: version 0 is the -historical version, version 1 shares interned strings in the file, and upon -unmarshalling. Version 2 uses a binary format for floating-point numbers. -``Py_MARSHAL_VERSION`` indicates the current file format (currently 2). +The module supports several versions of the data format; see +the :py:mod:`Python module documentation ` for details. +.. c:macro:: Py_MARSHAL_VERSION + + The current format version. See :py:data:`marshal.version`. .. c:function:: void PyMarshal_WriteLongToFile(long value, FILE *file, int version) diff --git a/Doc/library/marshal.rst b/Doc/library/marshal.rst index 9e4606df0f774e..8b14ee449d4631 100644 --- a/Doc/library/marshal.rst +++ b/Doc/library/marshal.rst @@ -38,23 +38,39 @@ supports a substantially wider range of objects than marshal. maliciously constructed data. Never unmarshal data received from an untrusted or unauthenticated source. +There are functions that read/write files as well as functions operating on +bytes-like objects. + .. index:: object; code, code object Not all Python object types are supported; in general, only objects whose value is independent from a particular invocation of Python can be written and read by -this module. The following types are supported: booleans, integers, floating-point -numbers, complex numbers, strings, bytes, bytearrays, tuples, lists, sets, -frozensets, dictionaries, and code objects (if *allow_code* is true), -where it should be understood that -tuples, lists, sets, frozensets and dictionaries are only supported as long as -the values contained therein are themselves supported. The -singletons :const:`None`, :const:`Ellipsis` and :exc:`StopIteration` can also be -marshalled and unmarshalled. -For format *version* lower than 3, recursive lists, sets and dictionaries cannot -be written (see below). +this module. The following types are supported: + +* Numeric types: :class:`int`, :class:`bool`, :class:`float`, :class:`complex`. +* Strings (:class:`str`) and :class:`bytes`. + :term:`Bytes-like objects ` like :class:`bytearray` are + marshalled as :class:`!bytes`. +* Containers: :class:`tuple`, :class:`list`, :class:`set`, :class:`frozenset`, + and (since :data:`version` 5), :class:`slice`. + It should be understood that these are supported only if the values contained + therein are themselves supported. + Recursive containers are supported since :data:`version` 3. +* The singletons :const:`None`, :const:`Ellipsis` and :exc:`StopIteration`. +* :class:`code` objects, if *allow_code* is true. See note above about + version dependence. + +.. versionchanged:: 3.4 + + * Added format version 3, which supports marshalling recursive lists, sets + and dictionaries. + * Added format version 4, which supports efficient representations + of short strings. + +.. versionchanged:: next + + Added format version 5, which allows marshalling slices. -There are functions that read/write files as well as functions operating on -bytes-like objects. The module defines these functions: @@ -140,11 +156,24 @@ In addition, the following constants are defined: .. data:: version - Indicates the format that the module uses. Version 0 is the historical - format, version 1 shares interned strings and version 2 uses a binary format - for floating-point numbers. - Version 3 adds support for object instancing and recursion. - The current version is 4. + Indicates the format that the module uses. + Version 0 is the historical first version; subsequent versions + add new features. + Generally, a new version becomes the default when it is introduced. + + ======= =============== ==================================================== + Version Available since New features + ======= =============== ==================================================== + 1 Python 2.4 Sharing interned strings + ------- --------------- ---------------------------------------------------- + 2 Python 2.5 Binary representation of floats + ------- --------------- ---------------------------------------------------- + 3 Python 3.4 Support for object instancing and recursion + ------- --------------- ---------------------------------------------------- + 4 Python 3.4 Efficient representation of short strings + ------- --------------- ---------------------------------------------------- + 5 Python 3.14 Support for :class:`slice` objects + ======= =============== ==================================================== .. rubric:: Footnotes @@ -154,4 +183,3 @@ In addition, the following constants are defined: around in a self-contained form. Strictly speaking, "to marshal" means to convert some data from internal to external form (in an RPC buffer for instance) and "unmarshalling" for the reverse process. - diff --git a/Include/marshal.h b/Include/marshal.h index f8b0de80cfc38d..f773587bdd0429 100644 --- a/Include/marshal.h +++ b/Include/marshal.h @@ -13,7 +13,7 @@ PyAPI_FUNC(PyObject *) PyMarshal_ReadObjectFromString(const char *, Py_ssize_t); PyAPI_FUNC(PyObject *) PyMarshal_WriteObjectToString(PyObject *, int); -#define Py_MARSHAL_VERSION 4 +#define Py_MARSHAL_VERSION 5 PyAPI_FUNC(long) PyMarshal_ReadLongFromFile(FILE *); PyAPI_FUNC(int) PyMarshal_ReadShortFromFile(FILE *); diff --git a/Lib/test/test_marshal.py b/Lib/test/test_marshal.py index 64ee1ba867d592..93b8684c725d24 100644 --- a/Lib/test/test_marshal.py +++ b/Lib/test/test_marshal.py @@ -28,6 +28,13 @@ def helper(self, sample, *extra): finally: os_helper.unlink(os_helper.TESTFN) +def omit_last_byte(data): + """return data[:-1]""" + # This file's code is used in CompatibilityTestCase, + # but slices need marshal version 5. + # Avoid the slice literal. + return data[slice(0, -1)] + class IntTestCase(unittest.TestCase, HelperMixin): def test_ints(self): # Test a range of Python ints larger than the machine word size. @@ -241,7 +248,8 @@ def test_bug_5888452(self): def test_patch_873224(self): self.assertRaises(Exception, marshal.loads, b'0') self.assertRaises(Exception, marshal.loads, b'f') - self.assertRaises(Exception, marshal.loads, marshal.dumps(2**65)[:-1]) + self.assertRaises(Exception, marshal.loads, + omit_last_byte(marshal.dumps(2**65))) def test_version_argument(self): # Python 2.4.0 crashes for any call to marshal.dumps(x, y) @@ -594,6 +602,19 @@ def testNoIntern(self): s2 = sys.intern(s) self.assertNotEqual(id(s2), id(s)) +class SliceTestCase(unittest.TestCase, HelperMixin): + def test_slice(self): + for obj in ( + slice(None), slice(1), slice(1, 2), slice(1, 2, 3), + slice({'set'}, ('tuple', {'with': 'dict'}, ), self.helper.__code__) + ): + with self.subTest(obj=str(obj)): + self.helper(obj) + + for version in range(4): + with self.assertRaises(ValueError): + marshal.dumps(obj, version) + @support.cpython_only @unittest.skipUnless(_testcapi, 'requires _testcapi') class CAPI_TestCase(unittest.TestCase, HelperMixin): @@ -654,7 +675,7 @@ def test_read_last_object_from_file(self): self.assertEqual(r, obj) with open(os_helper.TESTFN, 'wb') as f: - f.write(data[:1]) + f.write(omit_last_byte(data)) with self.assertRaises(EOFError): _testcapi.pymarshal_read_last_object_from_file(os_helper.TESTFN) os_helper.unlink(os_helper.TESTFN) @@ -671,7 +692,7 @@ def test_read_object_from_file(self): self.assertEqual(p, len(data)) with open(os_helper.TESTFN, 'wb') as f: - f.write(data[:1]) + f.write(omit_last_byte(data)) with self.assertRaises(EOFError): _testcapi.pymarshal_read_object_from_file(os_helper.TESTFN) os_helper.unlink(os_helper.TESTFN) diff --git a/Misc/NEWS.d/next/Library/2024-11-14-13-16-20.gh-issue-125063.kJ-WnH.rst b/Misc/NEWS.d/next/Library/2024-11-14-13-16-20.gh-issue-125063.kJ-WnH.rst new file mode 100644 index 00000000000000..5ddf41206db07e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-11-14-13-16-20.gh-issue-125063.kJ-WnH.rst @@ -0,0 +1,2 @@ +:mod:`marshal` now supports :class:`slice` objects. The marshal format +version was increased to 5. diff --git a/Programs/_freeze_module.c b/Programs/_freeze_module.c index 891e4256e897ab..06d1ee016dc2a8 100644 --- a/Programs/_freeze_module.c +++ b/Programs/_freeze_module.c @@ -121,6 +121,7 @@ compile_and_marshal(const char *name, const char *text) return NULL; } + assert(Py_MARSHAL_VERSION >= 5); PyObject *marshalled = PyMarshal_WriteObjectToString(code, Py_MARSHAL_VERSION); Py_CLEAR(code); if (marshalled == NULL) { diff --git a/Python/marshal.c b/Python/marshal.c index a280fbfd078f41..72afa4ff89432c 100644 --- a/Python/marshal.c +++ b/Python/marshal.c @@ -50,41 +50,52 @@ module marshal # define MAX_MARSHAL_STACK_DEPTH 2000 #endif +/* Supported types */ #define TYPE_NULL '0' #define TYPE_NONE 'N' #define TYPE_FALSE 'F' #define TYPE_TRUE 'T' #define TYPE_STOPITER 'S' #define TYPE_ELLIPSIS '.' -#define TYPE_INT 'i' -/* TYPE_INT64 is not generated anymore. - Supported for backward compatibility only. */ -#define TYPE_INT64 'I' -#define TYPE_FLOAT 'f' -#define TYPE_BINARY_FLOAT 'g' -#define TYPE_COMPLEX 'x' -#define TYPE_BINARY_COMPLEX 'y' -#define TYPE_LONG 'l' -#define TYPE_STRING 's' -#define TYPE_INTERNED 't' -#define TYPE_REF 'r' -#define TYPE_TUPLE '(' +#define TYPE_BINARY_FLOAT 'g' // Version 0 uses TYPE_FLOAT instead. +#define TYPE_BINARY_COMPLEX 'y' // Version 0 uses TYPE_COMPLEX instead. +#define TYPE_LONG 'l' // See also TYPE_INT. +#define TYPE_STRING 's' // Bytes. (Name comes from Python 2.) +#define TYPE_TUPLE '(' // See also TYPE_SMALL_TUPLE. #define TYPE_LIST '[' #define TYPE_DICT '{' #define TYPE_CODE 'c' #define TYPE_UNICODE 'u' #define TYPE_UNKNOWN '?' +// added in version 2: #define TYPE_SET '<' #define TYPE_FROZENSET '>' +// added in version 5: #define TYPE_SLICE ':' -#define FLAG_REF '\x80' /* with a type, add obj to index */ +// Remember to update the version and documentation when adding new types. +/* Special cases for unicode strings (added in version 4) */ +#define TYPE_INTERNED 't' // Version 1+ #define TYPE_ASCII 'a' #define TYPE_ASCII_INTERNED 'A' -#define TYPE_SMALL_TUPLE ')' #define TYPE_SHORT_ASCII 'z' #define TYPE_SHORT_ASCII_INTERNED 'Z' +/* Special cases for small objects */ +#define TYPE_INT 'i' // All versions. 32-bit encoding. +#define TYPE_SMALL_TUPLE ')' // Version 4+ + +/* Supported for backwards compatibility */ +#define TYPE_COMPLEX 'x' // Generated for version 0 only. +#define TYPE_FLOAT 'f' // Generated for version 0 only. +#define TYPE_INT64 'I' // Not generated any more. + +/* References (added in version 3) */ +#define TYPE_REF 'r' +#define FLAG_REF '\x80' /* with a type, add obj to index */ + + +// Error codes: #define WFERR_OK 0 #define WFERR_UNMARSHALLABLE 1 #define WFERR_NESTEDTOODEEP 2 @@ -615,6 +626,11 @@ w_complex_object(PyObject *v, char flag, WFILE *p) PyBuffer_Release(&view); } else if (PySlice_Check(v)) { + if (p->version < 5) { + w_byte(TYPE_UNKNOWN, p); + p->error = WFERR_UNMARSHALLABLE; + return; + } PySliceObject *slice = (PySliceObject *)v; W_TYPE(TYPE_SLICE, p); w_object(slice->start, p); From 08f98f4576f95f9ae1a4423d151fce053416f39f Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Fri, 15 Nov 2024 08:03:46 -0500 Subject: [PATCH 194/219] Fix intermittent failures for the `PyUnstable_Object_EnableDeferredRefcount` tests (GH-126849) Hotfix for the PyUnstable_Object_EnableDeferredRefcount tests. --- Lib/test/test_capi/test_object.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/Lib/test/test_capi/test_object.py b/Lib/test/test_capi/test_object.py index a38b203ed12fa2..b0d39937fd865f 100644 --- a/Lib/test/test_capi/test_object.py +++ b/Lib/test/test_capi/test_object.py @@ -134,6 +134,7 @@ def test_ClearWeakRefsNoCallbacks_no_weakref_support(self): _testcapi.pyobject_clear_weakrefs_no_callbacks(obj) +@threading_helper.requires_working_threading() class EnableDeferredRefcountingTest(unittest.TestCase): """Test PyUnstable_Object_EnableDeferredRefcount""" @support.requires_resource("cpu") @@ -158,21 +159,13 @@ def silly_func(obj): silly_list = [1, 2, 3] threads = [ - Thread(target=silly_func, args=(silly_list,)) for _ in range(5) + Thread(target=silly_func, args=(silly_list,)) for _ in range(4) ] - with threading_helper.catch_threading_exception() as cm: - for t in threads: - t.start() - + with threading_helper.start_threads(threads): for i in range(10): silly_list.append(i) - for t in threads: - t.join() - - self.assertIsNone(cm.exc_value) - if support.Py_GIL_DISABLED: self.assertTrue(_testinternalcapi.has_deferred_refcount(silly_list)) From 612ac283b81907d328891b102f5bfafcf62bd833 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Fri, 15 Nov 2024 14:52:56 +0100 Subject: [PATCH 195/219] gh-122549: Add platform.invalidate_caches() (#122547) Allow to invalidate platform's cached results. --- Doc/library/platform.rst | 12 +++++++ Doc/whatsnew/3.14.rst | 8 +++++ Lib/platform.py | 15 ++++++++- Lib/test/test_platform.py | 32 +++++++++++++++++++ ...-08-01-11-15-55.gh-issue-122549.ztV4Kz.rst | 1 + 5 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2024-08-01-11-15-55.gh-issue-122549.ztV4Kz.rst diff --git a/Doc/library/platform.rst b/Doc/library/platform.rst index 1beb3b9eb89d22..cfe1e7ba48da32 100644 --- a/Doc/library/platform.rst +++ b/Doc/library/platform.rst @@ -359,3 +359,15 @@ Android Platform `__. .. versionadded:: 3.13 + + +Miscellaneous +------------- + +.. function:: invalidate_caches() + + Clear out the internal cache of information, such as the :func:`uname`. + This is typically useful when the platform's :func:`node` is changed + by an external process and one needs to retrieve the updated value. + + .. versionadded:: 3.14 diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index d38188f0054754..6cec611d111ece 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -454,6 +454,14 @@ pathlib (Contributed by Barney Gale in :gh:`125413`.) +platform +-------- + +* Add :func:`platform.invalidate_caches` to invalidate the cached results. + + (Contributed by Bénédikt Tran in :gh:`122549`.) + + pdb --- diff --git a/Lib/platform.py b/Lib/platform.py index d6322c9d99d2f3..239e660cd1621d 100644 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -31,6 +31,7 @@ # # # +# 1.0.9 - added invalidate_caches() function to invalidate cached values # 1.0.8 - changed Windows support to read version from kernel32.dll # 1.0.7 - added DEV_NULL # 1.0.6 - added linux_distribution() @@ -109,7 +110,7 @@ """ -__version__ = '1.0.8' +__version__ = '1.0.9' import collections import os @@ -1441,6 +1442,18 @@ def freedesktop_os_release(): return _os_release_cache.copy() +def invalidate_caches(): + """Invalidate the cached results.""" + global _uname_cache + _uname_cache = None + + global _os_release_cache + _os_release_cache = None + + _sys_version_cache.clear() + _platform_cache.clear() + + ### Command line interface if __name__ == '__main__': diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py index 40d5fb338ce563..e04ad142061ad3 100644 --- a/Lib/test/test_platform.py +++ b/Lib/test/test_platform.py @@ -83,6 +83,38 @@ def clear_caches(self): platform._uname_cache = None platform._os_release_cache = None + def test_invalidate_caches(self): + self.clear_caches() + + self.assertDictEqual(platform._platform_cache, {}) + self.assertDictEqual(platform._sys_version_cache, {}) + self.assertIsNone(platform._uname_cache) + self.assertIsNone(platform._os_release_cache) + + # fill the cached entries (some have side effects on others) + platform.platform() # for platform._platform_cache + platform.python_implementation() # for platform._sys_version_cache + platform.uname() # for platform._uname_cache + + # check that the cache are filled + self.assertNotEqual(platform._platform_cache, {}) + self.assertNotEqual(platform._sys_version_cache, {}) + self.assertIsNotNone(platform._uname_cache) + + try: + platform.freedesktop_os_release() + except OSError: + self.assertIsNone(platform._os_release_cache) + else: + self.assertIsNotNone(platform._os_release_cache) + + with self.subTest('clear platform caches'): + platform.invalidate_caches() + self.assertDictEqual(platform._platform_cache, {}) + self.assertDictEqual(platform._sys_version_cache, {}) + self.assertIsNone(platform._uname_cache) + self.assertIsNone(platform._os_release_cache) + def test_architecture(self): res = platform.architecture() diff --git a/Misc/NEWS.d/next/Library/2024-08-01-11-15-55.gh-issue-122549.ztV4Kz.rst b/Misc/NEWS.d/next/Library/2024-08-01-11-15-55.gh-issue-122549.ztV4Kz.rst new file mode 100644 index 00000000000000..6b2cbc0a6c9b7a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-08-01-11-15-55.gh-issue-122549.ztV4Kz.rst @@ -0,0 +1 @@ +Add :func:`platform.invalidate_caches` to invalidate cached results. From a2c180f4e768267b12beda7f02436eb4a481dbe2 Mon Sep 17 00:00:00 2001 From: Seth Michael Larson Date: Fri, 15 Nov 2024 09:54:56 -0600 Subject: [PATCH 196/219] Add PEP 761 to What's New (#126550) Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- Doc/whatsnew/3.14.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 6cec611d111ece..958efbe73c1c27 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -791,6 +791,16 @@ Changes in the Python API Build changes ============= +PEP 761: Discontinuation of PGP signatures +------------------------------------------ + +PGP signatures will not be available for CPython 3.14 and onwards. +Users verifying artifacts must use `Sigstore verification materials`_ for +verifying CPython artifacts. This change in release process is specified +in :pep:`761`. + +.. _Sigstore verification materials: https://www.python.org/downloads/metadata/sigstore/ + C API changes ============= From 3be7498d2450519d5d8f63a35ef298db3b3d935b Mon Sep 17 00:00:00 2001 From: Nadeshiko Manju Date: Sat, 16 Nov 2024 03:03:06 +0800 Subject: [PATCH 197/219] gh-126476: Raise IllegalMonthError for calendar.formatmonth() when the input month is not correct (GH-126484) Co-authored-by: Ethan Furman --- Lib/calendar.py | 16 +++++++++++++--- Lib/test/test_calendar.py | 14 +++++++++++++- ...024-11-06-18-30-50.gh-issue-126476.F1wh3c.rst | 2 ++ 3 files changed, 28 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-11-06-18-30-50.gh-issue-126476.F1wh3c.rst diff --git a/Lib/calendar.py b/Lib/calendar.py index 069dd5174112ae..8c1c646da46a98 100644 --- a/Lib/calendar.py +++ b/Lib/calendar.py @@ -27,7 +27,9 @@ error = ValueError # Exceptions raised for bad input -class IllegalMonthError(ValueError): +# This is trick for backward compatibility. Since 3.13, we will raise IllegalMonthError instead of +# IndexError for bad month number(out of 1-12). But we can't remove IndexError for backward compatibility. +class IllegalMonthError(ValueError, IndexError): def __init__(self, month): self.month = month def __str__(self): @@ -158,11 +160,14 @@ def weekday(year, month, day): return Day(datetime.date(year, month, day).weekday()) +def _validate_month(month): + if not 1 <= month <= 12: + raise IllegalMonthError(month) + def monthrange(year, month): """Return weekday of first day of month (0-6 ~ Mon-Sun) and number of days (28-31) for year, month.""" - if not 1 <= month <= 12: - raise IllegalMonthError(month) + _validate_month(month) day1 = weekday(year, month, 1) ndays = mdays[month] + (month == FEBRUARY and isleap(year)) return day1, ndays @@ -370,6 +375,8 @@ def formatmonthname(self, theyear, themonth, width, withyear=True): """ Return a formatted month name. """ + _validate_month(themonth) + s = month_name[themonth] if withyear: s = "%s %r" % (s, theyear) @@ -500,6 +507,7 @@ def formatmonthname(self, theyear, themonth, withyear=True): """ Return a month name as a table row. """ + _validate_month(themonth) if withyear: s = '%s %s' % (month_name[themonth], theyear) else: @@ -786,6 +794,8 @@ def main(args=None): if options.month is None: optdict["c"] = options.spacing optdict["m"] = options.months + if options.month is not None: + _validate_month(options.month) if options.year is None: result = cal.formatyear(datetime.date.today().year, **optdict) elif options.month is None: diff --git a/Lib/test/test_calendar.py b/Lib/test/test_calendar.py index f119d89c0ec39a..073df310bb49eb 100644 --- a/Lib/test/test_calendar.py +++ b/Lib/test/test_calendar.py @@ -457,6 +457,11 @@ def test_formatmonth(self): calendar.TextCalendar().formatmonth(0, 2), result_0_02_text ) + def test_formatmonth_with_invalid_month(self): + with self.assertRaises(calendar.IllegalMonthError): + calendar.TextCalendar().formatmonth(2017, 13) + with self.assertRaises(calendar.IllegalMonthError): + calendar.TextCalendar().formatmonth(2017, -1) def test_formatmonthname_with_year(self): self.assertEqual( @@ -1121,7 +1126,7 @@ def test__all__(self): not_exported = { 'mdays', 'January', 'February', 'EPOCH', 'different_locale', 'c', 'prweek', 'week', 'format', - 'formatstring', 'main', 'monthlen', 'prevmonth', 'nextmonth'} + 'formatstring', 'main', 'monthlen', 'prevmonth', 'nextmonth', ""} support.check__all__(self, calendar, not_exported=not_exported) @@ -1149,6 +1154,13 @@ def test_formatmonth(self): self.assertIn('class="text-center month"', self.cal.formatmonth(2017, 5)) + def test_formatmonth_with_invalid_month(self): + with self.assertRaises(calendar.IllegalMonthError): + self.cal.formatmonth(2017, 13) + with self.assertRaises(calendar.IllegalMonthError): + self.cal.formatmonth(2017, -1) + + def test_formatweek(self): weeks = self.cal.monthdays2calendar(2017, 5) self.assertIn('class="wed text-nowrap"', self.cal.formatweek(weeks[0])) diff --git a/Misc/NEWS.d/next/Library/2024-11-06-18-30-50.gh-issue-126476.F1wh3c.rst b/Misc/NEWS.d/next/Library/2024-11-06-18-30-50.gh-issue-126476.F1wh3c.rst new file mode 100644 index 00000000000000..f558c29e8b087f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-11-06-18-30-50.gh-issue-126476.F1wh3c.rst @@ -0,0 +1,2 @@ +Raise :class:`calendar.IllegalMonthError` (now a subclass of :class:`IndexError`) for :func:`calendar.month` +when the input month is not correct. From 94a7a4e22fb8f567090514785c69e65298acca42 Mon Sep 17 00:00:00 2001 From: Beomsoo Kim Date: Sat, 16 Nov 2024 06:02:34 +0900 Subject: [PATCH 198/219] Docs: Miscellaneous corrections to simple statements in the language reference (GH-126720) * Replace: The :keyword:`global` -> The :keyword:`global` statement Add :keyword: when it's needed * Replace repeated links with duoble backticks --- Doc/reference/simple_stmts.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Doc/reference/simple_stmts.rst b/Doc/reference/simple_stmts.rst index a005395bfc402e..2a72af4e9a3299 100644 --- a/Doc/reference/simple_stmts.rst +++ b/Doc/reference/simple_stmts.rst @@ -408,9 +408,9 @@ The extended form, ``assert expression1, expression2``, is equivalent to :: These equivalences assume that :const:`__debug__` and :exc:`AssertionError` refer to the built-in variables with those names. In the current implementation, the -built-in variable :const:`__debug__` is ``True`` under normal circumstances, +built-in variable ``__debug__`` is ``True`` under normal circumstances, ``False`` when optimization is requested (command line option :option:`-O`). The current -code generator emits no code for an assert statement when optimization is +code generator emits no code for an :keyword:`assert` statement when optimization is requested at compile time. Note that it is unnecessary to include the source code for the expression that failed in the error message; it will be displayed as part of the stack trace. @@ -533,8 +533,8 @@ The :keyword:`!yield` statement yield_stmt: `yield_expression` A :keyword:`yield` statement is semantically equivalent to a :ref:`yield -expression `. The yield statement can be used to omit the parentheses -that would otherwise be required in the equivalent yield expression +expression `. The ``yield`` statement can be used to omit the +parentheses that would otherwise be required in the equivalent yield expression statement. For example, the yield statements :: yield @@ -546,7 +546,7 @@ are equivalent to the yield expression statements :: (yield from ) Yield expressions and statements are only used when defining a :term:`generator` -function, and are only used in the body of the generator function. Using yield +function, and are only used in the body of the generator function. Using :keyword:`yield` in a function definition is sufficient to cause that definition to create a generator function instead of a normal function. @@ -966,12 +966,12 @@ The :keyword:`!global` statement .. productionlist:: python-grammar global_stmt: "global" `identifier` ("," `identifier`)* -The :keyword:`global` causes the listed identifiers to be interpreted +The :keyword:`global` statement causes the listed identifiers to be interpreted as globals. It would be impossible to assign to a global variable without :keyword:`!global`, although free variables may refer to globals without being declared global. -The global statement applies to the entire scope of a function or +The :keyword:`global` statement applies to the entire scope of a function or class body. A :exc:`SyntaxError` is raised if a variable is used or assigned to prior to its global declaration in the scope. @@ -1009,7 +1009,7 @@ identifiers. If a name is bound in more than one nonlocal scope, the nearest binding is used. If a name is not bound in any nonlocal scope, or if there is no nonlocal scope, a :exc:`SyntaxError` is raised. -The nonlocal statement applies to the entire scope of a function or +The :keyword:`nonlocal` statement applies to the entire scope of a function or class body. A :exc:`SyntaxError` is raised if a variable is used or assigned to prior to its nonlocal declaration in the scope. From d6bcc154e93a0a20ab97187d3e8b726fffb14f8f Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Fri, 15 Nov 2024 18:09:05 -0500 Subject: [PATCH 199/219] Added a warning to the urljoin docs, indicating that it is not safe to use with attacker controlled URLs (GH-126659) This was flagged to me at a party today by someone who works in red-teaming as a frequently encountered footgun. Documenting the potentially unexpected behavior seemed like a good place to start. --- Doc/library/urllib.parse.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Doc/library/urllib.parse.rst b/Doc/library/urllib.parse.rst index 0501dc8733b2cd..44a9c79cba2216 100644 --- a/Doc/library/urllib.parse.rst +++ b/Doc/library/urllib.parse.rst @@ -407,6 +407,15 @@ or on combining URL components into a URL string. If you do not want that behavior, preprocess the *url* with :func:`urlsplit` and :func:`urlunsplit`, removing possible *scheme* and *netloc* parts. + .. warning:: + + Because an absolute URL may be passed as the ``url`` parameter, it is + generally **not secure** to use ``urljoin`` with an attacker-controlled + ``url``. For example in, + ``urljoin("https://website.com/users/", username)``, if ``username`` can + contain an absolute URL, the result of ``urljoin`` will be the absolute + URL. + .. versionchanged:: 3.5 From 544b001b233ac57dfce17587ffbd10a70abe3ab0 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Sat, 16 Nov 2024 02:44:05 +0100 Subject: [PATCH 200/219] gh-126691: Remove --with-emscripten-target (#126787) This unifies the code for nodejs and the code for the browser. After this commit, the browser example doesn't work; this will be fixed in a subsequent update. --- Doc/using/configure.rst | 9 -- ...-11-13-15-47-09.gh-issue-126691.ni4K-b.rst | 3 + Tools/wasm/README.md | 6 - Tools/wasm/emscripten/node_pre.js | 10 +- configure | 110 +++--------------- configure.ac | 73 +++--------- 6 files changed, 38 insertions(+), 173 deletions(-) create mode 100644 Misc/NEWS.d/next/Build/2024-11-13-15-47-09.gh-issue-126691.ni4K-b.rst diff --git a/Doc/using/configure.rst b/Doc/using/configure.rst index 5f1ee0c2a2e657..6fd623c5176778 100644 --- a/Doc/using/configure.rst +++ b/Doc/using/configure.rst @@ -454,15 +454,6 @@ Options for third-party dependencies WebAssembly Options ------------------- -.. option:: --with-emscripten-target=[browser|node] - - Set build flavor for ``wasm32-emscripten``. - - * ``browser`` (default): preload minimal stdlib, default MEMFS. - * ``node``: NODERAWFS and pthread support. - - .. versionadded:: 3.11 - .. option:: --enable-wasm-dynamic-linking Turn on dynamic linking support for WASM. diff --git a/Misc/NEWS.d/next/Build/2024-11-13-15-47-09.gh-issue-126691.ni4K-b.rst b/Misc/NEWS.d/next/Build/2024-11-13-15-47-09.gh-issue-126691.ni4K-b.rst new file mode 100644 index 00000000000000..9a2196dab8d711 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2024-11-13-15-47-09.gh-issue-126691.ni4K-b.rst @@ -0,0 +1,3 @@ +Removed the ``--with-emscripten-target`` configure flag. We unified the +``node`` and ``browser`` options and the same build can now be used, independent +of target runtime. diff --git a/Tools/wasm/README.md b/Tools/wasm/README.md index 4c9a643b0d9d74..3f4211fb1dfb28 100644 --- a/Tools/wasm/README.md +++ b/Tools/wasm/README.md @@ -21,12 +21,6 @@ https://github.com/psf/webassembly for more information. ### Build -For now the build system has two target flavors. The ``Emscripten/browser`` -target (``--with-emscripten-target=browser``) is optimized for browsers. -It comes with a reduced and preloaded stdlib without tests and threading -support. The ``Emscripten/node`` target has threading enabled and can -access the file system directly. - To cross compile to the ``wasm32-emscripten`` platform you need [the Emscripten compiler toolchain](https://emscripten.org/), a Python interpreter, and an installation of Node version 18 or newer. Emscripten diff --git a/Tools/wasm/emscripten/node_pre.js b/Tools/wasm/emscripten/node_pre.js index 3490d3ca591ef6..54b09dc08233f3 100644 --- a/Tools/wasm/emscripten/node_pre.js +++ b/Tools/wasm/emscripten/node_pre.js @@ -1,9 +1,15 @@ // If process is undefined, we're not running in the node runtime let it go I // guess? if (typeof process !== "undefined") { - const nodeVersion = Number(process.versions.node.split('.',1)[0]); + const nodeVersion = Number(process.versions.node.split(".", 1)[0]); if (nodeVersion < 18) { - process.stderr.write(`Node version must be >= 18, got version ${process.version}\n`); + process.stderr.write( + `Node version must be >= 18, got version ${process.version}\n`, + ); process.exit(1); } + Module.preRun = () => { + FS.mkdirTree("/lib/"); + FS.mount(NODEFS, { root: __dirname + "/lib/" }, "/lib/"); + }; } diff --git a/configure b/configure index b1ced3106618ba..5b44a3d69929a0 100755 --- a/configure +++ b/configure @@ -1082,7 +1082,6 @@ with_universal_archs with_framework_name enable_framework with_app_store_compliance -with_emscripten_target enable_wasm_dynamic_linking enable_wasm_pthreads with_suffix @@ -1868,8 +1867,6 @@ Optional Packages: Enable any patches required for compiliance with app stores. Optional PATCH-FILE specifies the custom patch to apply. - --with-emscripten-target=[browser|node] - Emscripten platform --with-suffix=SUFFIX set executable suffix to SUFFIX (default is empty, yes is mapped to '.exe') --without-static-libpython @@ -7223,48 +7220,6 @@ case $ac_sys_system/$ac_sys_release in #( ;; esac -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for --with-emscripten-target" >&5 -printf %s "checking for --with-emscripten-target... " >&6; } - -# Check whether --with-emscripten-target was given. -if test ${with_emscripten_target+y} -then : - withval=$with_emscripten_target; - if test "x$ac_sys_system" = xEmscripten -then : - - case $with_emscripten_target in #( - browser) : - ac_sys_emscripten_target=browser ;; #( - node) : - ac_sys_emscripten_target=node ;; #( - browser-debug) : - ac_sys_emscripten_target=browser-debug ;; #( - node-debug) : - ac_sys_emscripten_target=node-debug ;; #( - *) : - as_fn_error $? "Invalid argument: --with-emscripten-target=browser|node" "$LINENO" 5 - ;; -esac - -else $as_nop - - as_fn_error $? "--with-emscripten-target only applies to Emscripten" "$LINENO" 5 - -fi - -else $as_nop - - if test "x$ac_sys_system" = xEmscripten -then : - ac_sys_emscripten_target=browser -fi - -fi - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_sys_emscripten_target" >&5 -printf "%s\n" "$ac_sys_emscripten_target" >&6; } - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for --enable-wasm-dynamic-linking" >&5 printf %s "checking for --enable-wasm-dynamic-linking... " >&6; } # Check whether --enable-wasm-dynamic-linking was given. @@ -7334,12 +7289,10 @@ esac else $as_nop - case $ac_sys_system/$ac_sys_emscripten_target in #( - Emscripten/browser*) : - EXEEXT=.js ;; #( - Emscripten/node*) : + case $ac_sys_system in #( + Emscripten) : EXEEXT=.js ;; #( - WASI/*) : + WASI) : EXEEXT=.wasm ;; #( *) : EXEEXT= @@ -7674,8 +7627,8 @@ printf %s "checking HOSTRUNNER... " >&6; } if test -z "$HOSTRUNNER" then - case $ac_sys_system/$ac_sys_emscripten_target in #( - Emscripten/node*) : + case $ac_sys_system in #( + Emscripten) : if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}node", so it can be a program name with args. @@ -7791,7 +7744,7 @@ then : as_fn_append HOSTRUNNER " --experimental-wasm-memory64" fi ;; #( - WASI/*) : + WASI) : HOSTRUNNER='wasmtime run --wasm max-wasm-stack=16777216 --wasi preview2=n --env PYTHONPATH=/$(shell realpath --relative-to $(abs_srcdir) $(abs_builddir))/$(shell cat pybuilddir.txt):/Lib --dir $(srcdir)::/' ;; #( *) : HOSTRUNNER='' @@ -7807,13 +7760,8 @@ if test -n "$HOSTRUNNER"; then fi # LIBRARY_DEPS, LINK_PYTHON_OBJS and LINK_PYTHON_DEPS variable -case $ac_sys_system/$ac_sys_emscripten_target in #( - Emscripten/browser*) : - LIBRARY_DEPS='$(PY3LIBRARY) $(WASM_STDLIB) python.html python.worker.js' ;; #( - *) : - LIBRARY_DEPS='$(PY3LIBRARY) $(EXPORTSYMS)' - ;; -esac +LIBRARY_DEPS='$(PY3LIBRARY) $(EXPORTSYMS)' + LINK_PYTHON_DEPS='$(LIBRARY_DEPS)' if test "$PY_ENABLE_SHARED" = 1 || test "$enable_framework" ; then LIBRARY_DEPS="\$(LDLIBRARY) $LIBRARY_DEPS" @@ -9500,34 +9448,10 @@ then : as_fn_append LINKFORSHARED " -sPROXY_TO_PTHREAD" fi - - case $ac_sys_emscripten_target in #( - browser*) : - - if test "x$ac_sys_emscripten_target" = xbrowser-debug -then : - wasm_debug=yes -fi - as_fn_append LINKFORSHARED " --preload-file=\$(WASM_ASSETS_DIR)" - WASM_ASSETS_DIR=".\$(prefix)" - WASM_STDLIB="\$(WASM_ASSETS_DIR)/local/lib/python\$(VERSION)/os.py" - WASM_LINKFORSHARED_DEBUG="-gsource-map --emit-symbol-map" - ;; #( - node*) : - - if test "x$ac_sys_emscripten_target" = xnode-debug -then : - wasm_debug=yes -fi - as_fn_append LDFLAGS_NODIST " --pre-js=\$(srcdir)/Tools/wasm/emscripten/node_pre.js" - as_fn_append LDFLAGS_NODIST " -sALLOW_MEMORY_GROWTH -sNODERAWFS" - as_fn_append LINKFORSHARED " -sEXIT_RUNTIME" - WASM_LINKFORSHARED_DEBUG="-gseparate-dwarf --emit-symbol-map" - - ;; #( - *) : - ;; -esac + as_fn_append LDFLAGS_NODIST " -sALLOW_MEMORY_GROWTH" + as_fn_append LDFLAGS_NODIST " -sEXIT_RUNTIME" + as_fn_append LDFLAGS_NODIST " --pre-js=\$(srcdir)/Tools/wasm/emscripten/node_pre.js" + WASM_LINKFORSHARED_DEBUG="-gseparate-dwarf --emit-symbol-map" if test "x$wasm_debug" = xyes then : @@ -29058,15 +28982,7 @@ else $as_nop fi else $as_nop - - case $ac_sys_system/$ac_sys_emscripten_target in #( - Emscripten/browser*) : - TEST_MODULES=no ;; #( - *) : - TEST_MODULES=yes - ;; -esac - + TEST_MODULES=yes fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $TEST_MODULES" >&5 diff --git a/configure.ac b/configure.ac index 3a55cbc1320393..7904f8990c48ee 100644 --- a/configure.ac +++ b/configure.ac @@ -1282,30 +1282,6 @@ AS_CASE([$ac_sys_system/$ac_sys_release], ] ) -AC_MSG_CHECKING([for --with-emscripten-target]) -AC_ARG_WITH([emscripten-target], - [AS_HELP_STRING([--with-emscripten-target=@<:@browser|node@:>@], [Emscripten platform])], -[ - AS_VAR_IF([ac_sys_system], [Emscripten], [ - AS_CASE([$with_emscripten_target], - [browser], [ac_sys_emscripten_target=browser], - [node], [ac_sys_emscripten_target=node], -dnl Debug builds with source map / dwarf symbols. Py_DEBUG builds easily -dnl run out of stack space. Detached sybmols and map prohibit some -dnl optimizations and increase file size. Options are undocumented so we -dnl are free to remove them in the future. - [browser-debug], [ac_sys_emscripten_target=browser-debug], - [node-debug], [ac_sys_emscripten_target=node-debug], - [AC_MSG_ERROR([Invalid argument: --with-emscripten-target=browser|node])] - ) - ], [ - AC_MSG_ERROR([--with-emscripten-target only applies to Emscripten]) - ]) -], [ - AS_VAR_IF([ac_sys_system], [Emscripten], [ac_sys_emscripten_target=browser]) -]) -AC_MSG_RESULT([$ac_sys_emscripten_target]) - dnl On Emscripten dlopen() requires -s MAIN_MODULE and -fPIC. The flags dnl disables dead code elimination and increases the size of the WASM module dnl by about 1.5 to 2MB. MAIN_MODULE defines __wasm_mutable_globals__. @@ -1350,10 +1326,9 @@ AC_ARG_WITH([suffix], [EXEEXT=$with_suffix] ) ], [ - AS_CASE([$ac_sys_system/$ac_sys_emscripten_target], - [Emscripten/browser*], [EXEEXT=.js], - [Emscripten/node*], [EXEEXT=.js], - [WASI/*], [EXEEXT=.wasm], + AS_CASE([$ac_sys_system], + [Emscripten], [EXEEXT=.js], + [WASI], [EXEEXT=.wasm], [EXEEXT=] ) ]) @@ -1638,8 +1613,8 @@ AC_MSG_CHECKING([HOSTRUNNER]) AC_ARG_VAR([HOSTRUNNER], [Program to run CPython for the host platform]) if test -z "$HOSTRUNNER" then - AS_CASE([$ac_sys_system/$ac_sys_emscripten_target], - [Emscripten/node*], [ + AS_CASE([$ac_sys_system], + [Emscripten], [ AC_PATH_TOOL([NODE], [node], [node]) HOSTRUNNER="$NODE" AS_VAR_IF([host_cpu], [wasm64], [AS_VAR_APPEND([HOSTRUNNER], [" --experimental-wasm-memory64"])]) @@ -1647,7 +1622,7 @@ then dnl TODO: support other WASI runtimes dnl wasmtime starts the process with "/" as CWD. For OOT builds add the dnl directory containing _sysconfigdata to PYTHONPATH. - [WASI/*], [HOSTRUNNER='wasmtime run --wasm max-wasm-stack=16777216 --wasi preview2=n --env PYTHONPATH=/$(shell realpath --relative-to $(abs_srcdir) $(abs_builddir))/$(shell cat pybuilddir.txt):/Lib --dir $(srcdir)::/'], + [WASI], [HOSTRUNNER='wasmtime run --wasm max-wasm-stack=16777216 --wasi preview2=n --env PYTHONPATH=/$(shell realpath --relative-to $(abs_srcdir) $(abs_builddir))/$(shell cat pybuilddir.txt):/Lib --dir $(srcdir)::/'], [HOSTRUNNER=''] ) fi @@ -1660,10 +1635,8 @@ if test -n "$HOSTRUNNER"; then fi # LIBRARY_DEPS, LINK_PYTHON_OBJS and LINK_PYTHON_DEPS variable -AS_CASE([$ac_sys_system/$ac_sys_emscripten_target], - [Emscripten/browser*], [LIBRARY_DEPS='$(PY3LIBRARY) $(WASM_STDLIB) python.html python.worker.js'], - [LIBRARY_DEPS='$(PY3LIBRARY) $(EXPORTSYMS)'] -) +LIBRARY_DEPS='$(PY3LIBRARY) $(EXPORTSYMS)' + LINK_PYTHON_DEPS='$(LIBRARY_DEPS)' if test "$PY_ENABLE_SHARED" = 1 || test "$enable_framework" ; then LIBRARY_DEPS="\$(LDLIBRARY) $LIBRARY_DEPS" @@ -2365,24 +2338,11 @@ AS_CASE([$ac_sys_system], AS_VAR_APPEND([LDFLAGS_NODIST], [" -sUSE_PTHREADS"]) AS_VAR_APPEND([LINKFORSHARED], [" -sPROXY_TO_PTHREAD"]) ]) - - AS_CASE([$ac_sys_emscripten_target], - [browser*], [ - AS_VAR_IF([ac_sys_emscripten_target], [browser-debug], [wasm_debug=yes]) - AS_VAR_APPEND([LINKFORSHARED], [" --preload-file=\$(WASM_ASSETS_DIR)"]) - WASM_ASSETS_DIR=".\$(prefix)" - WASM_STDLIB="\$(WASM_ASSETS_DIR)/local/lib/python\$(VERSION)/os.py" - dnl separate-dwarf does not seem to work in Chrome DevTools Support. - WASM_LINKFORSHARED_DEBUG="-gsource-map --emit-symbol-map" - ], - [node*], [ - AS_VAR_IF([ac_sys_emscripten_target], [node-debug], [wasm_debug=yes]) - AS_VAR_APPEND([LDFLAGS_NODIST], [" --pre-js=\$(srcdir)/Tools/wasm/emscripten/node_pre.js"]) - AS_VAR_APPEND([LDFLAGS_NODIST], [" -sALLOW_MEMORY_GROWTH -sNODERAWFS"]) - AS_VAR_APPEND([LINKFORSHARED], [" -sEXIT_RUNTIME"]) - WASM_LINKFORSHARED_DEBUG="-gseparate-dwarf --emit-symbol-map" - ] - ) + AS_VAR_APPEND([LDFLAGS_NODIST], [" -sALLOW_MEMORY_GROWTH"]) + dnl not completely sure whether or not we want -sEXIT_RUNTIME, keeping it for now. + AS_VAR_APPEND([LDFLAGS_NODIST], [" -sEXIT_RUNTIME"]) + AS_VAR_APPEND([LDFLAGS_NODIST], [" --pre-js=\$(srcdir)/Tools/wasm/emscripten/node_pre.js"]) + WASM_LINKFORSHARED_DEBUG="-gseparate-dwarf --emit-symbol-map" AS_VAR_IF([wasm_debug], [yes], [ AS_VAR_APPEND([LDFLAGS_NODIST], [" -sASSERTIONS"]) @@ -7463,12 +7423,7 @@ AC_MSG_CHECKING([for --disable-test-modules]) AC_ARG_ENABLE([test-modules], [AS_HELP_STRING([--disable-test-modules], [don't build nor install test modules])], [ AS_VAR_IF([enable_test_modules], [yes], [TEST_MODULES=yes], [TEST_MODULES=no]) -], [ - AS_CASE([$ac_sys_system/$ac_sys_emscripten_target], - [Emscripten/browser*], [TEST_MODULES=no], - [TEST_MODULES=yes] - ) -]) +], [TEST_MODULES=yes]) AC_MSG_RESULT([$TEST_MODULES]) AC_SUBST([TEST_MODULES]) From 2313f8421080ceb3343c6f5d291279adea85e073 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Sat, 16 Nov 2024 02:46:39 +0100 Subject: [PATCH 201/219] GH-126748: amend configure.rst description for the 'build_wasm' make target (#126687) Clarified the documentation around how the build_wasm target is selected. --- Doc/using/configure.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Doc/using/configure.rst b/Doc/using/configure.rst index 6fd623c5176778..e7733a6dc11451 100644 --- a/Doc/using/configure.rst +++ b/Doc/using/configure.rst @@ -1088,7 +1088,8 @@ CPython project) this is usually the ``all`` target. The all`` will build. The three choices are: * ``profile-opt`` (configured with ``--enable-optimizations``) -* ``build_wasm`` (configured with ``--with-emscripten-target``) +* ``build_wasm`` (chosen if the host platform matches ``wasm32-wasi*`` or + ``wasm32-emscripten``) * ``build_all`` (configured without explicitly using either of the others) Depending on the most recent source file changes, Make will rebuild From ed81971e6b26c34445f06850192b34458b029337 Mon Sep 17 00:00:00 2001 From: RanKKI Date: Sun, 17 Nov 2024 10:01:52 +1100 Subject: [PATCH 202/219] gh-124452: Fix header mismatches when folding/unfolding with email message (#125919) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The header-folder of the new email API has a long standing known buglet where if the first token is longer than max_line_length, it puts that token on the next line. It turns out there is also a *parsing* bug when parsing such a header: the space prefixing that first, non-empty line gets preserved and tacked on to the start of the header value, which is not the expected behavior per the RFCs. The bug arises from the fact that the parser assumed that there would be at least one token on the line with the header, which is going to be true for probably every email producer other than the python email library with its folding buglet. Clearly, though, this is a case that needs to be handled correctly. The fix is simple: strip the blanks off the start of the whole value, not just the first physical line of the value. Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Lib/email/_policybase.py | 4 +- Lib/email/policy.py | 4 +- Lib/test/test_email/test_message.py | 50 ++++++++++++++++++- ...-10-24-10-49-47.gh-issue-124452.eqTRgx.rst | 4 ++ 4 files changed, 56 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-10-24-10-49-47.gh-issue-124452.eqTRgx.rst diff --git a/Lib/email/_policybase.py b/Lib/email/_policybase.py index c7694a44e26639..4b63b97217a835 100644 --- a/Lib/email/_policybase.py +++ b/Lib/email/_policybase.py @@ -302,12 +302,12 @@ def header_source_parse(self, sourcelines): """+ The name is parsed as everything up to the ':' and returned unmodified. The value is determined by stripping leading whitespace off the - remainder of the first line, joining all subsequent lines together, and + remainder of the first line joined with all subsequent lines, and stripping any trailing carriage return or linefeed characters. """ name, value = sourcelines[0].split(':', 1) - value = value.lstrip(' \t') + ''.join(sourcelines[1:]) + value = ''.join((value, *sourcelines[1:])).lstrip(' \t\r\n') return (name, value.rstrip('\r\n')) def header_store_parse(self, name, value): diff --git a/Lib/email/policy.py b/Lib/email/policy.py index 46b7de5bb6d8ae..6e109b65011a44 100644 --- a/Lib/email/policy.py +++ b/Lib/email/policy.py @@ -119,13 +119,13 @@ def header_source_parse(self, sourcelines): """+ The name is parsed as everything up to the ':' and returned unmodified. The value is determined by stripping leading whitespace off the - remainder of the first line, joining all subsequent lines together, and + remainder of the first line joined with all subsequent lines, and stripping any trailing carriage return or linefeed characters. (This is the same as Compat32). """ name, value = sourcelines[0].split(':', 1) - value = value.lstrip(' \t') + ''.join(sourcelines[1:]) + value = ''.join((value, *sourcelines[1:])).lstrip(' \t\r\n') return (name, value.rstrip('\r\n')) def header_store_parse(self, name, value): diff --git a/Lib/test/test_email/test_message.py b/Lib/test/test_email/test_message.py index 034f7626c1fc7c..96979db27f3a21 100644 --- a/Lib/test/test_email/test_message.py +++ b/Lib/test/test_email/test_message.py @@ -1,6 +1,6 @@ -import unittest import textwrap -from email import policy, message_from_string +import unittest +from email import message_from_bytes, message_from_string, policy from email.message import EmailMessage, MIMEPart from test.test_email import TestEmailBase, parameterize @@ -958,6 +958,52 @@ def test_folding_with_utf8_encoding_8(self): b'123456789-123456789\n 123456789 Hello ' b'=?utf-8?q?W=C3=B6rld!?= 123456789 123456789\n\n') + def test_folding_with_short_nospace_1(self): + # bpo-36520 + # + # Fold a line that contains a long whitespace after + # the fold point. + + m = EmailMessage(policy.default) + m['Message-ID'] = '123456789' * 3 + parsed_msg = message_from_bytes(m.as_bytes(), policy=policy.default) + self.assertEqual(parsed_msg['Message-ID'], m['Message-ID']) + + def test_folding_with_long_nospace_default_policy_1(self): + # Fixed: https://github.com/python/cpython/issues/124452 + # + # When the value is too long, it should be converted back + # to its original form without any modifications. + + m = EmailMessage(policy.default) + message = '123456789' * 10 + m['Message-ID'] = message + self.assertEqual(m.as_bytes(), + f'Message-ID:\n {message}\n\n'.encode()) + parsed_msg = message_from_bytes(m.as_bytes(), policy=policy.default) + self.assertEqual(parsed_msg['Message-ID'], m['Message-ID']) + + def test_folding_with_long_nospace_compat32_policy_1(self): + m = EmailMessage(policy.compat32) + message = '123456789' * 10 + m['Message-ID'] = message + parsed_msg = message_from_bytes(m.as_bytes(), policy=policy.default) + self.assertEqual(parsed_msg['Message-ID'], m['Message-ID']) + + def test_folding_with_long_nospace_smtp_policy_1(self): + m = EmailMessage(policy.SMTP) + message = '123456789' * 10 + m['Message-ID'] = message + parsed_msg = message_from_bytes(m.as_bytes(), policy=policy.default) + self.assertEqual(parsed_msg['Message-ID'], m['Message-ID']) + + def test_folding_with_long_nospace_http_policy_1(self): + m = EmailMessage(policy.HTTP) + message = '123456789' * 10 + m['Message-ID'] = message + parsed_msg = message_from_bytes(m.as_bytes(), policy=policy.default) + self.assertEqual(parsed_msg['Message-ID'], m['Message-ID']) + def test_get_body_malformed(self): """test for bpo-42892""" msg = textwrap.dedent("""\ diff --git a/Misc/NEWS.d/next/Library/2024-10-24-10-49-47.gh-issue-124452.eqTRgx.rst b/Misc/NEWS.d/next/Library/2024-10-24-10-49-47.gh-issue-124452.eqTRgx.rst new file mode 100644 index 00000000000000..b0d63794022db4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-10-24-10-49-47.gh-issue-124452.eqTRgx.rst @@ -0,0 +1,4 @@ +Fix an issue in :meth:`email.policy.EmailPolicy.header_source_parse` and +:meth:`email.policy.Compat32.header_source_parse` that introduced spurious +leading whitespaces into header values when the header includes a newline +character after the header name delimiter (``:``) and before the value. From acbd5c9c6c62dac34d2ed1a789d36fe61841c16d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns=20=F0=9F=87=B5=F0=9F=87=B8?= Date: Sun, 17 Nov 2024 00:07:25 +0000 Subject: [PATCH 203/219] GH-126789: fix some sysconfig data on late site initializations --- Lib/sysconfig/__init__.py | 18 ++++- Lib/test/support/venv.py | 70 +++++++++++++++++ Lib/test/test_sysconfig.py | 75 +++++++++++++++++++ ...-11-13-22-25-57.gh-issue-126789.lKzlc7.rst | 4 + 4 files changed, 163 insertions(+), 4 deletions(-) create mode 100644 Lib/test/support/venv.py create mode 100644 Misc/NEWS.d/next/Library/2024-11-13-22-25-57.gh-issue-126789.lKzlc7.rst diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py index 43f9276799b848..ec3b638f00766d 100644 --- a/Lib/sysconfig/__init__.py +++ b/Lib/sysconfig/__init__.py @@ -173,9 +173,7 @@ def joinuser(*args): _PY_VERSION = sys.version.split()[0] _PY_VERSION_SHORT = f'{sys.version_info[0]}.{sys.version_info[1]}' _PY_VERSION_SHORT_NO_DOT = f'{sys.version_info[0]}{sys.version_info[1]}' -_PREFIX = os.path.normpath(sys.prefix) _BASE_PREFIX = os.path.normpath(sys.base_prefix) -_EXEC_PREFIX = os.path.normpath(sys.exec_prefix) _BASE_EXEC_PREFIX = os.path.normpath(sys.base_exec_prefix) # Mutex guarding initialization of _CONFIG_VARS. _CONFIG_VARS_LOCK = threading.RLock() @@ -466,8 +464,10 @@ def _init_config_vars(): # Normalized versions of prefix and exec_prefix are handy to have; # in fact, these are the standard versions used most places in the # Distutils. - _CONFIG_VARS['prefix'] = _PREFIX - _CONFIG_VARS['exec_prefix'] = _EXEC_PREFIX + _PREFIX = os.path.normpath(sys.prefix) + _EXEC_PREFIX = os.path.normpath(sys.exec_prefix) + _CONFIG_VARS['prefix'] = _PREFIX # FIXME: This gets overwriten by _init_posix. + _CONFIG_VARS['exec_prefix'] = _EXEC_PREFIX # FIXME: This gets overwriten by _init_posix. _CONFIG_VARS['py_version'] = _PY_VERSION _CONFIG_VARS['py_version_short'] = _PY_VERSION_SHORT _CONFIG_VARS['py_version_nodot'] = _PY_VERSION_SHORT_NO_DOT @@ -540,6 +540,7 @@ def get_config_vars(*args): With arguments, return a list of values that result from looking up each argument in the configuration variable dictionary. """ + global _CONFIG_VARS_INITIALIZED # Avoid claiming the lock once initialization is complete. if not _CONFIG_VARS_INITIALIZED: @@ -550,6 +551,15 @@ def get_config_vars(*args): # don't re-enter init_config_vars(). if _CONFIG_VARS is None: _init_config_vars() + else: + # If the site module initialization happened after _CONFIG_VARS was + # initialized, a virtual environment might have been activated, resulting in + # variables like sys.prefix changing their value, so we need to re-init the + # config vars (see GH-126789). + if _CONFIG_VARS['base'] != os.path.normpath(sys.prefix): + with _CONFIG_VARS_LOCK: + _CONFIG_VARS_INITIALIZED = False + _init_config_vars() if args: vals = [] diff --git a/Lib/test/support/venv.py b/Lib/test/support/venv.py new file mode 100644 index 00000000000000..78e6a51ec1815e --- /dev/null +++ b/Lib/test/support/venv.py @@ -0,0 +1,70 @@ +import contextlib +import logging +import os +import subprocess +import shlex +import sys +import sysconfig +import tempfile +import venv + + +class VirtualEnvironment: + def __init__(self, prefix, **venv_create_args): + self._logger = logging.getLogger(self.__class__.__name__) + venv.create(prefix, **venv_create_args) + self._prefix = prefix + self._paths = sysconfig.get_paths( + scheme='venv', + vars={'base': self.prefix}, + expand=True, + ) + + @classmethod + @contextlib.contextmanager + def from_tmpdir(cls, *, prefix=None, dir=None, **venv_create_args): + delete = not bool(os.environ.get('PYTHON_TESTS_KEEP_VENV')) + with tempfile.TemporaryDirectory(prefix=prefix, dir=dir, delete=delete) as tmpdir: + yield cls(tmpdir, **venv_create_args) + + @property + def prefix(self): + return self._prefix + + @property + def paths(self): + return self._paths + + @property + def interpreter(self): + return os.path.join(self.paths['scripts'], os.path.basename(sys.executable)) + + def _format_output(self, name, data, indent='\t'): + if not data: + return indent + f'{name}: (none)' + if len(data.splitlines()) == 1: + return indent + f'{name}: {data}' + else: + prefixed_lines = '\n'.join(indent + '> ' + line for line in data.splitlines()) + return indent + f'{name}:\n' + prefixed_lines + + def run(self, *args, **subprocess_args): + if subprocess_args.get('shell'): + raise ValueError('Running the subprocess in shell mode is not supported.') + default_args = { + 'capture_output': True, + 'check': True, + } + try: + result = subprocess.run([self.interpreter, *args], **default_args | subprocess_args) + except subprocess.CalledProcessError as e: + if e.returncode != 0: + self._logger.error( + f'Interpreter returned non-zero exit status {e.returncode}.\n' + + self._format_output('COMMAND', shlex.join(e.cmd)) + '\n' + + self._format_output('STDOUT', e.stdout.decode()) + '\n' + + self._format_output('STDERR', e.stderr.decode()) + '\n' + ) + raise + else: + return result diff --git a/Lib/test/test_sysconfig.py b/Lib/test/test_sysconfig.py index 1ade49281b4e26..4f9541b6a0b726 100644 --- a/Lib/test/test_sysconfig.py +++ b/Lib/test/test_sysconfig.py @@ -5,6 +5,8 @@ import os import subprocess import shutil +import json +import textwrap from copy import copy from test.support import ( @@ -17,6 +19,7 @@ from test.support.import_helper import import_module from test.support.os_helper import (TESTFN, unlink, skip_unless_symlink, change_cwd) +from test.support.venv import VirtualEnvironment import sysconfig from sysconfig import (get_paths, get_platform, get_config_vars, @@ -101,6 +104,12 @@ def _cleanup_testfn(self): elif os.path.isdir(path): shutil.rmtree(path) + def venv(self, **venv_create_args): + return VirtualEnvironment.from_tmpdir( + prefix=f'{self.id()}-venv-', + **venv_create_args, + ) + def test_get_path_names(self): self.assertEqual(get_path_names(), sysconfig._SCHEME_KEYS) @@ -582,6 +591,72 @@ def test_osx_ext_suffix(self): suffix = sysconfig.get_config_var('EXT_SUFFIX') self.assertTrue(suffix.endswith('-darwin.so'), suffix) + @unittest.skipIf(sys.platform == 'wasi', 'venv is unsupported on WASI') + def test_config_vars_depend_on_site_initialization(self): + script = textwrap.dedent(""" + import sysconfig + + config_vars = sysconfig.get_config_vars() + + import json + print(json.dumps(config_vars, indent=2)) + """) + + with self.venv() as venv: + site_config_vars = json.loads(venv.run('-c', script).stdout) + no_site_config_vars = json.loads(venv.run('-S', '-c', script).stdout) + + self.assertNotEqual(site_config_vars, no_site_config_vars) + # With the site initialization, the virtual environment should be enabled. + self.assertEqual(site_config_vars['base'], venv.prefix) + self.assertEqual(site_config_vars['platbase'], venv.prefix) + #self.assertEqual(site_config_vars['prefix'], venv.prefix) # # FIXME: prefix gets overwriten by _init_posix + # Without the site initialization, the virtual environment should be disabled. + self.assertEqual(no_site_config_vars['base'], site_config_vars['installed_base']) + self.assertEqual(no_site_config_vars['platbase'], site_config_vars['installed_platbase']) + + @unittest.skipIf(sys.platform == 'wasi', 'venv is unsupported on WASI') + def test_config_vars_recalculation_after_site_initialization(self): + script = textwrap.dedent(""" + import sysconfig + + before = sysconfig.get_config_vars() + + import site + site.main() + + after = sysconfig.get_config_vars() + + import json + print(json.dumps({'before': before, 'after': after}, indent=2)) + """) + + with self.venv() as venv: + config_vars = json.loads(venv.run('-S', '-c', script).stdout) + + self.assertNotEqual(config_vars['before'], config_vars['after']) + self.assertEqual(config_vars['after']['base'], venv.prefix) + #self.assertEqual(config_vars['after']['prefix'], venv.prefix) # FIXME: prefix gets overwriten by _init_posix + #self.assertEqual(config_vars['after']['exec_prefix'], venv.prefix) # FIXME: exec_prefix gets overwriten by _init_posix + + @unittest.skipIf(sys.platform == 'wasi', 'venv is unsupported on WASI') + def test_paths_depend_on_site_initialization(self): + script = textwrap.dedent(""" + import sysconfig + + paths = sysconfig.get_paths() + + import json + print(json.dumps(paths, indent=2)) + """) + + with self.venv() as venv: + site_paths = json.loads(venv.run('-c', script).stdout) + no_site_paths = json.loads(venv.run('-S', '-c', script).stdout) + + self.assertNotEqual(site_paths, no_site_paths) + + class MakefileTests(unittest.TestCase): @unittest.skipIf(sys.platform.startswith('win'), diff --git a/Misc/NEWS.d/next/Library/2024-11-13-22-25-57.gh-issue-126789.lKzlc7.rst b/Misc/NEWS.d/next/Library/2024-11-13-22-25-57.gh-issue-126789.lKzlc7.rst new file mode 100644 index 00000000000000..09d4d2e5ab9037 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-11-13-22-25-57.gh-issue-126789.lKzlc7.rst @@ -0,0 +1,4 @@ +Fixed the values of :py:func:`sysconfig.get_config_vars`, +:py:func:`sysconfig.get_paths`, and their siblings when the :py:mod:`site` +initialization happens after :py:mod:`sysconfig` has built a cache for +:py:func:`sysconfig.get_config_vars`. From 9d6366b60d01305fc5e45100e0cd13e358aa397d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns=20=F0=9F=87=B5=F0=9F=87=B8?= Date: Sun, 17 Nov 2024 01:56:01 +0000 Subject: [PATCH 204/219] GH-126920: fix Makefile overwriting sysconfig.get_config_vars --- Lib/sysconfig/__init__.py | 3 +- Lib/test/test_sysconfig.py | 32 +++++++++++++++++++ ...-11-17-01-14-59.gh-issue-126920.s8-f_L.rst | 5 +++ 3 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2024-11-17-01-14-59.gh-issue-126920.s8-f_L.rst diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py index ec3b638f00766d..67a071963d8c7d 100644 --- a/Lib/sysconfig/__init__.py +++ b/Lib/sysconfig/__init__.py @@ -353,7 +353,8 @@ def _init_posix(vars): else: _temp = __import__(name, globals(), locals(), ['build_time_vars'], 0) build_time_vars = _temp.build_time_vars - vars.update(build_time_vars) + # GH-126920: Make sure we don't overwrite any of the keys already set + vars.update(build_time_vars | vars) def _init_non_posix(vars): """Initialize the module as appropriate for NT""" diff --git a/Lib/test/test_sysconfig.py b/Lib/test/test_sysconfig.py index 4f9541b6a0b726..c7acfe728bb664 100644 --- a/Lib/test/test_sysconfig.py +++ b/Lib/test/test_sysconfig.py @@ -656,6 +656,38 @@ def test_paths_depend_on_site_initialization(self): self.assertNotEqual(site_paths, no_site_paths) + @unittest.skipIf(sys.platform == 'wasi', 'venv is unsupported on WASI') + def test_makefile_overwrites_config_vars(self): + script = textwrap.dedent(""" + import sys, sysconfig + + data = { + 'prefix': sys.prefix, + 'exec_prefix': sys.exec_prefix, + 'base_prefix': sys.base_prefix, + 'base_exec_prefix': sys.base_exec_prefix, + 'config_vars': sysconfig.get_config_vars(), + } + + import json + print(json.dumps(data, indent=2)) + """) + + # We need to run the test inside a virtual environment so that + # sys.prefix/sys.exec_prefix have a different value from the + # prefix/exec_prefix Makefile variables. + with self.venv() as venv: + data = json.loads(venv.run('-c', script).stdout) + + # We expect sysconfig.get_config_vars to correctly reflect sys.prefix/sys.exec_prefix + self.assertEqual(data['prefix'], data['config_vars']['prefix']) + self.assertEqual(data['exec_prefix'], data['config_vars']['exec_prefix']) + # As a sanity check, just make sure sys.prefix/sys.exec_prefix really + # are different from the Makefile values. + # sys.base_prefix/sys.base_exec_prefix should reflect the value of the + # prefix/exec_prefix Makefile variables, so we use them in the comparison. + self.assertNotEqual(data['prefix'], data['base_prefix']) + self.assertNotEqual(data['exec_prefix'], data['base_exec_prefix']) class MakefileTests(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Library/2024-11-17-01-14-59.gh-issue-126920.s8-f_L.rst b/Misc/NEWS.d/next/Library/2024-11-17-01-14-59.gh-issue-126920.s8-f_L.rst new file mode 100644 index 00000000000000..6966aec380fae9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-11-17-01-14-59.gh-issue-126920.s8-f_L.rst @@ -0,0 +1,5 @@ +Fix the ``prefix`` and ``exec_prefix`` keys from +:py:func:`sysconfig.get_config_vars` incorrectly having the same value as +:py:const:`sys.base_prefix` and :py:const:`sys.base_exec_prefix`, +respectively, inside virtual environments. They now accurately reflect +:py:const:`sys.prefix` and :py:const:`sys.exec_prefix`. From 0c5c80928c476ac0dcb9a053b15a562af899cfba Mon Sep 17 00:00:00 2001 From: beavailable Date: Mon, 18 Nov 2024 03:32:35 +0800 Subject: [PATCH 205/219] gh-126896: Fix docs about `asyncio.start_server()` (#126897) --- Doc/library/asyncio-stream.rst | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Doc/library/asyncio-stream.rst b/Doc/library/asyncio-stream.rst index 3fdc79b3c6896c..48f2890c5eef8c 100644 --- a/Doc/library/asyncio-stream.rst +++ b/Doc/library/asyncio-stream.rst @@ -92,7 +92,8 @@ and work with streams: family=socket.AF_UNSPEC, \ flags=socket.AI_PASSIVE, sock=None, \ backlog=100, ssl=None, reuse_address=None, \ - reuse_port=None, ssl_handshake_timeout=None, \ + reuse_port=None, keep_alive=None, \ + ssl_handshake_timeout=None, \ ssl_shutdown_timeout=None, start_serving=True) Start a socket server. @@ -128,6 +129,9 @@ and work with streams: .. versionchanged:: 3.11 Added the *ssl_shutdown_timeout* parameter. + .. versionchanged:: 3.13 + Added the *keep_alive* parameter. + .. rubric:: Unix Sockets From 500a4712bb42355eeb785ed5b9d71507384d18bc Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Mon, 18 Nov 2024 07:43:41 +0800 Subject: [PATCH 206/219] gh-126167: Modify iOS Testbed to read arguments from Info.plist (#126169) Modify iOS Testbed to read arguments from Info.plist. --- ...-10-30-13-59-07.gh-issue-126167.j5cCWE.rst | 2 + iOS/README.rst | 14 ++-- .../iOSTestbed.xcodeproj/project.pbxproj | 10 ++- iOS/testbed/iOSTestbed/app/README | 7 ++ iOS/testbed/iOSTestbed/app_packages/README | 7 ++ iOS/testbed/iOSTestbed/iOSTestbed-Info.plist | 14 +++- iOS/testbed/iOSTestbedTests/iOSTestbedTests.m | 80 ++++++++++++++----- 7 files changed, 106 insertions(+), 28 deletions(-) create mode 100644 Misc/NEWS.d/next/Tools-Demos/2024-10-30-13-59-07.gh-issue-126167.j5cCWE.rst create mode 100644 iOS/testbed/iOSTestbed/app/README create mode 100644 iOS/testbed/iOSTestbed/app_packages/README diff --git a/Misc/NEWS.d/next/Tools-Demos/2024-10-30-13-59-07.gh-issue-126167.j5cCWE.rst b/Misc/NEWS.d/next/Tools-Demos/2024-10-30-13-59-07.gh-issue-126167.j5cCWE.rst new file mode 100644 index 00000000000000..338160e69fd522 --- /dev/null +++ b/Misc/NEWS.d/next/Tools-Demos/2024-10-30-13-59-07.gh-issue-126167.j5cCWE.rst @@ -0,0 +1,2 @@ +The iOS testbed was modified so that it can be used by third-party projects +for testing purposes. diff --git a/iOS/README.rst b/iOS/README.rst index 4d7c344d5e9e17..e33455eef8f44a 100644 --- a/iOS/README.rst +++ b/iOS/README.rst @@ -351,13 +351,13 @@ Running specific tests ^^^^^^^^^^^^^^^^^^^^^^ As the test suite is being executed on an iOS simulator, it is not possible to -pass in command line arguments to configure test suite operation. To work around -this limitation, the arguments that would normally be passed as command line -arguments are configured as a static string at the start of the XCTest method -``- (void)testPython`` in ``iOSTestbedTests.m``. To pass an argument to the test -suite, add a a string to the ``argv`` definition. These arguments will be passed -to the test suite as if they had been passed to ``python -m test`` at the -command line. +pass in command line arguments to configure test suite operation. To work +around this limitation, the arguments that would normally be passed as command +line arguments are configured as part of the ``iOSTestbed-Info.plist`` file +that is used to configure the iOS testbed app. In this file, the ``TestArgs`` +key is an array containing the arguments that would be passed to ``python -m`` +on the command line (including ``test`` in position 0, the name of the test +module to be executed). Disabling automated breakpoints ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/iOS/testbed/iOSTestbed.xcodeproj/project.pbxproj b/iOS/testbed/iOSTestbed.xcodeproj/project.pbxproj index d57cfc3dbe0304..6819ac0eeed95f 100644 --- a/iOS/testbed/iOSTestbed.xcodeproj/project.pbxproj +++ b/iOS/testbed/iOSTestbed.xcodeproj/project.pbxproj @@ -17,6 +17,8 @@ 607A66502B0EFFE00010BFC8 /* Python.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 607A664A2B0EFB310010BFC8 /* Python.xcframework */; }; 607A66512B0EFFE00010BFC8 /* Python.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 607A664A2B0EFB310010BFC8 /* Python.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 607A66582B0F079F0010BFC8 /* dylib-Info-template.plist in Resources */ = {isa = PBXBuildFile; fileRef = 607A66572B0F079F0010BFC8 /* dylib-Info-template.plist */; }; + 608619542CB77BA900F46182 /* app_packages in Resources */ = {isa = PBXBuildFile; fileRef = 608619532CB77BA900F46182 /* app_packages */; }; + 608619562CB7819B00F46182 /* app in Resources */ = {isa = PBXBuildFile; fileRef = 608619552CB7819B00F46182 /* app */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -66,6 +68,8 @@ 607A664A2B0EFB310010BFC8 /* Python.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = Python.xcframework; sourceTree = ""; }; 607A66572B0F079F0010BFC8 /* dylib-Info-template.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "dylib-Info-template.plist"; sourceTree = ""; }; 607A66592B0F08600010BFC8 /* iOSTestbed-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "iOSTestbed-Info.plist"; sourceTree = ""; }; + 608619532CB77BA900F46182 /* app_packages */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app_packages; sourceTree = ""; }; + 608619552CB7819B00F46182 /* app */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -111,6 +115,8 @@ 607A66142B0EFA380010BFC8 /* iOSTestbed */ = { isa = PBXGroup; children = ( + 608619552CB7819B00F46182 /* app */, + 608619532CB77BA900F46182 /* app_packages */, 607A66592B0F08600010BFC8 /* iOSTestbed-Info.plist */, 607A66572B0F079F0010BFC8 /* dylib-Info-template.plist */, 607A66152B0EFA380010BFC8 /* AppDelegate.h */, @@ -223,7 +229,9 @@ files = ( 607A66252B0EFA390010BFC8 /* LaunchScreen.storyboard in Resources */, 607A66582B0F079F0010BFC8 /* dylib-Info-template.plist in Resources */, + 608619562CB7819B00F46182 /* app in Resources */, 607A66222B0EFA390010BFC8 /* Assets.xcassets in Resources */, + 608619542CB77BA900F46182 /* app_packages in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -273,7 +281,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "set -e\n\ninstall_dylib () {\n INSTALL_BASE=$1\n FULL_EXT=$2\n\n # The name of the extension file\n EXT=$(basename \"$FULL_EXT\")\n # The location of the extension file, relative to the bundle\n RELATIVE_EXT=${FULL_EXT#$CODESIGNING_FOLDER_PATH/} \n # The path to the extension file, relative to the install base\n PYTHON_EXT=${RELATIVE_EXT/$INSTALL_BASE/}\n # The full dotted name of the extension module, constructed from the file path.\n FULL_MODULE_NAME=$(echo $PYTHON_EXT | cut -d \".\" -f 1 | tr \"/\" \".\"); \n # A bundle identifier; not actually used, but required by Xcode framework packaging\n FRAMEWORK_BUNDLE_ID=$(echo $PRODUCT_BUNDLE_IDENTIFIER.$FULL_MODULE_NAME | tr \"_\" \"-\")\n # The name of the framework folder.\n FRAMEWORK_FOLDER=\"Frameworks/$FULL_MODULE_NAME.framework\"\n\n # If the framework folder doesn't exist, create it.\n if [ ! -d \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER\" ]; then\n echo \"Creating framework for $RELATIVE_EXT\" \n mkdir -p \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER\"\n cp \"$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n plutil -replace CFBundleExecutable -string \"$FULL_MODULE_NAME\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n plutil -replace CFBundleIdentifier -string \"$FRAMEWORK_BUNDLE_ID\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n fi\n \n echo \"Installing binary for $FRAMEWORK_FOLDER/$FULL_MODULE_NAME\" \n mv \"$FULL_EXT\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME\"\n # Create a placeholder .fwork file where the .so was\n echo \"$FRAMEWORK_FOLDER/$FULL_MODULE_NAME\" > ${FULL_EXT%.so}.fwork\n # Create a back reference to the .so file location in the framework\n echo \"${RELATIVE_EXT%.so}.fwork\" > \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME.origin\" \n}\n\nPYTHON_VER=$(ls -1 \"$CODESIGNING_FOLDER_PATH/python/lib\")\necho \"Install Python $PYTHON_VER standard library extension modules...\"\nfind \"$CODESIGNING_FOLDER_PATH/python/lib/$PYTHON_VER/lib-dynload\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib python/lib/$PYTHON_VER/lib-dynload/ \"$FULL_EXT\"\ndone\n\n# Clean up dylib template \nrm -f \"$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist\"\necho \"Signing frameworks as $EXPANDED_CODE_SIGN_IDENTITY_NAME ($EXPANDED_CODE_SIGN_IDENTITY)...\"\nfind \"$CODESIGNING_FOLDER_PATH/Frameworks\" -name \"*.framework\" -exec /usr/bin/codesign --force --sign \"$EXPANDED_CODE_SIGN_IDENTITY\" ${OTHER_CODE_SIGN_FLAGS:-} -o runtime --timestamp=none --preserve-metadata=identifier,entitlements,flags --generate-entitlement-der \"{}\" \\; \n"; + shellScript = "set -e\n\ninstall_dylib () {\n INSTALL_BASE=$1\n FULL_EXT=$2\n\n # The name of the extension file\n EXT=$(basename \"$FULL_EXT\")\n # The location of the extension file, relative to the bundle\n RELATIVE_EXT=${FULL_EXT#$CODESIGNING_FOLDER_PATH/} \n # The path to the extension file, relative to the install base\n PYTHON_EXT=${RELATIVE_EXT/$INSTALL_BASE/}\n # The full dotted name of the extension module, constructed from the file path.\n FULL_MODULE_NAME=$(echo $PYTHON_EXT | cut -d \".\" -f 1 | tr \"/\" \".\"); \n # A bundle identifier; not actually used, but required by Xcode framework packaging\n FRAMEWORK_BUNDLE_ID=$(echo $PRODUCT_BUNDLE_IDENTIFIER.$FULL_MODULE_NAME | tr \"_\" \"-\")\n # The name of the framework folder.\n FRAMEWORK_FOLDER=\"Frameworks/$FULL_MODULE_NAME.framework\"\n\n # If the framework folder doesn't exist, create it.\n if [ ! -d \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER\" ]; then\n echo \"Creating framework for $RELATIVE_EXT\" \n mkdir -p \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER\"\n cp \"$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n plutil -replace CFBundleExecutable -string \"$FULL_MODULE_NAME\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n plutil -replace CFBundleIdentifier -string \"$FRAMEWORK_BUNDLE_ID\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n fi\n \n echo \"Installing binary for $FRAMEWORK_FOLDER/$FULL_MODULE_NAME\" \n mv \"$FULL_EXT\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME\"\n # Create a placeholder .fwork file where the .so was\n echo \"$FRAMEWORK_FOLDER/$FULL_MODULE_NAME\" > ${FULL_EXT%.so}.fwork\n # Create a back reference to the .so file location in the framework\n echo \"${RELATIVE_EXT%.so}.fwork\" > \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME.origin\" \n}\n\nPYTHON_VER=$(ls -1 \"$CODESIGNING_FOLDER_PATH/python/lib\")\necho \"Install Python $PYTHON_VER standard library extension modules...\"\nfind \"$CODESIGNING_FOLDER_PATH/python/lib/$PYTHON_VER/lib-dynload\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib python/lib/$PYTHON_VER/lib-dynload/ \"$FULL_EXT\"\ndone\necho \"Install app package extension modules...\"\nfind \"$CODESIGNING_FOLDER_PATH/app_packages\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib app_packages/ \"$FULL_EXT\"\ndone\necho \"Install app extension modules...\"\nfind \"$CODESIGNING_FOLDER_PATH/app\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib app/ \"$FULL_EXT\"\ndone\n\n# Clean up dylib template \nrm -f \"$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist\"\necho \"Signing frameworks as $EXPANDED_CODE_SIGN_IDENTITY_NAME ($EXPANDED_CODE_SIGN_IDENTITY)...\"\nfind \"$CODESIGNING_FOLDER_PATH/Frameworks\" -name \"*.framework\" -exec /usr/bin/codesign --force --sign \"$EXPANDED_CODE_SIGN_IDENTITY\" ${OTHER_CODE_SIGN_FLAGS:-} -o runtime --timestamp=none --preserve-metadata=identifier,entitlements,flags --generate-entitlement-der \"{}\" \\; \n"; }; /* End PBXShellScriptBuildPhase section */ diff --git a/iOS/testbed/iOSTestbed/app/README b/iOS/testbed/iOSTestbed/app/README new file mode 100644 index 00000000000000..af22c685f87976 --- /dev/null +++ b/iOS/testbed/iOSTestbed/app/README @@ -0,0 +1,7 @@ +This folder can contain any Python application code. + +During the build, any binary modules found in this folder will be processed into +iOS Framework form. + +When the test suite runs, this folder will be on the PYTHONPATH, and will be the +working directory for the test suite. diff --git a/iOS/testbed/iOSTestbed/app_packages/README b/iOS/testbed/iOSTestbed/app_packages/README new file mode 100644 index 00000000000000..42d7fdeb813250 --- /dev/null +++ b/iOS/testbed/iOSTestbed/app_packages/README @@ -0,0 +1,7 @@ +This folder can be a target for installing any Python dependencies needed by the +test suite. + +During the build, any binary modules found in this folder will be processed into +iOS Framework form. + +When the test suite runs, this folder will be on the PYTHONPATH. diff --git a/iOS/testbed/iOSTestbed/iOSTestbed-Info.plist b/iOS/testbed/iOSTestbed/iOSTestbed-Info.plist index e2aa460b6fd5ee..a582f42a212783 100644 --- a/iOS/testbed/iOSTestbed/iOSTestbed-Info.plist +++ b/iOS/testbed/iOSTestbed/iOSTestbed-Info.plist @@ -41,8 +41,18 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight - MainModule - ios + TestArgs + + test + -uall + --single-process + --rerun + -W + + UIApplicationSceneManifest UIApplicationSupportsMultipleScenes diff --git a/iOS/testbed/iOSTestbedTests/iOSTestbedTests.m b/iOS/testbed/iOSTestbedTests/iOSTestbedTests.m index 9bf502a808eb88..db00d43da85cbc 100644 --- a/iOS/testbed/iOSTestbedTests/iOSTestbedTests.m +++ b/iOS/testbed/iOSTestbedTests/iOSTestbedTests.m @@ -9,30 +9,38 @@ @implementation iOSTestbedTests - (void)testPython { - // Arguments to pass into the test suite runner. - // argv[0] must identify the process; any subsequent arg - // will be handled as if it were an argument to `python -m test` - const char *argv[] = { - "iOSTestbed", // argv[0] is the process that is running. - "-uall", // Enable all resources - "--single-process", // always run all tests sequentially in a single process - "--rerun", // Re-run failed tests in verbose mode - "-W", // Display test output on failure - // To run a subset of tests, add the test names below; e.g., - // "test_os", - // "test_sys", - }; - - // Start a Python interpreter. + const char **argv; int exit_code; + int failed; PyStatus status; PyPreConfig preconfig; PyConfig config; + PyObject *sys_module; + PyObject *sys_path_attr; + NSArray *test_args; NSString *python_home; + NSString *path; wchar_t *wtmp_str; NSString *resourcePath = [[NSBundle mainBundle] resourcePath]; + // Disable all color, as the Xcode log can't display color + setenv("NO_COLOR", "1", true); + + // Arguments to pass into the test suite runner. + // argv[0] must identify the process; any subsequent arg + // will be handled as if it were an argument to `python -m test` + test_args = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"TestArgs"]; + if (test_args == NULL) { + NSLog(@"Unable to identify test arguments."); + } + argv = malloc(sizeof(char *) * ([test_args count] + 1)); + argv[0] = "iOSTestbed"; + for (int i = 1; i < [test_args count]; i++) { + argv[i] = [[test_args objectAtIndex:i] UTF8String]; + } + NSLog(@"Test command: %@", test_args); + // Generate an isolated Python configuration. NSLog(@"Configuring isolated Python..."); PyPreConfig_InitIsolatedConfig(&preconfig); @@ -50,7 +58,7 @@ - (void)testPython { // Ensure that signal handlers are installed config.install_signal_handlers = 1; // Run the test module. - config.run_module = Py_DecodeLocale("test", NULL); + config.run_module = Py_DecodeLocale([[test_args objectAtIndex:0] UTF8String], NULL); // For debugging - enable verbose mode. // config.verbose = 1; @@ -83,7 +91,7 @@ - (void)testPython { } NSLog(@"Configure argc/argv..."); - status = PyConfig_SetBytesArgv(&config, sizeof(argv) / sizeof(char *), (char**) argv); + status = PyConfig_SetBytesArgv(&config, [test_args count], (char**) argv); if (PyStatus_Exception(status)) { XCTFail(@"Unable to configure argc/argv: %s", status.err_msg); PyConfig_Clear(&config); @@ -98,11 +106,47 @@ - (void)testPython { return; } + sys_module = PyImport_ImportModule("sys"); + if (sys_module == NULL) { + XCTFail(@"Could not import sys module"); + return; + } + + sys_path_attr = PyObject_GetAttrString(sys_module, "path"); + if (sys_path_attr == NULL) { + XCTFail(@"Could not access sys.path"); + return; + } + + // Add the app packages path + path = [NSString stringWithFormat:@"%@/app_packages", resourcePath, nil]; + NSLog(@"App packages path: %@", path); + wtmp_str = Py_DecodeLocale([path UTF8String], NULL); + failed = PyList_Insert(sys_path_attr, 0, PyUnicode_FromString([path UTF8String])); + if (failed) { + XCTFail(@"Unable to add app packages to sys.path"); + return; + } + PyMem_RawFree(wtmp_str); + + path = [NSString stringWithFormat:@"%@/app", resourcePath, nil]; + NSLog(@"App path: %@", path); + wtmp_str = Py_DecodeLocale([path UTF8String], NULL); + failed = PyList_Insert(sys_path_attr, 0, PyUnicode_FromString([path UTF8String])); + if (failed) { + XCTFail(@"Unable to add app to sys.path"); + return; + } + PyMem_RawFree(wtmp_str); + + // Ensure the working directory is the app folder. + chdir([path UTF8String]); + // Start the test suite. Print a separator to differentiate Python startup logs from app logs NSLog(@"---------------------------------------------------------------------------"); exit_code = Py_RunMain(); - XCTAssertEqual(exit_code, 0, @"Python test suite did not pass"); + XCTAssertEqual(exit_code, 0, @"Test suite did not pass"); NSLog(@"---------------------------------------------------------------------------"); From ce453e6c2ffda657d9d728ea6372121e8264418e Mon Sep 17 00:00:00 2001 From: Joseph Martinot-Lagarde Date: Mon, 18 Nov 2024 08:57:32 +0100 Subject: [PATCH 207/219] Doc: Reorganize math module documentation (#126337) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> Co-authored-by: Sergey B Kirpichev --- Doc/library/math.rst | 476 +++++++++++++++++++++++-------------------- 1 file changed, 250 insertions(+), 226 deletions(-) diff --git a/Doc/library/math.rst b/Doc/library/math.rst index 5ce2ad2d6aec47..bf79b23a72bbf9 100644 --- a/Doc/library/math.rst +++ b/Doc/library/math.rst @@ -27,36 +27,39 @@ noted otherwise, all return values are floats. ==================================================== ============================================ -**Number-theoretic and representation functions** +**Number-theoretic functions** -------------------------------------------------------------------------------------------------- -:func:`ceil(x) ` Ceiling of *x*, the smallest integer greater than or equal to *x* :func:`comb(n, k) ` Number of ways to choose *k* items from *n* items without repetition and without order -:func:`copysign(x, y) ` Magnitude (absolute value) of *x* with the sign of *y* -:func:`fabs(x) ` Absolute value of *x* :func:`factorial(n) ` *n* factorial -:func:`floor (x) ` Floor of *x*, the largest integer less than or equal to *x* +:func:`gcd(*integers) ` Greatest common divisor of the integer arguments +:func:`isqrt(n) ` Integer square root of a nonnegative integer *n* +:func:`lcm(*integers) ` Least common multiple of the integer arguments +:func:`perm(n, k) ` Number of ways to choose *k* items from *n* items without repetition and with order + +**Floating point arithmetic** +-------------------------------------------------------------------------------------------------- +:func:`ceil(x) ` Ceiling of *x*, the smallest integer greater than or equal to *x* +:func:`fabs(x) ` Absolute value of *x* +:func:`floor(x) ` Floor of *x*, the largest integer less than or equal to *x* :func:`fma(x, y, z) ` Fused multiply-add operation: ``(x * y) + z`` :func:`fmod(x, y) ` Remainder of division ``x / y`` +:func:`modf(x) ` Fractional and integer parts of *x* +:func:`remainder(x, y) ` Remainder of *x* with respect to *y* +:func:`trunc(x) ` Integer part of *x* + +**Floating point manipulation functions** +-------------------------------------------------------------------------------------------------- +:func:`copysign(x, y) ` Magnitude (absolute value) of *x* with the sign of *y* :func:`frexp(x) ` Mantissa and exponent of *x* -:func:`fsum(iterable) ` Sum of values in the input *iterable* -:func:`gcd(*integers) ` Greatest common divisor of the integer arguments :func:`isclose(a, b, rel_tol, abs_tol) ` Check if the values *a* and *b* are close to each other :func:`isfinite(x) ` Check if *x* is neither an infinity nor a NaN :func:`isinf(x) ` Check if *x* is a positive or negative infinity :func:`isnan(x) ` Check if *x* is a NaN (not a number) -:func:`isqrt(n) ` Integer square root of a nonnegative integer *n* -:func:`lcm(*integers) ` Least common multiple of the integer arguments :func:`ldexp(x, i) ` ``x * (2**i)``, inverse of function :func:`frexp` -:func:`modf(x) ` Fractional and integer parts of *x* :func:`nextafter(x, y, steps) ` Floating-point value *steps* steps after *x* towards *y* -:func:`perm(n, k) ` Number of ways to choose *k* items from *n* items without repetition and with order -:func:`prod(iterable, start) ` Product of elements in the input *iterable* with a *start* value -:func:`remainder(x, y) ` Remainder of *x* with respect to *y* -:func:`sumprod(p, q) ` Sum of products from two iterables *p* and *q* -:func:`trunc(x) ` Integer part of *x* :func:`ulp(x) ` Value of the least significant bit of *x* -**Power and logarithmic functions** +**Power, exponential and logarithmic functions** -------------------------------------------------------------------------------------------------- :func:`cbrt(x) ` Cube root of *x* :func:`exp(x) ` *e* raised to the power *x* @@ -69,6 +72,19 @@ noted otherwise, all return values are floats. :func:`pow(x, y) ` *x* raised to the power *y* :func:`sqrt(x) ` Square root of *x* +**Summation and product functions** +-------------------------------------------------------------------------------------------------- +:func:`dist(p, q) ` Euclidean distance between two points *p* and *q* given as an iterable of coordinates +:func:`fsum(iterable) ` Sum of values in the input *iterable* +:func:`hypot(*coordinates) ` Euclidean norm of an iterable of coordinates +:func:`prod(iterable, start) ` Product of elements in the input *iterable* with a *start* value +:func:`sumprod(p, q) ` Sum of products from two iterables *p* and *q* + +**Angular conversion** +-------------------------------------------------------------------------------------------------- +:func:`degrees(x) ` Convert angle *x* from radians to degrees +:func:`radians(x) ` Convert angle *x* from degrees to radians + **Trigonometric functions** -------------------------------------------------------------------------------------------------- :func:`acos(x) ` Arc cosine of *x* @@ -76,16 +92,9 @@ noted otherwise, all return values are floats. :func:`atan(x) ` Arc tangent of *x* :func:`atan2(y, x) ` ``atan(y / x)`` :func:`cos(x) ` Cosine of *x* -:func:`dist(p, q) ` Euclidean distance between two points *p* and *q* given as an iterable of coordinates -:func:`hypot(*coordinates) ` Euclidean norm of an iterable of coordinates :func:`sin(x) ` Sine of *x* :func:`tan(x) ` Tangent of *x* -**Angular conversion** --------------------------------------------------------------------------------------------------- -:func:`degrees(x) ` Convert angle *x* from radians to degrees -:func:`radians(x) ` Convert angle *x* from degrees to radians - **Hyperbolic functions** -------------------------------------------------------------------------------------------------- :func:`acosh(x) ` Inverse hyperbolic cosine of *x* @@ -112,15 +121,8 @@ noted otherwise, all return values are floats. ==================================================== ============================================ -Number-theoretic and representation functions ---------------------------------------------- - -.. function:: ceil(x) - - Return the ceiling of *x*, the smallest integer greater than or equal to *x*. - If *x* is not a float, delegates to :meth:`x.__ceil__ `, - which should return an :class:`~numbers.Integral` value. - +Number-theoretic functions +-------------------------- .. function:: comb(n, k) @@ -140,25 +142,85 @@ Number-theoretic and representation functions .. versionadded:: 3.8 -.. function:: copysign(x, y) +.. function:: factorial(n) - Return a float with the magnitude (absolute value) of *x* but the sign of - *y*. On platforms that support signed zeros, ``copysign(1.0, -0.0)`` - returns *-1.0*. + Return *n* factorial as an integer. Raises :exc:`ValueError` if *n* is not integral or + is negative. + .. versionchanged:: 3.10 + Floats with integral values (like ``5.0``) are no longer accepted. -.. function:: fabs(x) - Return the absolute value of *x*. +.. function:: gcd(*integers) + + Return the greatest common divisor of the specified integer arguments. + If any of the arguments is nonzero, then the returned value is the largest + positive integer that is a divisor of all arguments. If all arguments + are zero, then the returned value is ``0``. ``gcd()`` without arguments + returns ``0``. + .. versionadded:: 3.5 -.. function:: factorial(n) + .. versionchanged:: 3.9 + Added support for an arbitrary number of arguments. Formerly, only two + arguments were supported. - Return *n* factorial as an integer. Raises :exc:`ValueError` if *n* is not integral or - is negative. - .. versionchanged:: 3.10 - Floats with integral values (like ``5.0``) are no longer accepted. +.. function:: isqrt(n) + + Return the integer square root of the nonnegative integer *n*. This is the + floor of the exact square root of *n*, or equivalently the greatest integer + *a* such that *a*\ ² |nbsp| ≤ |nbsp| *n*. + + For some applications, it may be more convenient to have the least integer + *a* such that *n* |nbsp| ≤ |nbsp| *a*\ ², or in other words the ceiling of + the exact square root of *n*. For positive *n*, this can be computed using + ``a = 1 + isqrt(n - 1)``. + + .. versionadded:: 3.8 + + +.. function:: lcm(*integers) + + Return the least common multiple of the specified integer arguments. + If all arguments are nonzero, then the returned value is the smallest + positive integer that is a multiple of all arguments. If any of the arguments + is zero, then the returned value is ``0``. ``lcm()`` without arguments + returns ``1``. + + .. versionadded:: 3.9 + + +.. function:: perm(n, k=None) + + Return the number of ways to choose *k* items from *n* items + without repetition and with order. + + Evaluates to ``n! / (n - k)!`` when ``k <= n`` and evaluates + to zero when ``k > n``. + + If *k* is not specified or is ``None``, then *k* defaults to *n* + and the function returns ``n!``. + + Raises :exc:`TypeError` if either of the arguments are not integers. + Raises :exc:`ValueError` if either of the arguments are negative. + + .. versionadded:: 3.8 + + +Floating point arithmetic +------------------------- + +.. function:: ceil(x) + + Return the ceiling of *x*, the smallest integer greater than or equal to *x*. + If *x* is not a float, delegates to :meth:`x.__ceil__ `, + which should return an :class:`~numbers.Integral` value. + + +.. function:: fabs(x) + + Return the absolute value of *x*. .. function:: floor(x) @@ -199,45 +261,76 @@ Number-theoretic and representation functions floats, while Python's ``x % y`` is preferred when working with integers. -.. function:: frexp(x) +.. function:: modf(x) - Return the mantissa and exponent of *x* as the pair ``(m, e)``. *m* is a float - and *e* is an integer such that ``x == m * 2**e`` exactly. If *x* is zero, - returns ``(0.0, 0)``, otherwise ``0.5 <= abs(m) < 1``. This is used to "pick - apart" the internal representation of a float in a portable way. + Return the fractional and integer parts of *x*. Both results carry the sign + of *x* and are floats. + Note that :func:`modf` has a different call/return pattern + than its C equivalents: it takes a single argument and return a pair of + values, rather than returning its second return value through an 'output + parameter' (there is no such thing in Python). -.. function:: fsum(iterable) - Return an accurate floating-point sum of values in the iterable. Avoids - loss of precision by tracking multiple intermediate partial sums. +.. function:: remainder(x, y) - The algorithm's accuracy depends on IEEE-754 arithmetic guarantees and the - typical case where the rounding mode is half-even. On some non-Windows - builds, the underlying C library uses extended precision addition and may - occasionally double-round an intermediate sum causing it to be off in its - least significant bit. + Return the IEEE 754-style remainder of *x* with respect to *y*. For + finite *x* and finite nonzero *y*, this is the difference ``x - n*y``, + where ``n`` is the closest integer to the exact value of the quotient ``x / + y``. If ``x / y`` is exactly halfway between two consecutive integers, the + nearest *even* integer is used for ``n``. The remainder ``r = remainder(x, + y)`` thus always satisfies ``abs(r) <= 0.5 * abs(y)``. - For further discussion and two alternative approaches, see the `ASPN cookbook - recipes for accurate floating-point summation - `_\. + Special cases follow IEEE 754: in particular, ``remainder(x, math.inf)`` is + *x* for any finite *x*, and ``remainder(x, 0)`` and + ``remainder(math.inf, x)`` raise :exc:`ValueError` for any non-NaN *x*. + If the result of the remainder operation is zero, that zero will have + the same sign as *x*. + On platforms using IEEE 754 binary floating point, the result of this + operation is always exactly representable: no rounding error is introduced. -.. function:: gcd(*integers) + .. versionadded:: 3.7 - Return the greatest common divisor of the specified integer arguments. - If any of the arguments is nonzero, then the returned value is the largest - positive integer that is a divisor of all arguments. If all arguments - are zero, then the returned value is ``0``. ``gcd()`` without arguments - returns ``0``. - .. versionadded:: 3.5 +.. function:: trunc(x) - .. versionchanged:: 3.9 - Added support for an arbitrary number of arguments. Formerly, only two - arguments were supported. + Return *x* with the fractional part + removed, leaving the integer part. This rounds toward 0: ``trunc()`` is + equivalent to :func:`floor` for positive *x*, and equivalent to :func:`ceil` + for negative *x*. If *x* is not a float, delegates to :meth:`x.__trunc__ + `, which should return an :class:`~numbers.Integral` value. + + +For the :func:`ceil`, :func:`floor`, and :func:`modf` functions, note that *all* +floating-point numbers of sufficiently large magnitude are exact integers. +Python floats typically carry no more than 53 bits of precision (the same as the +platform C double type), in which case any float *x* with ``abs(x) >= 2**52`` +necessarily has no fractional bits. +Floating point manipulation functions +------------------------------------- + +.. function:: copysign(x, y) + + Return a float with the magnitude (absolute value) of *x* but the sign of + *y*. On platforms that support signed zeros, ``copysign(1.0, -0.0)`` + returns *-1.0*. + + +.. function:: frexp(x) + + Return the mantissa and exponent of *x* as the pair ``(m, e)``. *m* is a float + and *e* is an integer such that ``x == m * 2**e`` exactly. If *x* is zero, + returns ``(0.0, 0)``, otherwise ``0.5 <= abs(m) < 1``. This is used to "pick + apart" the internal representation of a float in a portable way. + + Note that :func:`frexp` has a different call/return pattern + than its C equivalents: it takes a single argument and return a pair of + values, rather than returning its second return value through an 'output + parameter' (there is no such thing in Python). + .. function:: isclose(a, b, *, rel_tol=1e-09, abs_tol=0.0) Return ``True`` if the values *a* and *b* are close to each other and @@ -291,43 +384,12 @@ Number-theoretic and representation functions Return ``True`` if *x* is a NaN (not a number), and ``False`` otherwise. -.. function:: isqrt(n) - - Return the integer square root of the nonnegative integer *n*. This is the - floor of the exact square root of *n*, or equivalently the greatest integer - *a* such that *a*\ ² |nbsp| ≤ |nbsp| *n*. - - For some applications, it may be more convenient to have the least integer - *a* such that *n* |nbsp| ≤ |nbsp| *a*\ ², or in other words the ceiling of - the exact square root of *n*. For positive *n*, this can be computed using - ``a = 1 + isqrt(n - 1)``. - - .. versionadded:: 3.8 - - -.. function:: lcm(*integers) - - Return the least common multiple of the specified integer arguments. - If all arguments are nonzero, then the returned value is the smallest - positive integer that is a multiple of all arguments. If any of the arguments - is zero, then the returned value is ``0``. ``lcm()`` without arguments - returns ``1``. - - .. versionadded:: 3.9 - - .. function:: ldexp(x, i) Return ``x * (2**i)``. This is essentially the inverse of function :func:`frexp`. -.. function:: modf(x) - - Return the fractional and integer parts of *x*. Both results carry the sign - of *x* and are floats. - - .. function:: nextafter(x, y, steps=1) Return the floating-point value *steps* steps after *x* towards *y*. @@ -348,79 +410,6 @@ Number-theoretic and representation functions .. versionchanged:: 3.12 Added the *steps* argument. -.. function:: perm(n, k=None) - - Return the number of ways to choose *k* items from *n* items - without repetition and with order. - - Evaluates to ``n! / (n - k)!`` when ``k <= n`` and evaluates - to zero when ``k > n``. - - If *k* is not specified or is ``None``, then *k* defaults to *n* - and the function returns ``n!``. - - Raises :exc:`TypeError` if either of the arguments are not integers. - Raises :exc:`ValueError` if either of the arguments are negative. - - .. versionadded:: 3.8 - - -.. function:: prod(iterable, *, start=1) - - Calculate the product of all the elements in the input *iterable*. - The default *start* value for the product is ``1``. - - When the iterable is empty, return the start value. This function is - intended specifically for use with numeric values and may reject - non-numeric types. - - .. versionadded:: 3.8 - - -.. function:: remainder(x, y) - - Return the IEEE 754-style remainder of *x* with respect to *y*. For - finite *x* and finite nonzero *y*, this is the difference ``x - n*y``, - where ``n`` is the closest integer to the exact value of the quotient ``x / - y``. If ``x / y`` is exactly halfway between two consecutive integers, the - nearest *even* integer is used for ``n``. The remainder ``r = remainder(x, - y)`` thus always satisfies ``abs(r) <= 0.5 * abs(y)``. - - Special cases follow IEEE 754: in particular, ``remainder(x, math.inf)`` is - *x* for any finite *x*, and ``remainder(x, 0)`` and - ``remainder(math.inf, x)`` raise :exc:`ValueError` for any non-NaN *x*. - If the result of the remainder operation is zero, that zero will have - the same sign as *x*. - - On platforms using IEEE 754 binary floating point, the result of this - operation is always exactly representable: no rounding error is introduced. - - .. versionadded:: 3.7 - - -.. function:: sumprod(p, q) - - Return the sum of products of values from two iterables *p* and *q*. - - Raises :exc:`ValueError` if the inputs do not have the same length. - - Roughly equivalent to:: - - sum(map(operator.mul, p, q, strict=True)) - - For float and mixed int/float inputs, the intermediate products - and sums are computed with extended precision. - - .. versionadded:: 3.12 - - -.. function:: trunc(x) - - Return *x* with the fractional part - removed, leaving the integer part. This rounds toward 0: ``trunc()`` is - equivalent to :func:`floor` for positive *x*, and equivalent to :func:`ceil` - for negative *x*. If *x* is not a float, delegates to :meth:`x.__trunc__ - `, which should return an :class:`~numbers.Integral` value. .. function:: ulp(x) @@ -447,20 +436,8 @@ Number-theoretic and representation functions .. versionadded:: 3.9 -Note that :func:`frexp` and :func:`modf` have a different call/return pattern -than their C equivalents: they take a single argument and return a pair of -values, rather than returning their second return value through an 'output -parameter' (there is no such thing in Python). - -For the :func:`ceil`, :func:`floor`, and :func:`modf` functions, note that *all* -floating-point numbers of sufficiently large magnitude are exact integers. -Python floats typically carry no more than 53 bits of precision (the same as the -platform C double type), in which case any float *x* with ``abs(x) >= 2**52`` -necessarily has no fractional bits. - - -Power and logarithmic functions -------------------------------- +Power, exponential and logarithmic functions +-------------------------------------------- .. function:: cbrt(x) @@ -557,41 +534,8 @@ Power and logarithmic functions Return the square root of *x*. -Trigonometric functions ------------------------ - -.. function:: acos(x) - - Return the arc cosine of *x*, in radians. The result is between ``0`` and - ``pi``. - - -.. function:: asin(x) - - Return the arc sine of *x*, in radians. The result is between ``-pi/2`` and - ``pi/2``. - - -.. function:: atan(x) - - Return the arc tangent of *x*, in radians. The result is between ``-pi/2`` and - ``pi/2``. - - -.. function:: atan2(y, x) - - Return ``atan(y / x)``, in radians. The result is between ``-pi`` and ``pi``. - The vector in the plane from the origin to point ``(x, y)`` makes this angle - with the positive X axis. The point of :func:`atan2` is that the signs of both - inputs are known to it, so it can compute the correct quadrant for the angle. - For example, ``atan(1)`` and ``atan2(1, 1)`` are both ``pi/4``, but ``atan2(-1, - -1)`` is ``-3*pi/4``. - - -.. function:: cos(x) - - Return the cosine of *x* radians. - +Summation and product functions +------------------------------- .. function:: dist(p, q) @@ -606,6 +550,22 @@ Trigonometric functions .. versionadded:: 3.8 +.. function:: fsum(iterable) + + Return an accurate floating-point sum of values in the iterable. Avoids + loss of precision by tracking multiple intermediate partial sums. + + The algorithm's accuracy depends on IEEE-754 arithmetic guarantees and the + typical case where the rounding mode is half-even. On some non-Windows + builds, the underlying C library uses extended precision addition and may + occasionally double-round an intermediate sum causing it to be off in its + least significant bit. + + For further discussion and two alternative approaches, see the `ASPN cookbook + recipes for accurate floating-point summation + `_\. + + .. function:: hypot(*coordinates) Return the Euclidean norm, ``sqrt(sum(x**2 for x in coordinates))``. @@ -626,14 +586,32 @@ Trigonometric functions is almost always correctly rounded to within 1/2 ulp. -.. function:: sin(x) +.. function:: prod(iterable, *, start=1) - Return the sine of *x* radians. + Calculate the product of all the elements in the input *iterable*. + The default *start* value for the product is ``1``. + When the iterable is empty, return the start value. This function is + intended specifically for use with numeric values and may reject + non-numeric types. -.. function:: tan(x) + .. versionadded:: 3.8 - Return the tangent of *x* radians. + +.. function:: sumprod(p, q) + + Return the sum of products of values from two iterables *p* and *q*. + + Raises :exc:`ValueError` if the inputs do not have the same length. + + Roughly equivalent to:: + + sum(map(operator.mul, p, q, strict=True)) + + For float and mixed int/float inputs, the intermediate products + and sums are computed with extended precision. + + .. versionadded:: 3.12 Angular conversion @@ -649,6 +627,52 @@ Angular conversion Convert angle *x* from degrees to radians. +Trigonometric functions +----------------------- + +.. function:: acos(x) + + Return the arc cosine of *x*, in radians. The result is between ``0`` and + ``pi``. + + +.. function:: asin(x) + + Return the arc sine of *x*, in radians. The result is between ``-pi/2`` and + ``pi/2``. + + +.. function:: atan(x) + + Return the arc tangent of *x*, in radians. The result is between ``-pi/2`` and + ``pi/2``. + + +.. function:: atan2(y, x) + + Return ``atan(y / x)``, in radians. The result is between ``-pi`` and ``pi``. + The vector in the plane from the origin to point ``(x, y)`` makes this angle + with the positive X axis. The point of :func:`atan2` is that the signs of both + inputs are known to it, so it can compute the correct quadrant for the angle. + For example, ``atan(1)`` and ``atan2(1, 1)`` are both ``pi/4``, but ``atan2(-1, + -1)`` is ``-3*pi/4``. + + +.. function:: cos(x) + + Return the cosine of *x* radians. + + +.. function:: sin(x) + + Return the sine of *x* radians. + + +.. function:: tan(x) + + Return the tangent of *x* radians. + + Hyperbolic functions -------------------- From 3938fd60c0c88891b213097380aeea91a45bcd77 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Mon, 18 Nov 2024 16:50:40 +0800 Subject: [PATCH 208/219] gh-126789: Correct sysconfig test exclusions for iOS and Android. (GH-126941) --- Lib/test/test_sysconfig.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_sysconfig.py b/Lib/test/test_sysconfig.py index c7acfe728bb664..9bbf8d0c6cf2da 100644 --- a/Lib/test/test_sysconfig.py +++ b/Lib/test/test_sysconfig.py @@ -591,7 +591,7 @@ def test_osx_ext_suffix(self): suffix = sysconfig.get_config_var('EXT_SUFFIX') self.assertTrue(suffix.endswith('-darwin.so'), suffix) - @unittest.skipIf(sys.platform == 'wasi', 'venv is unsupported on WASI') + @requires_subprocess() def test_config_vars_depend_on_site_initialization(self): script = textwrap.dedent(""" import sysconfig @@ -615,7 +615,7 @@ def test_config_vars_depend_on_site_initialization(self): self.assertEqual(no_site_config_vars['base'], site_config_vars['installed_base']) self.assertEqual(no_site_config_vars['platbase'], site_config_vars['installed_platbase']) - @unittest.skipIf(sys.platform == 'wasi', 'venv is unsupported on WASI') + @requires_subprocess() def test_config_vars_recalculation_after_site_initialization(self): script = textwrap.dedent(""" import sysconfig @@ -639,7 +639,7 @@ def test_config_vars_recalculation_after_site_initialization(self): #self.assertEqual(config_vars['after']['prefix'], venv.prefix) # FIXME: prefix gets overwriten by _init_posix #self.assertEqual(config_vars['after']['exec_prefix'], venv.prefix) # FIXME: exec_prefix gets overwriten by _init_posix - @unittest.skipIf(sys.platform == 'wasi', 'venv is unsupported on WASI') + @requires_subprocess() def test_paths_depend_on_site_initialization(self): script = textwrap.dedent(""" import sysconfig @@ -656,7 +656,7 @@ def test_paths_depend_on_site_initialization(self): self.assertNotEqual(site_paths, no_site_paths) - @unittest.skipIf(sys.platform == 'wasi', 'venv is unsupported on WASI') + @requires_subprocess() def test_makefile_overwrites_config_vars(self): script = textwrap.dedent(""" import sys, sysconfig @@ -689,6 +689,7 @@ def test_makefile_overwrites_config_vars(self): self.assertNotEqual(data['prefix'], data['base_prefix']) self.assertNotEqual(data['exec_prefix'], data['base_exec_prefix']) + class MakefileTests(unittest.TestCase): @unittest.skipIf(sys.platform.startswith('win'), From 7538e7f5696408fa0aa02fce8a413a7dfac76a04 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 18 Nov 2024 11:53:45 +0200 Subject: [PATCH 209/219] gh-67877: Fix memory leaks in terminated RE matching (GH-126840) If SRE(match) function terminates abruptly, either because of a signal or because memory allocation fails, allocated SRE_REPEAT blocks might be never released. Co-authored-by: --- Lib/test/test_re.py | 23 +++ ...4-11-14-22-25-49.gh-issue-67877.G9hw0w.rst | 2 + Modules/_sre/clinic/sre.c.h | 44 +++++- Modules/_sre/sre.c | 132 +++++++++++++++++- Modules/_sre/sre.h | 17 ++- Modules/_sre/sre_lib.h | 26 +++- 6 files changed, 230 insertions(+), 14 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-11-14-22-25-49.gh-issue-67877.G9hw0w.rst diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py index 7bc702ec89a4a7..1612fc7663e87e 100644 --- a/Lib/test/test_re.py +++ b/Lib/test/test_re.py @@ -2681,6 +2681,29 @@ def test_character_set_none(self): self.assertIsNone(re.search(p, s)) self.assertIsNone(re.search('(?s:.)' + p, s)) + def check_interrupt(self, pattern, string, maxcount): + class Interrupt(Exception): + pass + p = re.compile(pattern) + for n in range(maxcount): + try: + p._fail_after(n, Interrupt) + p.match(string) + return n + except Interrupt: + pass + finally: + p._fail_after(-1, None) + + @unittest.skipUnless(hasattr(re.Pattern, '_fail_after'), 'requires debug build') + def test_memory_leaks(self): + self.check_interrupt(r'(.)*:', 'abc:', 100) + self.check_interrupt(r'([^:])*?:', 'abc:', 100) + self.check_interrupt(r'([^:])*+:', 'abc:', 100) + self.check_interrupt(r'(.){2,4}:', 'abc:', 100) + self.check_interrupt(r'([^:]){2,4}?:', 'abc:', 100) + self.check_interrupt(r'([^:]){2,4}+:', 'abc:', 100) + def get_debug_out(pat): with captured_stdout() as out: diff --git a/Misc/NEWS.d/next/Library/2024-11-14-22-25-49.gh-issue-67877.G9hw0w.rst b/Misc/NEWS.d/next/Library/2024-11-14-22-25-49.gh-issue-67877.G9hw0w.rst new file mode 100644 index 00000000000000..021b4ae2e100bc --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-11-14-22-25-49.gh-issue-67877.G9hw0w.rst @@ -0,0 +1,2 @@ +Fix memory leaks when :mod:`regular expression ` matching terminates +abruptly, either because of a signal or because memory allocation fails. diff --git a/Modules/_sre/clinic/sre.c.h b/Modules/_sre/clinic/sre.c.h index e287f3d5ad3991..87e4785a428468 100644 --- a/Modules/_sre/clinic/sre.c.h +++ b/Modules/_sre/clinic/sre.c.h @@ -985,6 +985,44 @@ PyDoc_STRVAR(_sre_SRE_Pattern___deepcopy____doc__, #define _SRE_SRE_PATTERN___DEEPCOPY___METHODDEF \ {"__deepcopy__", (PyCFunction)_sre_SRE_Pattern___deepcopy__, METH_O, _sre_SRE_Pattern___deepcopy____doc__}, +#if defined(Py_DEBUG) + +PyDoc_STRVAR(_sre_SRE_Pattern__fail_after__doc__, +"_fail_after($self, count, exception, /)\n" +"--\n" +"\n" +"For debugging."); + +#define _SRE_SRE_PATTERN__FAIL_AFTER_METHODDEF \ + {"_fail_after", _PyCFunction_CAST(_sre_SRE_Pattern__fail_after), METH_FASTCALL, _sre_SRE_Pattern__fail_after__doc__}, + +static PyObject * +_sre_SRE_Pattern__fail_after_impl(PatternObject *self, int count, + PyObject *exception); + +static PyObject * +_sre_SRE_Pattern__fail_after(PatternObject *self, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + int count; + PyObject *exception; + + if (!_PyArg_CheckPositional("_fail_after", nargs, 2, 2)) { + goto exit; + } + count = PyLong_AsInt(args[0]); + if (count == -1 && PyErr_Occurred()) { + goto exit; + } + exception = args[1]; + return_value = _sre_SRE_Pattern__fail_after_impl(self, count, exception); + +exit: + return return_value; +} + +#endif /* defined(Py_DEBUG) */ + PyDoc_STRVAR(_sre_compile__doc__, "compile($module, /, pattern, flags, code, groups, groupindex,\n" " indexgroup)\n" @@ -1474,4 +1512,8 @@ _sre_SRE_Scanner_search(ScannerObject *self, PyTypeObject *cls, PyObject *const } return _sre_SRE_Scanner_search_impl(self, cls); } -/*[clinic end generated code: output=afaa301d55957cb0 input=a9049054013a1b77]*/ + +#ifndef _SRE_SRE_PATTERN__FAIL_AFTER_METHODDEF + #define _SRE_SRE_PATTERN__FAIL_AFTER_METHODDEF +#endif /* !defined(_SRE_SRE_PATTERN__FAIL_AFTER_METHODDEF) */ +/*[clinic end generated code: output=f8cb77f2261f0b2e input=a9049054013a1b77]*/ diff --git a/Modules/_sre/sre.c b/Modules/_sre/sre.c index 2c86f8869d8e58..36f542ddb4df2b 100644 --- a/Modules/_sre/sre.c +++ b/Modules/_sre/sre.c @@ -267,6 +267,85 @@ data_stack_grow(SRE_STATE* state, Py_ssize_t size) return 0; } +/* memory pool functions for SRE_REPEAT, this can avoid memory + leak when SRE(match) function terminates abruptly. + state->repeat_pool_used is a doubly-linked list, so that we + can remove a SRE_REPEAT node from it. + state->repeat_pool_unused is a singly-linked list, we put/get + node at the head. */ +static SRE_REPEAT * +repeat_pool_malloc(SRE_STATE *state) +{ + SRE_REPEAT *repeat; + + if (state->repeat_pool_unused) { + /* remove from unused pool (singly-linked list) */ + repeat = state->repeat_pool_unused; + state->repeat_pool_unused = repeat->pool_next; + } + else { + repeat = PyMem_Malloc(sizeof(SRE_REPEAT)); + if (!repeat) { + return NULL; + } + } + + /* add to used pool (doubly-linked list) */ + SRE_REPEAT *temp = state->repeat_pool_used; + if (temp) { + temp->pool_prev = repeat; + } + repeat->pool_prev = NULL; + repeat->pool_next = temp; + state->repeat_pool_used = repeat; + + return repeat; +} + +static void +repeat_pool_free(SRE_STATE *state, SRE_REPEAT *repeat) +{ + SRE_REPEAT *prev = repeat->pool_prev; + SRE_REPEAT *next = repeat->pool_next; + + /* remove from used pool (doubly-linked list) */ + if (prev) { + prev->pool_next = next; + } + else { + state->repeat_pool_used = next; + } + if (next) { + next->pool_prev = prev; + } + + /* add to unused pool (singly-linked list) */ + repeat->pool_next = state->repeat_pool_unused; + state->repeat_pool_unused = repeat; +} + +static void +repeat_pool_clear(SRE_STATE *state) +{ + /* clear used pool */ + SRE_REPEAT *next = state->repeat_pool_used; + state->repeat_pool_used = NULL; + while (next) { + SRE_REPEAT *temp = next; + next = temp->pool_next; + PyMem_Free(temp); + } + + /* clear unused pool */ + next = state->repeat_pool_unused; + state->repeat_pool_unused = NULL; + while (next) { + SRE_REPEAT *temp = next; + next = temp->pool_next; + PyMem_Free(temp); + } +} + /* generate 8-bit version */ #define SRE_CHAR Py_UCS1 @@ -511,6 +590,11 @@ state_init(SRE_STATE* state, PatternObject* pattern, PyObject* string, state->pos = start; state->endpos = end; +#ifdef Py_DEBUG + state->fail_after_count = pattern->fail_after_count; + state->fail_after_exc = pattern->fail_after_exc; // borrowed ref +#endif + return string; err: /* We add an explicit cast here because MSVC has a bug when @@ -533,6 +617,8 @@ state_fini(SRE_STATE* state) /* See above PyMem_Free() for why we explicitly cast here. */ PyMem_Free((void*) state->mark); state->mark = NULL; + /* SRE_REPEAT pool */ + repeat_pool_clear(state); } /* calculate offset from start of string */ @@ -619,6 +705,9 @@ pattern_traverse(PatternObject *self, visitproc visit, void *arg) Py_VISIT(self->groupindex); Py_VISIT(self->indexgroup); Py_VISIT(self->pattern); +#ifdef Py_DEBUG + Py_VISIT(self->fail_after_exc); +#endif return 0; } @@ -628,6 +717,9 @@ pattern_clear(PatternObject *self) Py_CLEAR(self->groupindex); Py_CLEAR(self->indexgroup); Py_CLEAR(self->pattern); +#ifdef Py_DEBUG + Py_CLEAR(self->fail_after_exc); +#endif return 0; } @@ -690,7 +782,7 @@ _sre_SRE_Pattern_match_impl(PatternObject *self, PyTypeObject *cls, Py_ssize_t status; PyObject *match; - if (!state_init(&state, (PatternObject *)self, string, pos, endpos)) + if (!state_init(&state, self, string, pos, endpos)) return NULL; INIT_TRACE(&state); @@ -1381,6 +1473,29 @@ _sre_SRE_Pattern___deepcopy__(PatternObject *self, PyObject *memo) return Py_NewRef(self); } +#ifdef Py_DEBUG +/*[clinic input] +_sre.SRE_Pattern._fail_after + + count: int + exception: object + / + +For debugging. +[clinic start generated code]*/ + +static PyObject * +_sre_SRE_Pattern__fail_after_impl(PatternObject *self, int count, + PyObject *exception) +/*[clinic end generated code: output=9a6bf12135ac50c2 input=ef80a45c66c5499d]*/ +{ + self->fail_after_count = count; + Py_INCREF(exception); + Py_XSETREF(self->fail_after_exc, exception); + Py_RETURN_NONE; +} +#endif /* Py_DEBUG */ + static PyObject * pattern_repr(PatternObject *obj) { @@ -1506,6 +1621,10 @@ _sre_compile_impl(PyObject *module, PyObject *pattern, int flags, self->pattern = NULL; self->groupindex = NULL; self->indexgroup = NULL; +#ifdef Py_DEBUG + self->fail_after_count = -1; + self->fail_after_exc = NULL; +#endif self->codesize = n; @@ -2604,7 +2723,8 @@ pattern_new_match(_sremodulestate* module_state, if (!match) return NULL; - match->pattern = (PatternObject*)Py_NewRef(pattern); + Py_INCREF(pattern); + match->pattern = pattern; match->string = Py_NewRef(state->string); @@ -2740,7 +2860,7 @@ _sre_SRE_Scanner_match_impl(ScannerObject *self, PyTypeObject *cls) return NULL; } - match = pattern_new_match(module_state, (PatternObject*) self->pattern, + match = pattern_new_match(module_state, self->pattern, state, status); if (status == 0) @@ -2790,7 +2910,7 @@ _sre_SRE_Scanner_search_impl(ScannerObject *self, PyTypeObject *cls) return NULL; } - match = pattern_new_match(module_state, (PatternObject*) self->pattern, + match = pattern_new_match(module_state, self->pattern, state, status); if (status == 0) @@ -2826,7 +2946,8 @@ pattern_scanner(_sremodulestate *module_state, return NULL; } - scanner->pattern = Py_NewRef(self); + Py_INCREF(self); + scanner->pattern = self; PyObject_GC_Track(scanner); return (PyObject*) scanner; @@ -3020,6 +3141,7 @@ static PyMethodDef pattern_methods[] = { _SRE_SRE_PATTERN_SCANNER_METHODDEF _SRE_SRE_PATTERN___COPY___METHODDEF _SRE_SRE_PATTERN___DEEPCOPY___METHODDEF + _SRE_SRE_PATTERN__FAIL_AFTER_METHODDEF {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, PyDoc_STR("See PEP 585")}, {NULL, NULL} diff --git a/Modules/_sre/sre.h b/Modules/_sre/sre.h index 83d89d57b11199..42681c2addf3c2 100644 --- a/Modules/_sre/sre.h +++ b/Modules/_sre/sre.h @@ -34,6 +34,11 @@ typedef struct { int flags; /* flags used when compiling pattern source */ PyObject *weakreflist; /* List of weak references */ int isbytes; /* pattern type (1 - bytes, 0 - string, -1 - None) */ +#ifdef Py_DEBUG + /* for simulation of user interruption */ + int fail_after_count; + PyObject *fail_after_exc; +#endif /* pattern code */ Py_ssize_t codesize; SRE_CODE code[1]; @@ -68,6 +73,9 @@ typedef struct SRE_REPEAT_T { const SRE_CODE* pattern; /* points to REPEAT operator arguments */ const void* last_ptr; /* helper to check for infinite loops */ struct SRE_REPEAT_T *prev; /* points to previous repeat context */ + /* for SRE_REPEAT pool */ + struct SRE_REPEAT_T *pool_prev; + struct SRE_REPEAT_T *pool_next; } SRE_REPEAT; typedef struct { @@ -95,12 +103,19 @@ typedef struct { size_t data_stack_base; /* current repeat context */ SRE_REPEAT *repeat; + /* SRE_REPEAT pool */ + SRE_REPEAT *repeat_pool_used; + SRE_REPEAT *repeat_pool_unused; unsigned int sigcount; +#ifdef Py_DEBUG + int fail_after_count; + PyObject *fail_after_exc; +#endif } SRE_STATE; typedef struct { PyObject_HEAD - PyObject* pattern; + PatternObject* pattern; SRE_STATE state; int executing; } ScannerObject; diff --git a/Modules/_sre/sre_lib.h b/Modules/_sre/sre_lib.h index 97fbb0a75e54b6..0c93f5121103e8 100644 --- a/Modules/_sre/sre_lib.h +++ b/Modules/_sre/sre_lib.h @@ -560,13 +560,28 @@ typedef struct { Py_ssize_t last_ctx_pos; } SRE(match_context); -#define MAYBE_CHECK_SIGNALS \ +#define _MAYBE_CHECK_SIGNALS \ do { \ if ((0 == (++sigcount & 0xfff)) && PyErr_CheckSignals()) { \ RETURN_ERROR(SRE_ERROR_INTERRUPTED); \ } \ } while (0) +#ifdef Py_DEBUG +# define MAYBE_CHECK_SIGNALS \ + do { \ + _MAYBE_CHECK_SIGNALS; \ + if (state->fail_after_count >= 0) { \ + if (state->fail_after_count-- == 0) { \ + PyErr_SetNone(state->fail_after_exc); \ + RETURN_ERROR(SRE_ERROR_INTERRUPTED); \ + } \ + } \ + } while (0) +#else +# define MAYBE_CHECK_SIGNALS _MAYBE_CHECK_SIGNALS +#endif /* Py_DEBUG */ + #ifdef HAVE_COMPUTED_GOTOS #ifndef USE_COMPUTED_GOTOS #define USE_COMPUTED_GOTOS 1 @@ -1120,12 +1135,9 @@ SRE(match)(SRE_STATE* state, const SRE_CODE* pattern, int toplevel) pattern[1], pattern[2])); /* install new repeat context */ - /* TODO(https://github.com/python/cpython/issues/67877): Fix this - * potential memory leak. */ - ctx->u.rep = (SRE_REPEAT*) PyMem_Malloc(sizeof(*ctx->u.rep)); + ctx->u.rep = repeat_pool_malloc(state); if (!ctx->u.rep) { - PyErr_NoMemory(); - RETURN_FAILURE; + RETURN_ERROR(SRE_ERROR_MEMORY); } ctx->u.rep->count = -1; ctx->u.rep->pattern = pattern; @@ -1136,7 +1148,7 @@ SRE(match)(SRE_STATE* state, const SRE_CODE* pattern, int toplevel) state->ptr = ptr; DO_JUMP(JUMP_REPEAT, jump_repeat, pattern+pattern[0]); state->repeat = ctx->u.rep->prev; - PyMem_Free(ctx->u.rep); + repeat_pool_free(state, ctx->u.rep); if (ret) { RETURN_ON_ERROR(ret); From f9c5573dedcb2f2e9ae152672ce157987cdea612 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 18 Nov 2024 13:43:44 +0200 Subject: [PATCH 210/219] gh-101955: Fix SystemError in possesive quantifier with alternative and group (GH-111362) Co-authored-by: --- Lib/test/test_re.py | 6 ++++++ ...3-10-26-16-36-22.gh-issue-101955.Ixu3IF.rst | 2 ++ Modules/_sre/sre_lib.h | 18 ++++++++++++++++++ 3 files changed, 26 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2023-10-26-16-36-22.gh-issue-101955.Ixu3IF.rst diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py index 1612fc7663e87e..0d3599be87f228 100644 --- a/Lib/test/test_re.py +++ b/Lib/test/test_re.py @@ -2640,6 +2640,12 @@ def test_bug_gh100061(self): self.assertEqual(re.match("(?>(?:ab?c){1,3})", "aca").span(), (0, 2)) self.assertEqual(re.match("(?:ab?c){1,3}+", "aca").span(), (0, 2)) + def test_bug_gh101955(self): + # Possessive quantifier with nested alternative with capture groups + self.assertEqual(re.match('((x)|y|z)*+', 'xyz').groups(), ('z', 'x')) + self.assertEqual(re.match('((x)|y|z){3}+', 'xyz').groups(), ('z', 'x')) + self.assertEqual(re.match('((x)|y|z){3,}+', 'xyz').groups(), ('z', 'x')) + @unittest.skipIf(multiprocessing is None, 'test requires multiprocessing') def test_regression_gh94675(self): pattern = re.compile(r'(?<=[({}])(((//[^\n]*)?[\n])([\000-\040])*)*' diff --git a/Misc/NEWS.d/next/Library/2023-10-26-16-36-22.gh-issue-101955.Ixu3IF.rst b/Misc/NEWS.d/next/Library/2023-10-26-16-36-22.gh-issue-101955.Ixu3IF.rst new file mode 100644 index 00000000000000..89431010f784f8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-10-26-16-36-22.gh-issue-101955.Ixu3IF.rst @@ -0,0 +1,2 @@ +Fix SystemError when match regular expression pattern containing some +combination of possessive quantifier, alternative and capture group. diff --git a/Modules/_sre/sre_lib.h b/Modules/_sre/sre_lib.h index 0c93f5121103e8..af4bfc56083bcb 100644 --- a/Modules/_sre/sre_lib.h +++ b/Modules/_sre/sre_lib.h @@ -1306,6 +1306,17 @@ SRE(match)(SRE_STATE* state, const SRE_CODE* pattern, int toplevel) pointer */ state->ptr = ptr; + /* Set state->repeat to non-NULL */ + ctx->u.rep = repeat_pool_malloc(state); + if (!ctx->u.rep) { + RETURN_ERROR(SRE_ERROR_MEMORY); + } + ctx->u.rep->count = -1; + ctx->u.rep->pattern = NULL; + ctx->u.rep->prev = state->repeat; + ctx->u.rep->last_ptr = NULL; + state->repeat = ctx->u.rep; + /* Initialize Count to 0 */ ctx->count = 0; @@ -1320,6 +1331,9 @@ SRE(match)(SRE_STATE* state, const SRE_CODE* pattern, int toplevel) } else { state->ptr = ptr; + /* Restore state->repeat */ + state->repeat = ctx->u.rep->prev; + repeat_pool_free(state, ctx->u.rep); RETURN_FAILURE; } } @@ -1392,6 +1406,10 @@ SRE(match)(SRE_STATE* state, const SRE_CODE* pattern, int toplevel) } } + /* Restore state->repeat */ + state->repeat = ctx->u.rep->prev; + repeat_pool_free(state, ctx->u.rep); + /* Evaluate Tail */ /* Jump to end of pattern indicated by skip, and then skip the SUCCESS op code that follows it. */ From 2c0a21c1aad65ab8362491acf856eb574b1257ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Mon, 18 Nov 2024 13:05:55 +0000 Subject: [PATCH 211/219] gh-126909: Fix running xattr tests on systems with lower limits (#126930) Modify the extended attribute tests to write fewer and smaller extended attributes, in order to fit within filesystems with total xattr limit of 1 KiB (e.g. ext4 with 1 KiB blocks). Previously, the test would write over 2 KiB, making it fail with ENOSPC on such systems. --- Lib/test/test_os.py | 6 +++--- .../Tests/2024-11-17-16-56-48.gh-issue-126909.60VTxW.rst | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Tests/2024-11-17-16-56-48.gh-issue-126909.60VTxW.rst diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 9a4be78556c648..919ed92ddb425f 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -3967,10 +3967,10 @@ def _check_xattrs_str(self, s, getxattr, setxattr, removexattr, listxattr, **kwa xattr.remove("user.test") self.assertEqual(set(listxattr(fn)), xattr) self.assertEqual(getxattr(fn, s("user.test2"), **kwargs), b"foo") - setxattr(fn, s("user.test"), b"a"*1024, **kwargs) - self.assertEqual(getxattr(fn, s("user.test"), **kwargs), b"a"*1024) + setxattr(fn, s("user.test"), b"a"*256, **kwargs) + self.assertEqual(getxattr(fn, s("user.test"), **kwargs), b"a"*256) removexattr(fn, s("user.test"), **kwargs) - many = sorted("user.test{}".format(i) for i in range(100)) + many = sorted("user.test{}".format(i) for i in range(32)) for thing in many: setxattr(fn, thing, b"x", **kwargs) self.assertEqual(set(listxattr(fn)), set(init_xattr) | set(many)) diff --git a/Misc/NEWS.d/next/Tests/2024-11-17-16-56-48.gh-issue-126909.60VTxW.rst b/Misc/NEWS.d/next/Tests/2024-11-17-16-56-48.gh-issue-126909.60VTxW.rst new file mode 100644 index 00000000000000..68bd9ac70cd1f4 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2024-11-17-16-56-48.gh-issue-126909.60VTxW.rst @@ -0,0 +1,2 @@ +Fix test_os extended attribute tests to work on filesystems with 1 KiB xattr size +limit. From a1d9c8aa800dd7c9eb634f89646be10e9cfc9c8d Mon Sep 17 00:00:00 2001 From: sobolevn Date: Mon, 18 Nov 2024 17:23:43 +0300 Subject: [PATCH 212/219] gh-109413: Enable `strict_optional = true` for `libregrtest/run_workers` (#126855) Co-authored-by: Alex Waygood --- Lib/test/libregrtest/mypy.ini | 4 ---- Lib/test/libregrtest/run_workers.py | 20 +++++++++++++------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/Lib/test/libregrtest/mypy.ini b/Lib/test/libregrtest/mypy.ini index da75a27158a600..905341cc04b8f1 100644 --- a/Lib/test/libregrtest/mypy.ini +++ b/Lib/test/libregrtest/mypy.ini @@ -22,10 +22,6 @@ disallow_untyped_defs = False check_untyped_defs = False warn_return_any = False -# Enable --strict-optional for these ASAP: -[mypy-Lib.test.libregrtest.run_workers.*] -strict_optional = False - # Various internal modules that typeshed deliberately doesn't have stubs for: [mypy-_abc.*,_opcode.*,_overlapped.*,_testcapi.*,_testinternalcapi.*,test.*] ignore_missing_imports = True diff --git a/Lib/test/libregrtest/run_workers.py b/Lib/test/libregrtest/run_workers.py index dcc817ae9aceb6..0ca86a986ea436 100644 --- a/Lib/test/libregrtest/run_workers.py +++ b/Lib/test/libregrtest/run_workers.py @@ -102,6 +102,9 @@ def __init__(self, super().__init__() +_NOT_RUNNING = "" + + class WorkerThread(threading.Thread): def __init__(self, worker_id: int, runner: "RunWorkers") -> None: super().__init__() @@ -111,8 +114,8 @@ def __init__(self, worker_id: int, runner: "RunWorkers") -> None: self.output = runner.output self.timeout = runner.worker_timeout self.log = runner.log - self.test_name: TestName | None = None - self.start_time: float | None = None + self.test_name = _NOT_RUNNING + self.start_time = time.monotonic() self._popen: subprocess.Popen[str] | None = None self._killed = False self._stopped = False @@ -129,7 +132,7 @@ def __repr__(self) -> str: popen = self._popen if popen is not None: dt = time.monotonic() - self.start_time - info.extend((f'pid={self._popen.pid}', + info.extend((f'pid={popen.pid}', f'time={format_duration(dt)}')) return '<%s>' % ' '.join(info) @@ -401,7 +404,7 @@ def run(self) -> None: except WorkerError as exc: mp_result = exc.mp_result finally: - self.test_name = None + self.test_name = _NOT_RUNNING mp_result.result.duration = time.monotonic() - self.start_time self.output.put((False, mp_result)) @@ -416,6 +419,9 @@ def run(self) -> None: def _wait_completed(self) -> None: popen = self._popen + # only needed for mypy: + if popen is None: + raise ValueError("Should never access `._popen` before calling `.run()`") try: popen.wait(WAIT_COMPLETED_TIMEOUT) @@ -483,7 +489,7 @@ def __init__(self, num_workers: int, runtests: RunTests, self.worker_timeout: float | None = min(self.timeout * 1.5, self.timeout + 5 * 60) else: self.worker_timeout = None - self.workers: list[WorkerThread] | None = None + self.workers: list[WorkerThread] = [] jobs = self.runtests.get_jobs() if jobs is not None: @@ -503,7 +509,7 @@ def start_workers(self) -> None: processes = plural(nworkers, "process", "processes") msg = (f"Run {tests} in parallel using " f"{nworkers} worker {processes}") - if self.timeout: + if self.timeout and self.worker_timeout is not None: msg += (" (timeout: %s, worker timeout: %s)" % (format_duration(self.timeout), format_duration(self.worker_timeout))) @@ -555,7 +561,7 @@ def display_result(self, mp_result: MultiprocessResult) -> None: if mp_result.err_msg: # WORKER_BUG text += ' (%s)' % mp_result.err_msg - elif (result.duration >= PROGRESS_MIN_TIME and not pgo): + elif (result.duration and result.duration >= PROGRESS_MIN_TIME and not pgo): text += ' (%s)' % format_duration(result.duration) if not pgo: running = get_running(self.workers) From b0fcc2c47a34a69c35c1a8031cd0589d3747c1af Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 18 Nov 2024 14:31:26 +0000 Subject: [PATCH 213/219] GH-126491: GC: Mark objects reachable from roots before doing cycle collection (GH-126502) * Mark almost all reachable objects before doing collection phase * Add stats for objects marked * Visit new frames before each increment * Remove lazy dict tracking * Update docs * Clearer calculation of work to do. --- Include/cpython/pystats.h | 2 + Include/internal/pycore_dict.h | 2 - Include/internal/pycore_frame.h | 3 + Include/internal/pycore_gc.h | 11 +- Include/internal/pycore_object.h | 4 +- Include/internal/pycore_runtime_init.h | 1 + InternalDocs/garbage_collector.md | 63 +++- Lib/test/test_dict.py | 109 ------- Lib/test/test_gc.py | 33 +-- ...-11-06-15-22-34.gh-issue-126491.n9VyZc.rst | 4 + Modules/_testinternalcapi.c | 7 + Objects/dictobject.c | 115 ++------ Objects/moduleobject.c | 2 - Python/bytecodes.c | 4 - Python/ceval.c | 1 + Python/executor_cases.c.h | 4 - Python/gc.c | 277 ++++++++++++++---- Python/gc_free_threading.c | 9 +- Python/generated_cases.c.h | 4 - Python/specialize.c | 2 + Tools/scripts/summarize_stats.py | 5 +- 21 files changed, 332 insertions(+), 330 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-11-06-15-22-34.gh-issue-126491.n9VyZc.rst diff --git a/Include/cpython/pystats.h b/Include/cpython/pystats.h index f1ca54839fbc38..2ae48002d720e9 100644 --- a/Include/cpython/pystats.h +++ b/Include/cpython/pystats.h @@ -99,6 +99,8 @@ typedef struct _gc_stats { uint64_t collections; uint64_t object_visits; uint64_t objects_collected; + uint64_t objects_transitively_reachable; + uint64_t objects_not_transitively_reachable; } GCStats; typedef struct _uop_stats { diff --git a/Include/internal/pycore_dict.h b/Include/internal/pycore_dict.h index c5399ad8e0497f..f7d747cc6c62a1 100644 --- a/Include/internal/pycore_dict.h +++ b/Include/internal/pycore_dict.h @@ -43,8 +43,6 @@ extern int _PyDict_Next( extern int _PyDict_HasOnlyStringKeys(PyObject *mp); -extern void _PyDict_MaybeUntrack(PyObject *mp); - // Export for '_ctypes' shared extension PyAPI_FUNC(Py_ssize_t) _PyDict_SizeOf(PyDictObject *); diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index 8c0100390d036e..b786c5f49e9831 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -75,6 +75,7 @@ typedef struct _PyInterpreterFrame { _PyStackRef *stackpointer; uint16_t return_offset; /* Only relevant during a function call */ char owner; + char visited; /* Locals and stack */ _PyStackRef localsplus[1]; } _PyInterpreterFrame; @@ -207,6 +208,7 @@ _PyFrame_Initialize( #endif frame->return_offset = 0; frame->owner = FRAME_OWNED_BY_THREAD; + frame->visited = 0; for (int i = null_locals_from; i < code->co_nlocalsplus; i++) { frame->localsplus[i] = PyStackRef_NULL; @@ -389,6 +391,7 @@ _PyFrame_PushTrampolineUnchecked(PyThreadState *tstate, PyCodeObject *code, int frame->instr_ptr = _PyCode_CODE(code); #endif frame->owner = FRAME_OWNED_BY_THREAD; + frame->visited = 0; frame->return_offset = 0; #ifdef Py_GIL_DISABLED diff --git a/Include/internal/pycore_gc.h b/Include/internal/pycore_gc.h index 38a1c56c09d9db..c81e7a1de4b727 100644 --- a/Include/internal/pycore_gc.h +++ b/Include/internal/pycore_gc.h @@ -10,11 +10,11 @@ extern "C" { /* GC information is stored BEFORE the object structure. */ typedef struct { - // Pointer to next object in the list. + // Tagged pointer to next object in the list. // 0 means the object is not tracked uintptr_t _gc_next; - // Pointer to previous object in the list. + // Tagged pointer to previous object in the list. // Lowest two bits are used for flags documented later. uintptr_t _gc_prev; } PyGC_Head; @@ -302,6 +302,11 @@ struct gc_generation_stats { Py_ssize_t uncollectable; }; +enum _GCPhase { + GC_PHASE_MARK = 0, + GC_PHASE_COLLECT = 1 +}; + struct _gc_runtime_state { /* List of objects that still need to be cleaned up, singly linked * via their gc headers' gc_prev pointers. */ @@ -325,10 +330,12 @@ struct _gc_runtime_state { /* a list of callbacks to be invoked when collection is performed */ PyObject *callbacks; + Py_ssize_t prior_heap_size; Py_ssize_t heap_size; Py_ssize_t work_to_do; /* Which of the old spaces is the visited space */ int visited_space; + int phase; #ifdef Py_GIL_DISABLED /* This is the number of objects that survived the last full diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h index c7af720b1ce43d..0cba7bc083087e 100644 --- a/Include/internal/pycore_object.h +++ b/Include/internal/pycore_object.h @@ -466,8 +466,8 @@ static inline void _PyObject_GC_TRACK( PyGC_Head *last = (PyGC_Head*)(generation0->_gc_prev); _PyGCHead_SET_NEXT(last, gc); _PyGCHead_SET_PREV(gc, last); - /* Young objects will be moved into the visited space during GC, so set the bit here */ - gc->_gc_next = ((uintptr_t)generation0) | (uintptr_t)interp->gc.visited_space; + uintptr_t not_visited = 1 ^ interp->gc.visited_space; + gc->_gc_next = ((uintptr_t)generation0) | not_visited; generation0->_gc_prev = (uintptr_t)gc; #endif } diff --git a/Include/internal/pycore_runtime_init.h b/Include/internal/pycore_runtime_init.h index 8a8f47695fb8b0..ce116ad609b4bb 100644 --- a/Include/internal/pycore_runtime_init.h +++ b/Include/internal/pycore_runtime_init.h @@ -134,6 +134,7 @@ extern PyTypeObject _PyExc_MemoryError; { .threshold = 0, }, \ }, \ .work_to_do = -5000, \ + .phase = GC_PHASE_MARK, \ }, \ .qsbr = { \ .wr_seq = QSBR_INITIAL, \ diff --git a/InternalDocs/garbage_collector.md b/InternalDocs/garbage_collector.md index 377a846428ae0c..ea21928b50e34a 100644 --- a/InternalDocs/garbage_collector.md +++ b/InternalDocs/garbage_collector.md @@ -351,6 +351,7 @@ follows these steps in order: the reference counts fall to 0, triggering the destruction of all unreachable objects. + Optimization: incremental collection ==================================== @@ -484,6 +485,46 @@ specifically in a generation by calling `gc.collect(generation=NUM)`. ``` +Optimization: visiting reachable objects +======================================== + +An object cannot be garbage if it can be reached. + +To avoid having to identify reference cycles across the whole heap, we can +reduce the amount of work done considerably by first moving most reachable objects +to the `visited` space. Empirically, most reachable objects can be reached from a +small set of global objects and local variables. +This step does much less work per object, so reduces the time spent +performing garbage collection by at least half. + +> [!NOTE] +> Objects that are not determined to be reachable by this pass are not necessarily +> unreachable. We still need to perform the main algorithm to determine which objects +> are actually unreachable. + +We use the same technique of forming a transitive closure as the incremental +collector does to find reachable objects, seeding the list with some global +objects and the currently executing frames. + +This phase moves objects to the `visited` space, as follows: + +1. All objects directly referred to by any builtin class, the `sys` module, the `builtins` +module and all objects directly referred to from stack frames are added to a working +set of reachable objects. +2. Until this working set is empty: + 1. Pop an object from the set and move it to the `visited` space + 2. For each object directly reachable from that object: + * If it is not already in `visited` space and it is a GC object, + add it to the working set + + +Before each increment of collection is performed, the stacks are scanned +to check for any new stack frames that have been created since the last +increment. All objects directly referred to from those stack frames are +added to the working set. +Then the above algorithm is repeated, starting from step 2. + + Optimization: reusing fields to save memory =========================================== @@ -532,8 +573,8 @@ of `PyGC_Head` discussed in the `Memory layout and object structure`_ section: currently in. Instead, when that's needed, ad hoc tricks (like the `NEXT_MASK_UNREACHABLE` flag) are employed. -Optimization: delay tracking containers -======================================= +Optimization: delayed untracking of containers +============================================== Certain types of containers cannot participate in a reference cycle, and so do not need to be tracked by the garbage collector. Untracking these objects @@ -548,8 +589,8 @@ a container: As a general rule, instances of atomic types aren't tracked and instances of non-atomic types (containers, user-defined objects...) are. However, some type-specific optimizations can be present in order to suppress the garbage -collector footprint of simple instances. Some examples of native types that -benefit from delayed tracking: +collector footprint of simple instances. Historically, both dictionaries and +tuples were untracked during garbage collection. Now it is only tuples: - Tuples containing only immutable objects (integers, strings etc, and recursively, tuples of immutable objects) do not need to be tracked. The @@ -558,14 +599,8 @@ benefit from delayed tracking: tuples at creation time. Instead, all tuples except the empty tuple are tracked when created. During garbage collection it is determined whether any surviving tuples can be untracked. A tuple can be untracked if all of its contents are - already not tracked. Tuples are examined for untracking in all garbage collection - cycles. It may take more than one cycle to untrack a tuple. - -- Dictionaries containing only immutable objects also do not need to be tracked. - Dictionaries are untracked when created. If a tracked item is inserted into a - dictionary (either as a key or value), the dictionary becomes tracked. During a - full garbage collection (all generations), the collector will untrack any dictionaries - whose contents are not tracked. + already not tracked. Tuples are examined for untracking when moved from the + young to the old generation. The garbage collector module provides the Python function `is_tracked(obj)`, which returns the current tracking status of the object. Subsequent garbage collections may change the @@ -578,11 +613,9 @@ tracking status of the object. False >>> gc.is_tracked([]) True - >>> gc.is_tracked({}) + >>> gc.is_tracked(("a", 1)) False >>> gc.is_tracked({"a": 1}) - False - >>> gc.is_tracked({"a": []}) True ``` diff --git a/Lib/test/test_dict.py b/Lib/test/test_dict.py index 4030716efb51f9..c94dc2df4f0a7f 100644 --- a/Lib/test/test_dict.py +++ b/Lib/test/test_dict.py @@ -880,115 +880,6 @@ class C(object): gc.collect() self.assertIs(ref(), None, "Cycle was not collected") - def _not_tracked(self, t): - # Nested containers can take several collections to untrack - gc.collect() - gc.collect() - self.assertFalse(gc.is_tracked(t), t) - - def _tracked(self, t): - self.assertTrue(gc.is_tracked(t), t) - gc.collect() - gc.collect() - self.assertTrue(gc.is_tracked(t), t) - - def test_string_keys_can_track_values(self): - # Test that this doesn't leak. - for i in range(10): - d = {} - for j in range(10): - d[str(j)] = j - d["foo"] = d - - @support.cpython_only - def test_track_literals(self): - # Test GC-optimization of dict literals - x, y, z, w = 1.5, "a", (1, None), [] - - self._not_tracked({}) - self._not_tracked({x:(), y:x, z:1}) - self._not_tracked({1: "a", "b": 2}) - self._not_tracked({1: 2, (None, True, False, ()): int}) - self._not_tracked({1: object()}) - - # Dicts with mutable elements are always tracked, even if those - # elements are not tracked right now. - self._tracked({1: []}) - self._tracked({1: ([],)}) - self._tracked({1: {}}) - self._tracked({1: set()}) - - @support.cpython_only - def test_track_dynamic(self): - # Test GC-optimization of dynamically-created dicts - class MyObject(object): - pass - x, y, z, w, o = 1.5, "a", (1, object()), [], MyObject() - - d = dict() - self._not_tracked(d) - d[1] = "a" - self._not_tracked(d) - d[y] = 2 - self._not_tracked(d) - d[z] = 3 - self._not_tracked(d) - self._not_tracked(d.copy()) - d[4] = w - self._tracked(d) - self._tracked(d.copy()) - d[4] = None - self._not_tracked(d) - self._not_tracked(d.copy()) - - # dd isn't tracked right now, but it may mutate and therefore d - # which contains it must be tracked. - d = dict() - dd = dict() - d[1] = dd - self._not_tracked(dd) - self._tracked(d) - dd[1] = d - self._tracked(dd) - - d = dict.fromkeys([x, y, z]) - self._not_tracked(d) - dd = dict() - dd.update(d) - self._not_tracked(dd) - d = dict.fromkeys([x, y, z, o]) - self._tracked(d) - dd = dict() - dd.update(d) - self._tracked(dd) - - d = dict(x=x, y=y, z=z) - self._not_tracked(d) - d = dict(x=x, y=y, z=z, w=w) - self._tracked(d) - d = dict() - d.update(x=x, y=y, z=z) - self._not_tracked(d) - d.update(w=w) - self._tracked(d) - - d = dict([(x, y), (z, 1)]) - self._not_tracked(d) - d = dict([(x, y), (z, w)]) - self._tracked(d) - d = dict() - d.update([(x, y), (z, 1)]) - self._not_tracked(d) - d.update([(x, y), (z, w)]) - self._tracked(d) - - @support.cpython_only - def test_track_subtypes(self): - # Dict subtypes are always tracked - class MyDict(dict): - pass - self._tracked(MyDict()) - def make_shared_key_dict(self, n): class C: pass diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py index 0372815b9bfd27..e02ec7c65212e0 100644 --- a/Lib/test/test_gc.py +++ b/Lib/test/test_gc.py @@ -31,6 +31,11 @@ def __new__(cls, *args, **kwargs): return C ContainerNoGC = None +try: + import _testinternalcapi +except ImportError: + _testinternalcapi = None + ### Support code ############################################################################### @@ -1130,6 +1135,7 @@ def setUp(self): def tearDown(self): gc.disable() + @unittest.skipIf(_testinternalcapi is None, "requires _testinternalcapi") @requires_gil_enabled("Free threading does not support incremental GC") # Use small increments to emulate longer running process in a shorter time @gc_threshold(200, 10) @@ -1155,32 +1161,18 @@ def make_ll(depth): return head head = make_ll(1000) - count = 1000 - - # There will be some objects we aren't counting, - # e.g. the gc stats dicts. This test checks - # that the counts don't grow, so we try to - # correct for the uncounted objects - # This is just an estimate. - CORRECTION = 20 enabled = gc.isenabled() gc.enable() olds = [] + initial_heap_size = _testinternalcapi.get_heap_size() for i in range(20_000): newhead = make_ll(20) - count += 20 newhead.surprise = head olds.append(newhead) if len(olds) == 20: - stats = gc.get_stats() - young = stats[0] - incremental = stats[1] - old = stats[2] - collected = young['collected'] + incremental['collected'] + old['collected'] - count += CORRECTION - live = count - collected - self.assertLess(live, 25000) + new_objects = _testinternalcapi.get_heap_size() - initial_heap_size + self.assertLess(new_objects, 25_000) del olds[:] if not enabled: gc.disable() @@ -1322,7 +1314,8 @@ def test_refcount_errors(self): from test.support import gc_collect, SuppressCrashReport a = [1, 2, 3] - b = [a] + b = [a, a] + a.append(b) # Avoid coredump when Py_FatalError() calls abort() SuppressCrashReport().__enter__() @@ -1332,6 +1325,8 @@ def test_refcount_errors(self): # (to avoid deallocating it): import ctypes ctypes.pythonapi.Py_DecRef(ctypes.py_object(a)) + del a + del b # The garbage collector should now have a fatal error # when it reaches the broken object @@ -1360,7 +1355,7 @@ def test_refcount_errors(self): self.assertRegex(stderr, br'object type name: list') self.assertRegex(stderr, - br'object repr : \[1, 2, 3\]') + br'object repr : \[1, 2, 3, \[\[...\], \[...\]\]\]') class GCTogglingTests(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-06-15-22-34.gh-issue-126491.n9VyZc.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-06-15-22-34.gh-issue-126491.n9VyZc.rst new file mode 100644 index 00000000000000..f69977852c994a --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-06-15-22-34.gh-issue-126491.n9VyZc.rst @@ -0,0 +1,4 @@ +Add a marking phase to the GC. All objects that can be transitively +reached from builtin modules or the stacks are marked as reachable +before cycle detection. This reduces the amount of work done by the +GC by approximately half. diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index b02f794d27d5bd..35cb215418e0cd 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -2077,6 +2077,12 @@ has_deferred_refcount(PyObject *self, PyObject *op) } +static PyObject * +get_heap_size(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return PyLong_FromInt64(PyInterpreterState_Get()->gc.heap_size); +} + static PyMethodDef module_functions[] = { {"get_configs", get_configs, METH_NOARGS}, {"get_recursion_depth", get_recursion_depth, METH_NOARGS}, @@ -2174,6 +2180,7 @@ static PyMethodDef module_functions[] = { {"get_static_builtin_types", get_static_builtin_types, METH_NOARGS}, {"identify_type_slot_wrappers", identify_type_slot_wrappers, METH_NOARGS}, {"has_deferred_refcount", has_deferred_refcount, METH_O}, + {"get_heap_size", get_heap_size, METH_NOARGS}, {NULL, NULL} /* sentinel */ }; diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 2090008055b7c0..19a3ba9e13e5cc 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -883,6 +883,7 @@ new_dict(PyInterpreterState *interp, mp->ma_used = used; mp->_ma_watcher_tag = 0; ASSERT_CONSISTENT(mp); + _PyObject_GC_TRACK(mp); return (PyObject *)mp; } @@ -1578,64 +1579,6 @@ _PyDict_HasOnlyStringKeys(PyObject *dict) return 1; } -#define MAINTAIN_TRACKING(mp, key, value) \ - do { \ - if (!_PyObject_GC_IS_TRACKED(mp)) { \ - if (_PyObject_GC_MAY_BE_TRACKED(key) || \ - _PyObject_GC_MAY_BE_TRACKED(value)) { \ - _PyObject_GC_TRACK(mp); \ - } \ - } \ - } while(0) - -void -_PyDict_MaybeUntrack(PyObject *op) -{ - PyDictObject *mp; - PyObject *value; - Py_ssize_t i, numentries; - - ASSERT_WORLD_STOPPED_OR_DICT_LOCKED(op); - - if (!PyDict_CheckExact(op) || !_PyObject_GC_IS_TRACKED(op)) - return; - - mp = (PyDictObject *) op; - ASSERT_CONSISTENT(mp); - numentries = mp->ma_keys->dk_nentries; - if (_PyDict_HasSplitTable(mp)) { - for (i = 0; i < numentries; i++) { - if ((value = mp->ma_values->values[i]) == NULL) - continue; - if (_PyObject_GC_MAY_BE_TRACKED(value)) { - return; - } - } - } - else { - if (DK_IS_UNICODE(mp->ma_keys)) { - PyDictUnicodeEntry *ep0 = DK_UNICODE_ENTRIES(mp->ma_keys); - for (i = 0; i < numentries; i++) { - if ((value = ep0[i].me_value) == NULL) - continue; - if (_PyObject_GC_MAY_BE_TRACKED(value)) - return; - } - } - else { - PyDictKeyEntry *ep0 = DK_ENTRIES(mp->ma_keys); - for (i = 0; i < numentries; i++) { - if ((value = ep0[i].me_value) == NULL) - continue; - if (_PyObject_GC_MAY_BE_TRACKED(value) || - _PyObject_GC_MAY_BE_TRACKED(ep0[i].me_key)) - return; - } - } - } - _PyObject_GC_UNTRACK(op); -} - void _PyDict_EnablePerThreadRefcounting(PyObject *op) { @@ -1761,7 +1704,6 @@ insert_split_value(PyInterpreterState *interp, PyDictObject *mp, PyObject *key, { assert(PyUnicode_CheckExact(key)); ASSERT_DICT_LOCKED(mp); - MAINTAIN_TRACKING(mp, key, value); PyObject *old_value = mp->ma_values->values[ix]; if (old_value == NULL) { _PyDict_NotifyEvent(interp, PyDict_EVENT_ADDED, mp, key, value); @@ -1818,8 +1760,6 @@ insertdict(PyInterpreterState *interp, PyDictObject *mp, if (ix == DKIX_ERROR) goto Fail; - MAINTAIN_TRACKING(mp, key, value); - if (ix == DKIX_EMPTY) { assert(!_PyDict_HasSplitTable(mp)); /* Insert into new slot. */ @@ -1878,8 +1818,6 @@ insert_to_emptydict(PyInterpreterState *interp, PyDictObject *mp, /* We don't decref Py_EMPTY_KEYS here because it is immortal. */ assert(mp->ma_values == NULL); - MAINTAIN_TRACKING(mp, key, value); - size_t hashpos = (size_t)hash & (PyDict_MINSIZE-1); dictkeys_set_index(newkeys, hashpos, 0); if (unicode) { @@ -3770,11 +3708,6 @@ dict_dict_merge(PyInterpreterState *interp, PyDictObject *mp, PyDictObject *othe STORE_USED(mp, other->ma_used); ASSERT_CONSISTENT(mp); - if (_PyObject_GC_IS_TRACKED(other) && !_PyObject_GC_IS_TRACKED(mp)) { - /* Maintain tracking. */ - _PyObject_GC_TRACK(mp); - } - return 0; } } @@ -4024,8 +3957,7 @@ copy_lock_held(PyObject *o) split_copy->ma_used = mp->ma_used; split_copy->_ma_watcher_tag = 0; dictkeys_incref(mp->ma_keys); - if (_PyObject_GC_IS_TRACKED(mp)) - _PyObject_GC_TRACK(split_copy); + _PyObject_GC_TRACK(split_copy); return (PyObject *)split_copy; } @@ -4060,10 +3992,6 @@ copy_lock_held(PyObject *o) new->ma_used = mp->ma_used; ASSERT_CONSISTENT(new); - if (_PyObject_GC_IS_TRACKED(mp)) { - /* Maintain tracking. */ - _PyObject_GC_TRACK(new); - } return (PyObject *)new; } @@ -4350,8 +4278,6 @@ dict_setdefault_ref_lock_held(PyObject *d, PyObject *key, PyObject *default_valu *result = NULL; } } - - MAINTAIN_TRACKING(mp, key, value); STORE_USED(mp, mp->ma_used + 1); assert(mp->ma_keys->dk_usable >= 0); ASSERT_CONSISTENT(mp); @@ -4801,15 +4727,8 @@ dict_new(PyTypeObject *type, PyObject *args, PyObject *kwds) d->ma_values = NULL; ASSERT_CONSISTENT(d); - if (type != &PyDict_Type) { - // Don't track if a subclass tp_alloc is PyType_GenericAlloc() - if (!_PyObject_GC_IS_TRACKED(d)) { - _PyObject_GC_TRACK(d); - } - } - else { - // _PyType_AllocNoTrack() does not track the created object - assert(!_PyObject_GC_IS_TRACKED(d)); + if (!_PyObject_GC_IS_TRACKED(d)) { + _PyObject_GC_TRACK(d); } return self; } @@ -6746,19 +6665,14 @@ make_dict_from_instance_attributes(PyInterpreterState *interp, { dictkeys_incref(keys); Py_ssize_t used = 0; - Py_ssize_t track = 0; size_t size = shared_keys_usable_size(keys); for (size_t i = 0; i < size; i++) { PyObject *val = values->values[i]; if (val != NULL) { used += 1; - track += _PyObject_GC_MAY_BE_TRACKED(val); } } PyDictObject *res = (PyDictObject *)new_dict(interp, keys, values, used, 0); - if (track && res) { - _PyObject_GC_TRACK(res); - } return res; } @@ -7204,6 +7118,7 @@ _PyObject_SetManagedDict(PyObject *obj, PyObject *new_dict) // since we locked it. dict = _PyObject_ManagedDictPointer(obj)->dict; err = _PyDict_DetachFromObject(dict, obj); + assert(err == 0 || new_dict == NULL); if (err == 0) { FT_ATOMIC_STORE_PTR(_PyObject_ManagedDictPointer(obj)->dict, (PyDictObject *)Py_XNewRef(new_dict)); @@ -7236,7 +7151,21 @@ void PyObject_ClearManagedDict(PyObject *obj) { if (_PyObject_SetManagedDict(obj, NULL) < 0) { + /* Must be out of memory */ + assert(PyErr_Occurred() == PyExc_MemoryError); PyErr_WriteUnraisable(NULL); + /* Clear the dict */ + PyDictObject *dict = _PyObject_GetManagedDict(obj); + Py_BEGIN_CRITICAL_SECTION2(dict, obj); + dict = _PyObject_ManagedDictPointer(obj)->dict; + PyInterpreterState *interp = _PyInterpreterState_GET(); + PyDictKeysObject *oldkeys = dict->ma_keys; + set_keys(dict, Py_EMPTY_KEYS); + dict->ma_values = NULL; + dictkeys_decref(interp, oldkeys, IS_DICT_SHARED(dict)); + STORE_USED(dict, 0); + set_dict_inline_values(obj, NULL); + Py_END_CRITICAL_SECTION2(); } } @@ -7261,12 +7190,6 @@ _PyDict_DetachFromObject(PyDictObject *mp, PyObject *obj) PyDictValues *values = copy_values(mp->ma_values); if (values == NULL) { - /* Out of memory. Clear the dict */ - PyInterpreterState *interp = _PyInterpreterState_GET(); - PyDictKeysObject *oldkeys = mp->ma_keys; - set_keys(mp, Py_EMPTY_KEYS); - dictkeys_decref(interp, oldkeys, IS_DICT_SHARED(mp)); - STORE_USED(mp, 0); PyErr_NoMemory(); return -1; } diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c index 535b0d068f064f..a8d64c9aefae6b 100644 --- a/Objects/moduleobject.c +++ b/Objects/moduleobject.c @@ -107,8 +107,6 @@ static void track_module(PyModuleObject *m) { _PyDict_EnablePerThreadRefcounting(m->md_dict); - PyObject_GC_Track(m->md_dict); - _PyObject_SetDeferredRefcount((PyObject *)m); PyObject_GC_Track(m); } diff --git a/Python/bytecodes.c b/Python/bytecodes.c index c85b49842daf44..4479bd5dc56cb1 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2340,10 +2340,6 @@ dummy_func( DEOPT_IF(ep->me_key != name); PyObject *old_value = ep->me_value; DEOPT_IF(old_value == NULL); - /* Ensure dict is GC tracked if it needs to be */ - if (!_PyObject_GC_IS_TRACKED(dict) && _PyObject_GC_MAY_BE_TRACKED(PyStackRef_AsPyObjectBorrow(value))) { - _PyObject_GC_TRACK(dict); - } _PyDict_NotifyEvent(tstate->interp, PyDict_EVENT_MODIFIED, dict, name, PyStackRef_AsPyObjectBorrow(value)); ep->me_value = PyStackRef_AsPyObjectSteal(value); // old_value should be DECREFed after GC track checking is done, if not, it could raise a segmentation fault, diff --git a/Python/ceval.c b/Python/ceval.c index 9a608f06966688..3360e50d57fbcb 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -821,6 +821,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int entry_frame.instr_ptr = (_Py_CODEUNIT *)_Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS + 1; entry_frame.stackpointer = entry_frame.localsplus; entry_frame.owner = FRAME_OWNED_BY_CSTACK; + entry_frame.visited = 0; entry_frame.return_offset = 0; /* Push frame */ entry_frame.previous = tstate->current_frame; diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 2c2a09adf281a7..33e4c57be5628a 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -2914,10 +2914,6 @@ UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); } - /* Ensure dict is GC tracked if it needs to be */ - if (!_PyObject_GC_IS_TRACKED(dict) && _PyObject_GC_MAY_BE_TRACKED(PyStackRef_AsPyObjectBorrow(value))) { - _PyObject_GC_TRACK(dict); - } _PyFrame_SetStackPointer(frame, stack_pointer); _PyDict_NotifyEvent(tstate->interp, PyDict_EVENT_MODIFIED, dict, name, PyStackRef_AsPyObjectBorrow(value)); stack_pointer = _PyFrame_GetStackPointer(frame); diff --git a/Python/gc.c b/Python/gc.c index fe81ca5989c621..60b41377654af4 100644 --- a/Python/gc.c +++ b/Python/gc.c @@ -5,7 +5,7 @@ #include "Python.h" #include "pycore_ceval.h" // _Py_set_eval_breaker_bit() #include "pycore_context.h" -#include "pycore_dict.h" // _PyDict_MaybeUntrack() +#include "pycore_dict.h" // _PyInlineValuesSize() #include "pycore_initconfig.h" #include "pycore_interp.h" // PyInterpreterState.gc #include "pycore_object.h" @@ -185,6 +185,7 @@ _PyGC_Init(PyInterpreterState *interp) if (gcstate->callbacks == NULL) { return _PyStatus_NO_MEMORY(); } + gcstate->prior_heap_size = 0; gcstate->heap_size = 0; return _PyStatus_OK(); @@ -747,21 +748,6 @@ untrack_tuples(PyGC_Head *head) } } -/* Try to untrack all currently tracked dictionaries */ -static void -untrack_dicts(PyGC_Head *head) -{ - PyGC_Head *next, *gc = GC_NEXT(head); - while (gc != head) { - PyObject *op = FROM_GC(gc); - next = GC_NEXT(gc); - if (PyDict_CheckExact(op)) { - _PyDict_MaybeUntrack(op); - } - gc = next; - } -} - /* Return true if object has a pre-PEP 442 finalization method. */ static int has_legacy_finalizer(PyObject *op) @@ -1258,15 +1244,10 @@ handle_resurrected_objects(PyGC_Head *unreachable, PyGC_Head* still_unreachable, gc_list_merge(resurrected, old_generation); } - -#define UNTRACK_TUPLES 1 -#define UNTRACK_DICTS 2 - static void gc_collect_region(PyThreadState *tstate, PyGC_Head *from, PyGC_Head *to, - int untrack, struct gc_collection_stats *stats); static inline Py_ssize_t @@ -1315,6 +1296,7 @@ gc_collect_young(PyThreadState *tstate, GCState *gcstate = &tstate->interp->gc; PyGC_Head *young = &gcstate->young.head; PyGC_Head *visited = &gcstate->old[gcstate->visited_space].head; + untrack_tuples(&gcstate->young.head); GC_STAT_ADD(0, collections, 1); #ifdef Py_STATS { @@ -1328,7 +1310,8 @@ gc_collect_young(PyThreadState *tstate, PyGC_Head survivors; gc_list_init(&survivors); - gc_collect_region(tstate, young, &survivors, UNTRACK_TUPLES, stats); + gc_list_set_space(young, gcstate->visited_space); + gc_collect_region(tstate, young, &survivors, stats); Py_ssize_t survivor_count = 0; if (gcstate->visited_space) { /* objects in visited space have bit set, so we set it here */ @@ -1343,16 +1326,11 @@ gc_collect_young(PyThreadState *tstate, survivor_count++; } } - (void)survivor_count; // Silence compiler warning gc_list_merge(&survivors, visited); validate_old(gcstate); gcstate->young.count = 0; gcstate->old[gcstate->visited_space].count++; - Py_ssize_t scale_factor = gcstate->old[0].threshold; - if (scale_factor < 1) { - scale_factor = 1; - } - gcstate->work_to_do += gcstate->heap_size / SCAN_RATE_DIVISOR / scale_factor; + gcstate->work_to_do += survivor_count * 4; add_stats(gcstate, 0, stats); } @@ -1368,15 +1346,15 @@ IS_IN_VISITED(PyGC_Head *gc, int visited_space) struct container_and_flag { PyGC_Head *container; int visited_space; - uintptr_t size; + Py_ssize_t size; }; /* A traversal callback for adding to container) */ static int visit_add_to_container(PyObject *op, void *arg) { - OBJECT_STAT_INC(object_visits); struct container_and_flag *cf = (struct container_and_flag *)arg; + OBJECT_STAT_INC(object_visits); int visited = cf->visited_space; assert(visited == get_gc_state()->visited_space); if (!_Py_IsImmortal(op) && _PyObject_IS_GC(op)) { @@ -1391,10 +1369,9 @@ visit_add_to_container(PyObject *op, void *arg) return 0; } -static uintptr_t +static Py_ssize_t expand_region_transitively_reachable(PyGC_Head *container, PyGC_Head *gc, GCState *gcstate) { - validate_list(container, collecting_clear_unreachable_clear); struct container_and_flag arg = { .container = container, .visited_space = gcstate->visited_space, @@ -1406,6 +1383,7 @@ expand_region_transitively_reachable(PyGC_Head *container, PyGC_Head *gc, GCStat * have been marked as visited */ assert(IS_IN_VISITED(gc, gcstate->visited_space)); PyObject *op = FROM_GC(gc); + assert(_PyObject_GC_IS_TRACKED(op)); if (_Py_IsImmortal(op)) { PyGC_Head *next = GC_NEXT(gc); gc_list_move(gc, &get_gc_state()->permanent_generation.head); @@ -1425,20 +1403,187 @@ expand_region_transitively_reachable(PyGC_Head *container, PyGC_Head *gc, GCStat static void completed_cycle(GCState *gcstate) { -#ifdef Py_DEBUG - PyGC_Head *not_visited = &gcstate->old[gcstate->visited_space^1].head; - assert(gc_list_is_empty(not_visited)); -#endif - gcstate->visited_space = flip_old_space(gcstate->visited_space); + assert(gc_list_is_empty(&gcstate->old[gcstate->visited_space^1].head)); + int not_visited = gcstate->visited_space; + gcstate->visited_space = flip_old_space(not_visited); /* Make sure all young objects have old space bit set correctly */ PyGC_Head *young = &gcstate->young.head; PyGC_Head *gc = GC_NEXT(young); while (gc != young) { PyGC_Head *next = GC_NEXT(gc); - gc_set_old_space(gc, gcstate->visited_space); + gc_set_old_space(gc, not_visited); gc = next; } gcstate->work_to_do = 0; + gcstate->phase = GC_PHASE_MARK; +} + +static Py_ssize_t +move_to_reachable(PyObject *op, PyGC_Head *reachable, int visited_space) +{ + if (op != NULL && !_Py_IsImmortal(op) && _PyObject_IS_GC(op)) { + PyGC_Head *gc = AS_GC(op); + if (_PyObject_GC_IS_TRACKED(op) && + gc_old_space(gc) != visited_space) { + gc_flip_old_space(gc); + gc_list_move(gc, reachable); + return 1; + } + } + return 0; +} + +static Py_ssize_t +mark_all_reachable(PyGC_Head *reachable, PyGC_Head *visited, int visited_space) +{ + // Transitively traverse all objects from reachable, until empty + struct container_and_flag arg = { + .container = reachable, + .visited_space = visited_space, + .size = 0 + }; + while (!gc_list_is_empty(reachable)) { + PyGC_Head *gc = _PyGCHead_NEXT(reachable); + assert(gc_old_space(gc) == visited_space); + gc_list_move(gc, visited); + PyObject *op = FROM_GC(gc); + traverseproc traverse = Py_TYPE(op)->tp_traverse; + (void) traverse(op, + visit_add_to_container, + &arg); + } + gc_list_validate_space(visited, visited_space); + return arg.size; +} + +static Py_ssize_t +mark_global_roots(PyInterpreterState *interp, PyGC_Head *visited, int visited_space) +{ + PyGC_Head reachable; + gc_list_init(&reachable); + Py_ssize_t objects_marked = 0; + objects_marked += move_to_reachable(interp->sysdict, &reachable, visited_space); + objects_marked += move_to_reachable(interp->builtins, &reachable, visited_space); + objects_marked += move_to_reachable(interp->dict, &reachable, visited_space); + struct types_state *types = &interp->types; + for (int i = 0; i < _Py_MAX_MANAGED_STATIC_BUILTIN_TYPES; i++) { + objects_marked += move_to_reachable(types->builtins.initialized[i].tp_dict, &reachable, visited_space); + objects_marked += move_to_reachable(types->builtins.initialized[i].tp_subclasses, &reachable, visited_space); + } + for (int i = 0; i < _Py_MAX_MANAGED_STATIC_EXT_TYPES; i++) { + objects_marked += move_to_reachable(types->for_extensions.initialized[i].tp_dict, &reachable, visited_space); + objects_marked += move_to_reachable(types->for_extensions.initialized[i].tp_subclasses, &reachable, visited_space); + } + objects_marked += mark_all_reachable(&reachable, visited, visited_space); + assert(gc_list_is_empty(&reachable)); + return objects_marked; +} + +static Py_ssize_t +mark_stacks(PyInterpreterState *interp, PyGC_Head *visited, int visited_space, bool start) +{ + PyGC_Head reachable; + gc_list_init(&reachable); + Py_ssize_t objects_marked = 0; + // Move all objects on stacks to reachable + _PyRuntimeState *runtime = &_PyRuntime; + HEAD_LOCK(runtime); + PyThreadState* ts = PyInterpreterState_ThreadHead(interp); + HEAD_UNLOCK(runtime); + while (ts) { + _PyInterpreterFrame *frame = ts->current_frame; + while (frame) { + if (frame->owner == FRAME_OWNED_BY_CSTACK) { + frame = frame->previous; + continue; + } + _PyStackRef *locals = frame->localsplus; + _PyStackRef *sp = frame->stackpointer; + objects_marked += move_to_reachable(frame->f_locals, &reachable, visited_space); + PyObject *func = PyStackRef_AsPyObjectBorrow(frame->f_funcobj); + objects_marked += move_to_reachable(func, &reachable, visited_space); + while (sp > locals) { + sp--; + if (PyStackRef_IsNull(*sp)) { + continue; + } + PyObject *op = PyStackRef_AsPyObjectBorrow(*sp); + if (!_Py_IsImmortal(op) && _PyObject_IS_GC(op)) { + PyGC_Head *gc = AS_GC(op); + if (_PyObject_GC_IS_TRACKED(op) && + gc_old_space(gc) != visited_space) { + gc_flip_old_space(gc); + objects_marked++; + gc_list_move(gc, &reachable); + } + } + } + if (!start && frame->visited) { + // If this frame has already been visited, then the lower frames + // will have already been visited and will not have changed + break; + } + frame->visited = 1; + frame = frame->previous; + } + HEAD_LOCK(runtime); + ts = PyThreadState_Next(ts); + HEAD_UNLOCK(runtime); + } + objects_marked += mark_all_reachable(&reachable, visited, visited_space); + assert(gc_list_is_empty(&reachable)); + return objects_marked; +} + +static Py_ssize_t +mark_at_start(PyThreadState *tstate) +{ + // TO DO -- Make this incremental + GCState *gcstate = &tstate->interp->gc; + validate_old(gcstate); + PyGC_Head *visited = &gcstate->old[gcstate->visited_space].head; + Py_ssize_t objects_marked = mark_global_roots(tstate->interp, visited, gcstate->visited_space); + objects_marked += mark_stacks(tstate->interp, visited, gcstate->visited_space, true); + gcstate->work_to_do -= objects_marked; + gcstate->phase = GC_PHASE_COLLECT; + return objects_marked; +} + +static Py_ssize_t +assess_work_to_do(GCState *gcstate) +{ + /* The amount of work we want to do depends on three things. + * 1. The number of new objects created + * 2. The growth in heap size since the last collection + * 3. The heap size (up to the number of new objects, to avoid quadratic effects) + * + * For a steady state heap, the amount of work to do is three times the number + * of new objects added to the heap. This ensures that we stay ahead in the + * worst case of all new objects being garbage. + * + * This could be improved by tracking survival rates, but it is still a + * large improvement on the non-marking approach. + */ + Py_ssize_t scale_factor = gcstate->old[0].threshold; + if (scale_factor < 2) { + scale_factor = 2; + } + Py_ssize_t new_objects = gcstate->young.count; + Py_ssize_t growth = gcstate->heap_size - gcstate->prior_heap_size; + if (growth < 0) { + growth = 0; + } + if (gcstate->heap_size < new_objects * scale_factor) { + // Small heap: ignore growth + growth = 0; + } + Py_ssize_t heap_fraction = gcstate->heap_size / SCAN_RATE_DIVISOR / scale_factor; + if (heap_fraction > new_objects) { + heap_fraction = new_objects; + } + gcstate->young.count = 0; + gcstate->prior_heap_size = gcstate->heap_size; + return new_objects*3/2 + growth*2 + heap_fraction*3/2; } static void @@ -1446,16 +1591,24 @@ gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats) { GC_STAT_ADD(1, collections, 1); GCState *gcstate = &tstate->interp->gc; + + gcstate->work_to_do += assess_work_to_do(gcstate); + untrack_tuples(&gcstate->young.head); + if (gcstate->phase == GC_PHASE_MARK) { + Py_ssize_t objects_marked = mark_at_start(tstate); + GC_STAT_ADD(1, objects_transitively_reachable, objects_marked); + gcstate->work_to_do -= objects_marked; + return; + } PyGC_Head *not_visited = &gcstate->old[gcstate->visited_space^1].head; PyGC_Head *visited = &gcstate->old[gcstate->visited_space].head; PyGC_Head increment; gc_list_init(&increment); - Py_ssize_t scale_factor = gcstate->old[0].threshold; - if (scale_factor < 1) { - scale_factor = 1; - } + Py_ssize_t objects_marked = mark_stacks(tstate->interp, visited, gcstate->visited_space, false); + GC_STAT_ADD(1, objects_transitively_reachable, objects_marked); + gcstate->work_to_do -= objects_marked; + gc_list_set_space(&gcstate->young.head, gcstate->visited_space); gc_list_merge(&gcstate->young.head, &increment); - gcstate->young.count = 0; gc_list_validate_space(&increment, gcstate->visited_space); Py_ssize_t increment_size = 0; while (increment_size < gcstate->work_to_do) { @@ -1465,17 +1618,18 @@ gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats) PyGC_Head *gc = _PyGCHead_NEXT(not_visited); gc_list_move(gc, &increment); increment_size++; + assert(!_Py_IsImmortal(FROM_GC(gc))); gc_set_old_space(gc, gcstate->visited_space); increment_size += expand_region_transitively_reachable(&increment, gc, gcstate); } + GC_STAT_ADD(1, objects_not_transitively_reachable, increment_size); gc_list_validate_space(&increment, gcstate->visited_space); PyGC_Head survivors; gc_list_init(&survivors); - gc_collect_region(tstate, &increment, &survivors, UNTRACK_TUPLES, stats); + gc_collect_region(tstate, &increment, &survivors, stats); gc_list_validate_space(&survivors, gcstate->visited_space); gc_list_merge(&survivors, visited); assert(gc_list_is_empty(&increment)); - gcstate->work_to_do += gcstate->heap_size / SCAN_RATE_DIVISOR / scale_factor; gcstate->work_to_do -= increment_size; validate_old(gcstate); @@ -1496,20 +1650,25 @@ gc_collect_full(PyThreadState *tstate, PyGC_Head *young = &gcstate->young.head; PyGC_Head *pending = &gcstate->old[gcstate->visited_space^1].head; PyGC_Head *visited = &gcstate->old[gcstate->visited_space].head; - /* merge all generations into visited */ - gc_list_validate_space(young, gcstate->visited_space); - gc_list_set_space(pending, gcstate->visited_space); + untrack_tuples(&gcstate->young.head); + /* merge all generations into pending */ + gc_list_validate_space(young, 1-gcstate->visited_space); gc_list_merge(young, pending); + gc_list_set_space(visited, 1-gcstate->visited_space); + gc_list_merge(visited, pending); + /* Mark reachable */ + Py_ssize_t reachable = mark_global_roots(tstate->interp, visited, gcstate->visited_space); + reachable += mark_stacks(tstate->interp, visited, gcstate->visited_space, true); + (void)reachable; + GC_STAT_ADD(2, objects_transitively_reachable, reachable); + GC_STAT_ADD(2, objects_not_transitively_reachable, gc_list_size(pending)); gcstate->young.count = 0; - gc_list_merge(pending, visited); - - gc_collect_region(tstate, visited, visited, - UNTRACK_TUPLES | UNTRACK_DICTS, - stats); + gc_list_set_space(pending, gcstate->visited_space); + gc_collect_region(tstate, pending, visited, stats); gcstate->young.count = 0; gcstate->old[0].count = 0; gcstate->old[1].count = 0; - + completed_cycle(gcstate); gcstate->work_to_do = - gcstate->young.threshold * 2; _PyGC_ClearAllFreeLists(tstate->interp); validate_old(gcstate); @@ -1522,7 +1681,6 @@ static void gc_collect_region(PyThreadState *tstate, PyGC_Head *from, PyGC_Head *to, - int untrack, struct gc_collection_stats *stats) { PyGC_Head unreachable; /* non-problematic unreachable trash */ @@ -1536,12 +1694,6 @@ gc_collect_region(PyThreadState *tstate, gc_list_init(&unreachable); deduce_unreachable(from, &unreachable); validate_consistent_old_space(from); - if (untrack & UNTRACK_TUPLES) { - untrack_tuples(from); - } - if (untrack & UNTRACK_DICTS) { - untrack_dicts(from); - } validate_consistent_old_space(to); if (from != to) { gc_list_merge(from, to); @@ -1761,9 +1913,10 @@ _PyGC_Freeze(PyInterpreterState *interp) { GCState *gcstate = &interp->gc; /* The permanent_generation has its old space bit set to zero */ - if (gcstate->visited_space) { + if (!gcstate->visited_space) { gc_list_set_space(&gcstate->young.head, 0); } + gc_list_validate_space(&gcstate->young.head, 0); gc_list_merge(&gcstate->young.head, &gcstate->permanent_generation.head); gcstate->young.count = 0; PyGC_Head*old0 = &gcstate->old[0].head; diff --git a/Python/gc_free_threading.c b/Python/gc_free_threading.c index 499ee51fdb2cd4..a6e0022340b7fe 100644 --- a/Python/gc_free_threading.c +++ b/Python/gc_free_threading.c @@ -3,7 +3,7 @@ #include "pycore_brc.h" // struct _brc_thread_state #include "pycore_ceval.h" // _Py_set_eval_breaker_bit() #include "pycore_context.h" -#include "pycore_dict.h" // _PyDict_MaybeUntrack() +#include "pycore_dict.h" // _PyInlineValuesSize() #include "pycore_freelist.h" // _PyObject_ClearFreeLists() #include "pycore_initconfig.h" #include "pycore_interp.h" // PyInterpreterState.gc @@ -493,13 +493,6 @@ update_refs(const mi_heap_t *heap, const mi_heap_area_t *area, return true; } } - else if (PyDict_CheckExact(op)) { - _PyDict_MaybeUntrack(op); - if (!_PyObject_GC_IS_TRACKED(op)) { - gc_restore_refs(op); - return true; - } - } } // We repurpose ob_tid to compute "gc_refs", the number of external diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 15308d6f1f7146..f00e10f40d689a 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -7432,10 +7432,6 @@ DEOPT_IF(ep->me_key != name, STORE_ATTR); PyObject *old_value = ep->me_value; DEOPT_IF(old_value == NULL, STORE_ATTR); - /* Ensure dict is GC tracked if it needs to be */ - if (!_PyObject_GC_IS_TRACKED(dict) && _PyObject_GC_MAY_BE_TRACKED(PyStackRef_AsPyObjectBorrow(value))) { - _PyObject_GC_TRACK(dict); - } _PyFrame_SetStackPointer(frame, stack_pointer); _PyDict_NotifyEvent(tstate->interp, PyDict_EVENT_MODIFIED, dict, name, PyStackRef_AsPyObjectBorrow(value)); stack_pointer = _PyFrame_GetStackPointer(frame); diff --git a/Python/specialize.c b/Python/specialize.c index 4c8cf8534b3dc7..dbdff197ac7829 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -230,6 +230,8 @@ print_gc_stats(FILE *out, GCStats *stats) for (int i = 0; i < NUM_GENERATIONS; i++) { fprintf(out, "GC[%d] collections: %" PRIu64 "\n", i, stats[i].collections); fprintf(out, "GC[%d] object visits: %" PRIu64 "\n", i, stats[i].object_visits); + fprintf(out, "GC[%d] objects reachable from roots: %" PRIu64 "\n", i, stats[i].objects_transitively_reachable); + fprintf(out, "GC[%d] objects not reachable from roots: %" PRIu64 "\n", i, stats[i].objects_not_transitively_reachable); fprintf(out, "GC[%d] objects collected: %" PRIu64 "\n", i, stats[i].objects_collected); } } diff --git a/Tools/scripts/summarize_stats.py b/Tools/scripts/summarize_stats.py index 5793e5c649d6b3..296a31d5916bd8 100644 --- a/Tools/scripts/summarize_stats.py +++ b/Tools/scripts/summarize_stats.py @@ -1118,6 +1118,8 @@ def calc_gc_stats(stats: Stats) -> Rows: Count(gen["collections"]), Count(gen["objects collected"]), Count(gen["object visits"]), + Count(gen["objects reachable from roots"]), + Count(gen["objects not reachable from roots"]), ) for (i, gen) in enumerate(gc_stats) ] @@ -1127,7 +1129,8 @@ def calc_gc_stats(stats: Stats) -> Rows: "GC collections and effectiveness", [ Table( - ("Generation:", "Collections:", "Objects collected:", "Object visits:"), + ("Generation:", "Collections:", "Objects collected:", "Object visits:", + "Reachable from roots:", "Not reachable from roots:"), calc_gc_stats, ) ], From 8fe1926164932f868e6e907ad72a74c2f2372b07 Mon Sep 17 00:00:00 2001 From: "Stan U." <89152624+StanFromIreland@users.noreply.github.com> Date: Mon, 18 Nov 2024 15:29:14 +0000 Subject: [PATCH 214/219] gh-126911: Update credits output (#126913) Co-authored-by: Petr Viktorin Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> Co-authored-by: Terry Jan Reedy Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- Lib/site.py | 5 +++-- .../Windows/2024-11-16-22-08-41.gh-issue-126911.HchCZZ.rst | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Windows/2024-11-16-22-08-41.gh-issue-126911.HchCZZ.rst diff --git a/Lib/site.py b/Lib/site.py index 07a6361fad44e5..54f07ab5b4e6d5 100644 --- a/Lib/site.py +++ b/Lib/site.py @@ -442,8 +442,9 @@ def setcopyright(): """Set 'copyright' and 'credits' in builtins""" builtins.copyright = _sitebuiltins._Printer("copyright", sys.copyright) builtins.credits = _sitebuiltins._Printer("credits", """\ - Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands - for supporting Python development. See www.python.org for more information.""") + Thanks to CWI, CNRI, BeOpen, Zope Corporation, the Python Software + Foundation, and a cast of thousands for supporting Python + development. See www.python.org for more information.""") files, dirs = [], [] # Not all modules are required to have a __file__ attribute. See # PEP 420 for more details. diff --git a/Misc/NEWS.d/next/Windows/2024-11-16-22-08-41.gh-issue-126911.HchCZZ.rst b/Misc/NEWS.d/next/Windows/2024-11-16-22-08-41.gh-issue-126911.HchCZZ.rst new file mode 100644 index 00000000000000..32481cde930fcd --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2024-11-16-22-08-41.gh-issue-126911.HchCZZ.rst @@ -0,0 +1 @@ +Update credits command output. From f7ef0203d44acb21ab1c5ff0c3e15f9727862760 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 18 Nov 2024 19:45:25 +0200 Subject: [PATCH 215/219] gh-123803: Support arbitrary code page encodings on Windows (GH-123804) If the cpXXX encoding is not directly implemented in Python, fall back to use the Windows-specific API codecs.code_page_encode() and codecs.code_page_decode(). --- Doc/library/codecs.rst | 7 ++ Doc/whatsnew/3.14.rst | 3 + Lib/encodings/__init__.py | 31 ++--- Lib/encodings/_win_cp_codecs.py | 36 ++++++ Lib/test/test_codecs.py | 118 ++++++++++++++---- ...-09-07-15-16-24.gh-issue-123803.J9VNQU.rst | 1 + 6 files changed, 161 insertions(+), 35 deletions(-) create mode 100644 Lib/encodings/_win_cp_codecs.py create mode 100644 Misc/NEWS.d/next/Windows/2024-09-07-15-16-24.gh-issue-123803.J9VNQU.rst diff --git a/Doc/library/codecs.rst b/Doc/library/codecs.rst index 2cfd8a1eaee806..a129a26190ba99 100644 --- a/Doc/library/codecs.rst +++ b/Doc/library/codecs.rst @@ -1042,6 +1042,10 @@ is meant to be exhaustive. Notice that spelling alternatives that only differ in case or use a hyphen instead of an underscore are also valid aliases; therefore, e.g. ``'utf-8'`` is a valid alias for the ``'utf_8'`` codec. +On Windows, ``cpXXX`` codecs are available for all code pages. +But only codecs listed in the following table are guarantead to exist on +other platforms. + .. impl-detail:: Some common encodings can bypass the codecs lookup machinery to @@ -1307,6 +1311,9 @@ particular, the following variants typically exist: .. versionchanged:: 3.8 ``cp65001`` is now an alias to ``utf_8``. +.. versionchanged:: 3.14 + On Windows, ``cpXXX`` codecs are now available for all code pages. + Python Specific Encodings ------------------------- diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 958efbe73c1c27..8196250d784843 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -194,6 +194,9 @@ Other language changes They raise an error if the argument is a string. (Contributed by Serhiy Storchaka in :gh:`84978`.) +* All Windows code pages are now supported as "cpXXX" codecs on Windows. + (Contributed by Serhiy Storchaka in :gh:`123803`.) + * :class:`super` objects are now :mod:`pickleable ` and :mod:`copyable `. (Contributed by Serhiy Storchaka in :gh:`125767`.) diff --git a/Lib/encodings/__init__.py b/Lib/encodings/__init__.py index f9075b8f0d98ac..298177eb8003a7 100644 --- a/Lib/encodings/__init__.py +++ b/Lib/encodings/__init__.py @@ -156,19 +156,22 @@ def search_function(encoding): codecs.register(search_function) if sys.platform == 'win32': - # bpo-671666, bpo-46668: If Python does not implement a codec for current - # Windows ANSI code page, use the "mbcs" codec instead: - # WideCharToMultiByte() and MultiByteToWideChar() functions with CP_ACP. - # Python does not support custom code pages. - def _alias_mbcs(encoding): + from ._win_cp_codecs import create_win32_code_page_codec + + def win32_code_page_search_function(encoding): + encoding = encoding.lower() + if not encoding.startswith('cp'): + return None try: - import _winapi - ansi_code_page = "cp%s" % _winapi.GetACP() - if encoding == ansi_code_page: - import encodings.mbcs - return encodings.mbcs.getregentry() - except ImportError: - # Imports may fail while we are shutting down - pass + cp = int(encoding[2:]) + except ValueError: + return None + # Test if the code page is supported + try: + codecs.code_page_encode(cp, 'x') + except (OverflowError, OSError): + return None + + return create_win32_code_page_codec(cp) - codecs.register(_alias_mbcs) + codecs.register(win32_code_page_search_function) diff --git a/Lib/encodings/_win_cp_codecs.py b/Lib/encodings/_win_cp_codecs.py new file mode 100644 index 00000000000000..4f8eb886794404 --- /dev/null +++ b/Lib/encodings/_win_cp_codecs.py @@ -0,0 +1,36 @@ +import codecs + +def create_win32_code_page_codec(cp): + from codecs import code_page_encode, code_page_decode + + def encode(input, errors='strict'): + return code_page_encode(cp, input, errors) + + def decode(input, errors='strict'): + return code_page_decode(cp, input, errors, True) + + class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return code_page_encode(cp, input, self.errors)[0] + + class IncrementalDecoder(codecs.BufferedIncrementalDecoder): + def _buffer_decode(self, input, errors, final): + return code_page_decode(cp, input, errors, final) + + class StreamWriter(codecs.StreamWriter): + def encode(self, input, errors='strict'): + return code_page_encode(cp, input, errors) + + class StreamReader(codecs.StreamReader): + def decode(self, input, errors, final): + return code_page_decode(cp, input, errors, final) + + return codecs.CodecInfo( + name=f'cp{cp}', + encode=encode, + decode=decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py index 290656f070503a..e51f7e0ee12b1f 100644 --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -3256,7 +3256,11 @@ def test_code_page_name(self): codecs.code_page_decode, self.CP_UTF8, b'\xff', 'strict', True) def check_decode(self, cp, tests): - for raw, errors, expected in tests: + for raw, errors, expected, *rest in tests: + if rest: + altexpected, = rest + else: + altexpected = expected if expected is not None: try: decoded = codecs.code_page_decode(cp, raw, errors, True) @@ -3273,8 +3277,21 @@ def check_decode(self, cp, tests): self.assertRaises(UnicodeDecodeError, codecs.code_page_decode, cp, raw, errors, True) + if altexpected is not None: + decoded = raw.decode(f'cp{cp}', errors) + self.assertEqual(decoded, altexpected, + '%a.decode("cp%s", %r)=%a != %a' + % (raw, cp, errors, decoded, altexpected)) + else: + self.assertRaises(UnicodeDecodeError, + raw.decode, f'cp{cp}', errors) + def check_encode(self, cp, tests): - for text, errors, expected in tests: + for text, errors, expected, *rest in tests: + if rest: + altexpected, = rest + else: + altexpected = expected if expected is not None: try: encoded = codecs.code_page_encode(cp, text, errors) @@ -3285,18 +3302,26 @@ def check_encode(self, cp, tests): '%a.encode("cp%s", %r)=%a != %a' % (text, cp, errors, encoded[0], expected)) self.assertEqual(encoded[1], len(text)) + + encoded = text.encode(f'cp{cp}', errors) + self.assertEqual(encoded, altexpected, + '%a.encode("cp%s", %r)=%a != %a' + % (text, cp, errors, encoded, altexpected)) else: self.assertRaises(UnicodeEncodeError, codecs.code_page_encode, cp, text, errors) + self.assertRaises(UnicodeEncodeError, + text.encode, f'cp{cp}', errors) def test_cp932(self): self.check_encode(932, ( ('abc', 'strict', b'abc'), ('\uff44\u9a3e', 'strict', b'\x82\x84\xe9\x80'), + ('\uf8f3', 'strict', b'\xff'), # test error handlers ('\xff', 'strict', None), ('[\xff]', 'ignore', b'[]'), - ('[\xff]', 'replace', b'[y]'), + ('[\xff]', 'replace', b'[y]', b'[?]'), ('[\u20ac]', 'replace', b'[?]'), ('[\xff]', 'backslashreplace', b'[\\xff]'), ('[\xff]', 'namereplace', @@ -3310,12 +3335,12 @@ def test_cp932(self): (b'abc', 'strict', 'abc'), (b'\x82\x84\xe9\x80', 'strict', '\uff44\u9a3e'), # invalid bytes - (b'[\xff]', 'strict', None), - (b'[\xff]', 'ignore', '[]'), - (b'[\xff]', 'replace', '[\ufffd]'), - (b'[\xff]', 'backslashreplace', '[\\xff]'), - (b'[\xff]', 'surrogateescape', '[\udcff]'), - (b'[\xff]', 'surrogatepass', None), + (b'[\xff]', 'strict', None, '[\uf8f3]'), + (b'[\xff]', 'ignore', '[]', '[\uf8f3]'), + (b'[\xff]', 'replace', '[\ufffd]', '[\uf8f3]'), + (b'[\xff]', 'backslashreplace', '[\\xff]', '[\uf8f3]'), + (b'[\xff]', 'surrogateescape', '[\udcff]', '[\uf8f3]'), + (b'[\xff]', 'surrogatepass', None, '[\uf8f3]'), (b'\x81\x00abc', 'strict', None), (b'\x81\x00abc', 'ignore', '\x00abc'), (b'\x81\x00abc', 'replace', '\ufffd\x00abc'), @@ -3330,7 +3355,7 @@ def test_cp1252(self): # test error handlers ('\u0141', 'strict', None), ('\u0141', 'ignore', b''), - ('\u0141', 'replace', b'L'), + ('\u0141', 'replace', b'L', b'?'), ('\udc98', 'surrogateescape', b'\x98'), ('\udc98', 'surrogatepass', None), )) @@ -3340,6 +3365,59 @@ def test_cp1252(self): (b'\xff', 'strict', '\xff'), )) + def test_cp708(self): + self.check_encode(708, ( + ('abc2%', 'strict', b'abc2%'), + ('\u060c\u0621\u064a', 'strict', b'\xac\xc1\xea'), + ('\u2562\xe7\xa0', 'strict', b'\x86\x87\xff'), + ('\x9a\x9f', 'strict', b'\x9a\x9f'), + ('\u256b', 'strict', b'\xc0'), + # test error handlers + ('[\u0662]', 'strict', None), + ('[\u0662]', 'ignore', b'[]'), + ('[\u0662]', 'replace', b'[?]'), + ('\udca0', 'surrogateescape', b'\xa0'), + ('\udca0', 'surrogatepass', None), + )) + self.check_decode(708, ( + (b'abc2%', 'strict', 'abc2%'), + (b'\xac\xc1\xea', 'strict', '\u060c\u0621\u064a'), + (b'\x86\x87\xff', 'strict', '\u2562\xe7\xa0'), + (b'\x9a\x9f', 'strict', '\x9a\x9f'), + (b'\xc0', 'strict', '\u256b'), + # test error handlers + (b'\xa0', 'strict', None), + (b'[\xa0]', 'ignore', '[]'), + (b'[\xa0]', 'replace', '[\ufffd]'), + (b'[\xa0]', 'backslashreplace', '[\\xa0]'), + (b'[\xa0]', 'surrogateescape', '[\udca0]'), + (b'[\xa0]', 'surrogatepass', None), + )) + + def test_cp20106(self): + self.check_encode(20106, ( + ('abc', 'strict', b'abc'), + ('\xa7\xc4\xdf', 'strict', b'@[~'), + # test error handlers + ('@', 'strict', None), + ('@', 'ignore', b''), + ('@', 'replace', b'?'), + ('\udcbf', 'surrogateescape', b'\xbf'), + ('\udcbf', 'surrogatepass', None), + )) + self.check_decode(20106, ( + (b'abc', 'strict', 'abc'), + (b'@[~', 'strict', '\xa7\xc4\xdf'), + (b'\xe1\xfe', 'strict', 'a\xdf'), + # test error handlers + (b'(\xbf)', 'strict', None), + (b'(\xbf)', 'ignore', '()'), + (b'(\xbf)', 'replace', '(\ufffd)'), + (b'(\xbf)', 'backslashreplace', '(\\xbf)'), + (b'(\xbf)', 'surrogateescape', '(\udcbf)'), + (b'(\xbf)', 'surrogatepass', None), + )) + def test_cp_utf7(self): cp = 65000 self.check_encode(cp, ( @@ -3412,17 +3490,15 @@ def test_incremental(self): False) self.assertEqual(decoded, ('abc', 3)) - def test_mbcs_alias(self): - # Check that looking up our 'default' codepage will return - # mbcs when we don't have a more specific one available - code_page = 99_999 - name = f'cp{code_page}' - with mock.patch('_winapi.GetACP', return_value=code_page): - try: - codec = codecs.lookup(name) - self.assertEqual(codec.name, 'mbcs') - finally: - codecs.unregister(name) + def test_mbcs_code_page(self): + # Check that codec for the current Windows (ANSII) code page is + # always available. + try: + from _winapi import GetACP + except ImportError: + self.skipTest('requires _winapi.GetACP') + cp = GetACP() + codecs.lookup(f'cp{cp}') @support.bigmemtest(size=2**31, memuse=7, dry_run=False) def test_large_input(self, size): diff --git a/Misc/NEWS.d/next/Windows/2024-09-07-15-16-24.gh-issue-123803.J9VNQU.rst b/Misc/NEWS.d/next/Windows/2024-09-07-15-16-24.gh-issue-123803.J9VNQU.rst new file mode 100644 index 00000000000000..3ad4d12eb0dc3e --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2024-09-07-15-16-24.gh-issue-123803.J9VNQU.rst @@ -0,0 +1 @@ +All Windows code pages are now supported as "cpXXX" codecs on Windows. From 933f21c3c92f758fb0615d6a4cca10249c686ae7 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 18 Nov 2024 20:13:20 +0200 Subject: [PATCH 216/219] gh-85957: Add missing MIME types for images with RFCs (#126966) --- Doc/whatsnew/3.14.rst | 11 +++++++++++ Lib/mimetypes.py | 9 +++++++++ Lib/test/test_mimetypes.py | 8 ++++++++ .../2024-11-18-15-33-25.gh-issue-85957.8gT3B-.rst | 2 ++ 4 files changed, 30 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2024-11-18-15-33-25.gh-issue-85957.8gT3B-.rst diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 8196250d784843..f2c357b1ebc709 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -396,6 +396,17 @@ mimetypes (Contributed by Hugo van Kemenade in :gh:`89416`.) +* Add MIME types for images with RFCs: + + * :rfc:`1494`: CCITT Group 3 (``.g3``) + * :rfc:`3362`: Real-time Facsimile, T.38 (``.t38``) + * :rfc:`3745`: JPEG 2000 (``.jp2``), extension (``.jpx``) and compound (``.jpm``) + * :rfc:`3950`: Tag Image File Format Fax eXtended, TIFF-FX (``.tfx``) + * :rfc:`4047`: Flexible Image Transport System (``.fits``) + * :rfc:`7903`: Enhanced Metafile (``.emf``) and Windows Metafile (``.wmf``) + + (Contributed by Hugo van Kemenade in :gh:`85957`.) + multiprocessing --------------- diff --git a/Lib/mimetypes.py b/Lib/mimetypes.py index 210d2264757d08..61cba1ac4932d0 100644 --- a/Lib/mimetypes.py +++ b/Lib/mimetypes.py @@ -550,19 +550,28 @@ def _default_mime_types(): '.woff2' : 'font/woff2', '.avif' : 'image/avif', '.bmp' : 'image/bmp', + '.emf' : 'image/emf', + '.fits' : 'image/fits', + '.g3' : 'image/g3fax', '.gif' : 'image/gif', '.ief' : 'image/ief', + '.jp2' : 'image/jp2', '.jpg' : 'image/jpeg', '.jpe' : 'image/jpeg', '.jpeg' : 'image/jpeg', + '.jpm' : 'image/jpm', + '.jpx' : 'image/jpx', '.heic' : 'image/heic', '.heif' : 'image/heif', '.png' : 'image/png', '.svg' : 'image/svg+xml', + '.t38' : 'image/t38', '.tiff' : 'image/tiff', '.tif' : 'image/tiff', + '.tfx' : 'image/tiff-fx', '.ico' : 'image/vnd.microsoft.icon', '.webp' : 'image/webp', + '.wmf' : 'image/wmf', '.ras' : 'image/x-cmu-raster', '.pnm' : 'image/x-portable-anymap', '.pbm' : 'image/x-portable-bitmap', diff --git a/Lib/test/test_mimetypes.py b/Lib/test/test_mimetypes.py index c4bb8dfb1a7422..0a5b511e75537c 100644 --- a/Lib/test/test_mimetypes.py +++ b/Lib/test/test_mimetypes.py @@ -240,9 +240,17 @@ def check_extensions(): ("font/woff", ".woff"), ("font/woff2", ".woff2"), ("image/avif", ".avif"), + ("image/emf", ".emf"), + ("image/fits", ".fits"), + ("image/g3fax", ".g3"), + ("image/jp2", ".jp2"), + ("image/jpm", ".jpm"), + ("image/t38", ".t38"), ("image/webp", ".webp"), + ("image/wmf", ".wmf"), ("image/jpeg", ".jpg"), ("image/tiff", ".tiff"), + ("image/tiff-fx", ".tfx"), ("message/rfc822", ".eml"), ("text/html", ".html"), ("text/plain", ".txt"), diff --git a/Misc/NEWS.d/next/Library/2024-11-18-15-33-25.gh-issue-85957.8gT3B-.rst b/Misc/NEWS.d/next/Library/2024-11-18-15-33-25.gh-issue-85957.8gT3B-.rst new file mode 100644 index 00000000000000..02d3fe3077e4d1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-11-18-15-33-25.gh-issue-85957.8gT3B-.rst @@ -0,0 +1,2 @@ +Add missing MIME types for images with RFCs: emf, fits, g3fax, jp2, jpm, +jpx, t38, tiff-fx and wmf. Patch by Hugo van Kemenade. From 4cd10762b06ec57252e3c7373e74240b4d0c5ed8 Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Mon, 18 Nov 2024 11:11:23 -0800 Subject: [PATCH 217/219] GH-126795: Increase the JIT threshold from 16 to 4096 (GH-126816) --- Include/internal/pycore_backoff.h | 7 +- Lib/test/test_capi/test_opt.py | 194 +++++++++--------- ...-11-13-17-18-13.gh-issue-126795._JBX9e.rst | 2 + Modules/_testinternalcapi.c | 2 +- 4 files changed, 104 insertions(+), 101 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-11-13-17-18-13.gh-issue-126795._JBX9e.rst diff --git a/Include/internal/pycore_backoff.h b/Include/internal/pycore_backoff.h index 20436a68b69677..3e02728522828e 100644 --- a/Include/internal/pycore_backoff.h +++ b/Include/internal/pycore_backoff.h @@ -102,10 +102,9 @@ backoff_counter_triggers(_Py_BackoffCounter counter) } /* Initial JUMP_BACKWARD counter. - * This determines when we create a trace for a loop. -* Backoff sequence 16, 32, 64, 128, 256, 512, 1024, 2048, 4096. */ -#define JUMP_BACKWARD_INITIAL_VALUE 15 -#define JUMP_BACKWARD_INITIAL_BACKOFF 4 + * This determines when we create a trace for a loop. */ +#define JUMP_BACKWARD_INITIAL_VALUE 4095 +#define JUMP_BACKWARD_INITIAL_BACKOFF 12 static inline _Py_BackoffCounter initial_jump_backoff_counter(void) { diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index 9726353bcd6a6d..edd83872e573e4 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -62,9 +62,9 @@ def test_get_set_optimizer(self): def test_counter_optimizer(self): # Generate a new function at each call ns = {} - exec(textwrap.dedent(""" + exec(textwrap.dedent(f""" def loop(): - for _ in range(1000): + for _ in range({TIER2_THRESHOLD + 1000}): pass """), ns, ns) loop = ns['loop'] @@ -75,20 +75,19 @@ def loop(): self.assertEqual(opt.get_count(), 0) with clear_executors(loop): loop() - # Subtract because optimizer doesn't kick in sooner - self.assertEqual(opt.get_count(), 1000 - TIER2_THRESHOLD) + self.assertEqual(opt.get_count(), 1001) def test_long_loop(self): "Check that we aren't confused by EXTENDED_ARG" # Generate a new function at each call ns = {} - exec(textwrap.dedent(""" + exec(textwrap.dedent(f""" def nop(): pass def long_loop(): - for _ in range(20): + for _ in range({TIER2_THRESHOLD + 20}): nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop(); @@ -103,7 +102,7 @@ def long_loop(): with temporary_optimizer(opt): self.assertEqual(opt.get_count(), 0) long_loop() - self.assertEqual(opt.get_count(), 20 - TIER2_THRESHOLD) # Need iterations to warm up + self.assertEqual(opt.get_count(), 21) # Need iterations to warm up def test_code_restore_for_ENTER_EXECUTOR(self): def testfunc(x): @@ -159,7 +158,7 @@ def test_invalidate_object(self): func_src = "\n".join( f""" def f{n}(): - for _ in range(1000): + for _ in range({TIER2_THRESHOLD}): pass """ for n in range(5) ) @@ -190,9 +189,9 @@ def f{n}(): def test_uop_optimizer_invalidation(self): # Generate a new function at each call ns = {} - exec(textwrap.dedent(""" + exec(textwrap.dedent(f""" def f(): - for i in range(1000): + for i in range({TIER2_THRESHOLD}): pass """), ns, ns) f = ns['f'] @@ -207,7 +206,7 @@ def f(): def test_sys__clear_internal_caches(self): def f(): - for _ in range(1000): + for _ in range(TIER2_THRESHOLD): pass opt = _testinternalcapi.new_uop_optimizer() with temporary_optimizer(opt): @@ -236,7 +235,7 @@ def testfunc(x): opt = _testinternalcapi.new_uop_optimizer() with temporary_optimizer(opt): - testfunc(1000) + testfunc(TIER2_THRESHOLD) ex = get_first_executor(testfunc) self.assertIsNotNone(ex) @@ -247,7 +246,7 @@ def testfunc(x): def test_extended_arg(self): "Check EXTENDED_ARG handling in superblock creation" ns = {} - exec(textwrap.dedent(""" + exec(textwrap.dedent(f""" def many_vars(): # 260 vars, so z9 should have index 259 a0 = a1 = a2 = a3 = a4 = a5 = a6 = a7 = a8 = a9 = 42 @@ -275,7 +274,7 @@ def many_vars(): w0 = w1 = w2 = w3 = w4 = w5 = w6 = w7 = w8 = w9 = 42 x0 = x1 = x2 = x3 = x4 = x5 = x6 = x7 = x8 = x9 = 42 y0 = y1 = y2 = y3 = y4 = y5 = y6 = y7 = y8 = y9 = 42 - z0 = z1 = z2 = z3 = z4 = z5 = z6 = z7 = z8 = z9 = 42 + z0 = z1 = z2 = z3 = z4 = z5 = z6 = z7 = z8 = z9 = {TIER2_THRESHOLD} while z9 > 0: z9 = z9 - 1 +z9 @@ -308,7 +307,7 @@ def testfunc(x): opt = _testinternalcapi.new_uop_optimizer() with temporary_optimizer(opt): - testfunc(20) + testfunc(TIER2_THRESHOLD) ex = get_first_executor(testfunc) self.assertIsNotNone(ex) @@ -323,7 +322,7 @@ def testfunc(n): opt = _testinternalcapi.new_uop_optimizer() with temporary_optimizer(opt): - testfunc(20) + testfunc(TIER2_THRESHOLD) ex = get_first_executor(testfunc) self.assertIsNotNone(ex) @@ -338,7 +337,7 @@ def testfunc(a): opt = _testinternalcapi.new_uop_optimizer() with temporary_optimizer(opt): - testfunc(range(20)) + testfunc(range(TIER2_THRESHOLD)) ex = get_first_executor(testfunc) self.assertIsNotNone(ex) @@ -355,7 +354,7 @@ def testfunc(a): opt = _testinternalcapi.new_uop_optimizer() with temporary_optimizer(opt): - testfunc(range(20)) + testfunc(range(TIER2_THRESHOLD)) ex = get_first_executor(testfunc) self.assertIsNotNone(ex) @@ -371,7 +370,7 @@ def testfunc(n): opt = _testinternalcapi.new_uop_optimizer() with temporary_optimizer(opt): - testfunc(20) + testfunc(TIER2_THRESHOLD) ex = get_first_executor(testfunc) self.assertIsNotNone(ex) @@ -386,7 +385,7 @@ def testfunc(n): opt = _testinternalcapi.new_uop_optimizer() with temporary_optimizer(opt): - testfunc(20) + testfunc(TIER2_THRESHOLD) ex = get_first_executor(testfunc) self.assertIsNotNone(ex) @@ -406,7 +405,7 @@ def testfunc(n): opt = _testinternalcapi.new_uop_optimizer() with temporary_optimizer(opt): - testfunc(20) + testfunc(TIER2_THRESHOLD) ex = get_first_executor(testfunc) self.assertIsNotNone(ex) @@ -424,8 +423,8 @@ def testfunc(n): opt = _testinternalcapi.new_uop_optimizer() with temporary_optimizer(opt): - total = testfunc(20) - self.assertEqual(total, 190) + total = testfunc(TIER2_THRESHOLD) + self.assertEqual(total, sum(range(TIER2_THRESHOLD))) ex = get_first_executor(testfunc) self.assertIsNotNone(ex) @@ -445,9 +444,9 @@ def testfunc(a): opt = _testinternalcapi.new_uop_optimizer() with temporary_optimizer(opt): - a = list(range(20)) + a = list(range(TIER2_THRESHOLD)) total = testfunc(a) - self.assertEqual(total, 190) + self.assertEqual(total, sum(a)) ex = get_first_executor(testfunc) self.assertIsNotNone(ex) @@ -467,9 +466,9 @@ def testfunc(a): opt = _testinternalcapi.new_uop_optimizer() with temporary_optimizer(opt): - a = tuple(range(20)) + a = tuple(range(TIER2_THRESHOLD)) total = testfunc(a) - self.assertEqual(total, 190) + self.assertEqual(total, sum(a)) ex = get_first_executor(testfunc) self.assertIsNotNone(ex) @@ -503,7 +502,7 @@ def dummy(x): opt = _testinternalcapi.new_uop_optimizer() with temporary_optimizer(opt): - testfunc(20) + testfunc(TIER2_THRESHOLD) ex = get_first_executor(testfunc) self.assertIsNotNone(ex) @@ -521,7 +520,7 @@ def testfunc(n): opt = _testinternalcapi.new_uop_optimizer() with temporary_optimizer(opt): - testfunc(20) + testfunc(TIER2_THRESHOLD) ex = get_first_executor(testfunc) self.assertIsNotNone(ex) @@ -549,9 +548,9 @@ def testfunc(n, m): opt = _testinternalcapi.new_uop_optimizer() with temporary_optimizer(opt): - x = testfunc(10, 10) + x = testfunc(TIER2_THRESHOLD, TIER2_THRESHOLD) - self.assertEqual(x, sum(range(10)) * 10010) + self.assertEqual(x, sum(range(TIER2_THRESHOLD)) * TIER2_THRESHOLD * 1001) ex = get_first_executor(testfunc) self.assertIsNotNone(ex) @@ -572,15 +571,13 @@ def testfunc(n): bits += 1 if i&0x10: bits += 1 - if i&0x20: - bits += 1 return bits opt = _testinternalcapi.new_uop_optimizer() with temporary_optimizer(opt): - x = testfunc(20) + x = testfunc(TIER2_THRESHOLD * 2) - self.assertEqual(x, 40) + self.assertEqual(x, TIER2_THRESHOLD * 5) ex = get_first_executor(testfunc) self.assertIsNotNone(ex) ops = list(iter_opnames(ex)) @@ -615,9 +612,9 @@ def testfunc(loops): num += 1 return a - res, ex = self._run_with_optimizer(testfunc, 32) + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) self.assertIsNotNone(ex) - self.assertEqual(res, 63) + self.assertEqual(res, (TIER2_THRESHOLD - 1) * 2 + 1) binop_count = [opname for opname in iter_opnames(ex) if opname == "_BINARY_OP_ADD_INT"] guard_both_int_count = [opname for opname in iter_opnames(ex) if opname == "_GUARD_BOTH_INT"] self.assertGreaterEqual(len(binop_count), 3) @@ -637,11 +634,11 @@ def testfunc(loops): opt = _testinternalcapi.new_uop_optimizer() res = None with temporary_optimizer(opt): - res = testfunc(32) + res = testfunc(TIER2_THRESHOLD) ex = get_first_executor(testfunc) self.assertIsNotNone(ex) - self.assertEqual(res, 124) + self.assertEqual(res, (TIER2_THRESHOLD - 1) * 4) binop_count = [opname for opname in iter_opnames(ex) if opname == "_BINARY_OP_ADD_INT"] guard_both_int_count = [opname for opname in iter_opnames(ex) if opname == "_GUARD_BOTH_INT"] self.assertGreaterEqual(len(binop_count), 3) @@ -661,11 +658,11 @@ def testfunc(loops): opt = _testinternalcapi.new_uop_optimizer() res = None with temporary_optimizer(opt): - res = testfunc(32) + res = testfunc(TIER2_THRESHOLD) ex = get_first_executor(testfunc) self.assertIsNotNone(ex) - self.assertEqual(res, 124) + self.assertEqual(res, (TIER2_THRESHOLD - 1) * 4) binop_count = [opname for opname in iter_opnames(ex) if opname == "_BINARY_OP_ADD_INT"] guard_both_int_count = [opname for opname in iter_opnames(ex) if opname == "_GUARD_BOTH_INT"] self.assertGreaterEqual(len(binop_count), 3) @@ -682,7 +679,7 @@ def testfunc(loops): num += 1 return a - res, ex = self._run_with_optimizer(testfunc, 64) + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) self.assertIsNotNone(ex) binop_count = [opname for opname in iter_opnames(ex) if opname == "_BINARY_OP_ADD_INT"] self.assertGreaterEqual(len(binop_count), 3) @@ -694,7 +691,7 @@ def dummy(x): for i in range(n): dummy(i) - res, ex = self._run_with_optimizer(testfunc, 32) + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) self.assertIsNotNone(ex) uops = get_opnames(ex) self.assertIn("_PUSH_FRAME", uops) @@ -708,8 +705,8 @@ def testfunc(n): x = i + i return x - res, ex = self._run_with_optimizer(testfunc, 32) - self.assertEqual(res, 62) + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, (TIER2_THRESHOLD - 1) * 2) self.assertIsNotNone(ex) uops = get_opnames(ex) self.assertNotIn("_GUARD_BOTH_INT", uops) @@ -726,7 +723,7 @@ def testfunc(n): res = x + z + a + b return res - res, ex = self._run_with_optimizer(testfunc, 32) + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) self.assertEqual(res, 4) self.assertIsNotNone(ex) uops = get_opnames(ex) @@ -739,8 +736,8 @@ def testfunc(n): for _ in range(n): return [i for i in range(n)] - res, ex = self._run_with_optimizer(testfunc, 32) - self.assertEqual(res, list(range(32))) + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, list(range(TIER2_THRESHOLD))) self.assertIsNotNone(ex) uops = get_opnames(ex) self.assertNotIn("_BINARY_OP_ADD_INT", uops) @@ -797,7 +794,7 @@ def testfunc(n): opt = _testinternalcapi.new_uop_optimizer() _testinternalcapi.set_optimizer(opt) - testfunc(64) + testfunc(_testinternalcapi.TIER2_THRESHOLD) ex = get_first_executor(testfunc) assert ex is not None @@ -817,8 +814,8 @@ def testfunc(n): a = a + 0.25 return a - res, ex = self._run_with_optimizer(testfunc, 32) - self.assertAlmostEqual(res, 33.0) + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertAlmostEqual(res, TIER2_THRESHOLD + 1) self.assertIsNotNone(ex) uops = get_opnames(ex) guard_both_float_count = [opname for opname in iter_opnames(ex) if opname == "_GUARD_BOTH_FLOAT"] @@ -837,8 +834,8 @@ def testfunc(n): a = a - 0.25 return a - res, ex = self._run_with_optimizer(testfunc, 32) - self.assertAlmostEqual(res, -31.0) + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertAlmostEqual(res, -TIER2_THRESHOLD + 1) self.assertIsNotNone(ex) uops = get_opnames(ex) guard_both_float_count = [opname for opname in iter_opnames(ex) if opname == "_GUARD_BOTH_FLOAT"] @@ -857,7 +854,7 @@ def testfunc(n): a = a * 1.0 return a - res, ex = self._run_with_optimizer(testfunc, 32) + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) self.assertAlmostEqual(res, 1.0) self.assertIsNotNone(ex) uops = get_opnames(ex) @@ -877,7 +874,7 @@ def testfunc(n): a + a return a - res, ex = self._run_with_optimizer(testfunc, 32) + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) self.assertEqual(res, "") self.assertIsNotNone(ex) uops = get_opnames(ex) @@ -895,7 +892,7 @@ def testfunc(n): x = a == a return x - res, ex = self._run_with_optimizer(testfunc, 32) + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) self.assertTrue(res) self.assertIsNotNone(ex) uops = get_opnames(ex) @@ -913,7 +910,7 @@ def testfunc(n): x = a == a return x - res, ex = self._run_with_optimizer(testfunc, 32) + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) self.assertTrue(res) self.assertIsNotNone(ex) uops = get_opnames(ex) @@ -931,7 +928,7 @@ def testfunc(n): x = 1 return x - res, ex = self._run_with_optimizer(testfunc, 32) + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) self.assertEqual(res, 1) self.assertIsNotNone(ex) uops = get_opnames(ex) @@ -951,7 +948,7 @@ def testfunc(n): x = 1 return x - res, ex = self._run_with_optimizer(testfunc, 32) + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) self.assertEqual(res, 1) self.assertIsNotNone(ex) uops = get_opnames(ex) @@ -971,7 +968,7 @@ def testfunc(n): x = a == a return x - res, ex = self._run_with_optimizer(testfunc, 32) + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) self.assertTrue(res) self.assertIsNotNone(ex) uops = get_opnames(ex) @@ -989,10 +986,10 @@ def testfunc(n): exec(src, ns, ns) testfunc = ns['testfunc'] ns['_test_global'] = 0 - _, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + _, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD - 1) self.assertIsNone(ex) ns['_test_global'] = 1 - _, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + _, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD - 1) self.assertIsNotNone(ex) uops = get_opnames(ex) self.assertNotIn("_GUARD_BOTH_INT", uops) @@ -1003,10 +1000,10 @@ def testfunc(n): exec(src, ns, ns) testfunc = ns['testfunc'] ns['_test_global'] = 0 - _, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + _, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD - 1) self.assertIsNone(ex) ns['_test_global'] = 3.14 - _, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + _, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD - 1) self.assertIsNone(ex) def test_combine_stack_space_checks_sequential(self): @@ -1023,8 +1020,8 @@ def testfunc(n): a += b + c + d return a - res, ex = self._run_with_optimizer(testfunc, 32) - self.assertEqual(res, 832) + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD * 26) self.assertIsNotNone(ex) uops_and_operands = [(opcode, operand) for opcode, _, _, operand in ex] @@ -1050,8 +1047,8 @@ def testfunc(n): a += b + c return a - res, ex = self._run_with_optimizer(testfunc, 32) - self.assertEqual(res, 224) + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD * 7) self.assertIsNotNone(ex) uops_and_operands = [(opcode, operand) for opcode, _, _, operand in ex] @@ -1085,8 +1082,8 @@ def testfunc(n): a += b + c + d + e return a - res, ex = self._run_with_optimizer(testfunc, 32) - self.assertEqual(res, 800) + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD * 25) self.assertIsNotNone(ex) uops_and_operands = [(opcode, operand) for opcode, _, _, operand in ex] @@ -1121,8 +1118,8 @@ def testfunc(n): a += b + c + d + e return a - res, ex = self._run_with_optimizer(testfunc, 32) - self.assertEqual(res, 800) + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD * 25) self.assertIsNotNone(ex) uops_and_operands = [(opcode, operand) for opcode, _, _, operand in ex] @@ -1157,16 +1154,16 @@ def dummy6(x): z = dummy0(y) return dummy4(z) def testfunc(n): - a = 0; - for _ in range(32): + a = 0 + for _ in range(n): b = dummy5(1) c = dummy0(1) d = dummy6(1) a += b + c + d return a - res, ex = self._run_with_optimizer(testfunc, 32) - self.assertEqual(res, 96) + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD * 3) self.assertIsNotNone(ex) uops_and_operands = [(opcode, operand) for opcode, _, _, operand in ex] @@ -1225,8 +1222,8 @@ def testfunc(n): b += dummy15(7) return b - res, ex = self._run_with_optimizer(testfunc, 32) - self.assertEqual(res, 32 * (repetitions + 9)) + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD * (repetitions + 9)) self.assertIsNotNone(ex) uops_and_operands = [(opcode, operand) for opcode, _, _, operand in ex] @@ -1259,8 +1256,13 @@ def testfunc(n): a += dummy15(n) return a - res, ex = self._run_with_optimizer(testfunc, 32) - self.assertEqual(res, 42 * 32) + recursion_limit = sys.getrecursionlimit() + try: + sys.setrecursionlimit(TIER2_THRESHOLD + recursion_limit) + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + finally: + sys.setrecursionlimit(recursion_limit) + self.assertEqual(res, TIER2_THRESHOLD * 42) self.assertIsNotNone(ex) uops_and_operands = [(opcode, operand) for opcode, _, _, operand in ex] @@ -1307,8 +1309,8 @@ def testfunc(n): for i in range(n): gen() return i - res, ex = self._run_with_optimizer(testfunc, 20) - self.assertEqual(res, 19) + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD - 1) self.assertIsNotNone(ex) self.assertIn("_RETURN_GENERATOR", get_opnames(ex)) @@ -1322,8 +1324,8 @@ def testfunc(n): for i in g: s += i return s - res, ex = self._run_with_optimizer(testfunc, 20) - self.assertEqual(res, 190) + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, sum(range(TIER2_THRESHOLD))) self.assertIsNotNone(ex) self.assertIn("_FOR_ITER_GEN_FRAME", get_opnames(ex)) @@ -1342,7 +1344,7 @@ def test_modified_local_is_seen_by_optimized_code(self): def test_guard_type_version_removed(self): def thing(a): x = 0 - for _ in range(100): + for _ in range(TIER2_THRESHOLD): x += a.attr x += a.attr return x @@ -1353,7 +1355,7 @@ class Foo: res, ex = self._run_with_optimizer(thing, Foo()) opnames = list(iter_opnames(ex)) self.assertIsNotNone(ex) - self.assertEqual(res, 200) + self.assertEqual(res, TIER2_THRESHOLD * 2) guard_type_version_count = opnames.count("_GUARD_TYPE_VERSION") self.assertEqual(guard_type_version_count, 1) @@ -1367,7 +1369,7 @@ def fn(): def thing(a): x = 0 - for _ in range(100): + for _ in range(TIER2_THRESHOLD): x += a.attr fn() x += a.attr @@ -1379,7 +1381,7 @@ class Foo: res, ex = self._run_with_optimizer(thing, Foo()) opnames = list(iter_opnames(ex)) self.assertIsNotNone(ex) - self.assertEqual(res, 200) + self.assertEqual(res, TIER2_THRESHOLD * 2) guard_type_version_count = opnames.count("_GUARD_TYPE_VERSION") self.assertEqual(guard_type_version_count, 1) @@ -1390,13 +1392,13 @@ def test_guard_type_version_not_removed(self): def thing(a): x = 0 - for i in range(100): + for i in range(TIER2_THRESHOLD + 100): x += a.attr - # for the first 90 iterations we set the attribute on this dummy function which shouldn't + # for the first (TIER2_THRESHOLD + 90) iterations we set the attribute on this dummy function which shouldn't # trigger the type watcher - # then after 90 it should trigger it and stop optimizing + # then after for the next 10 it should trigger it and stop optimizing # Note that the code needs to be in this weird form so it's optimized inline without any control flow - setattr((Foo, Bar)[i < 90], "attr", 2) + setattr((Foo, Bar)[i < TIER2_THRESHOLD + 90], "attr", 2) x += a.attr return x @@ -1410,7 +1412,7 @@ class Bar: opnames = list(iter_opnames(ex)) self.assertIsNotNone(ex) - self.assertEqual(res, 219) + self.assertEqual(res, (TIER2_THRESHOLD * 2) + 219) guard_type_version_count = opnames.count("_GUARD_TYPE_VERSION") self.assertEqual(guard_type_version_count, 2) @@ -1449,7 +1451,7 @@ def test_guard_type_version_executor_invalidated(self): def thing(a): x = 0 - for i in range(100): + for i in range(TIER2_THRESHOLD): x += a.attr x += a.attr return x @@ -1458,7 +1460,7 @@ class Foo: attr = 1 res, ex = self._run_with_optimizer(thing, Foo()) - self.assertEqual(res, 200) + self.assertEqual(res, TIER2_THRESHOLD * 2) self.assertIsNotNone(ex) self.assertEqual(list(iter_opnames(ex)).count("_GUARD_TYPE_VERSION"), 1) self.assertTrue(ex.is_valid()) @@ -1494,7 +1496,7 @@ def testfunc(n): opt = _testinternalcapi.new_uop_optimizer() with temporary_optimizer(opt): - testfunc(20) + testfunc(TIER2_THRESHOLD) ex = get_first_executor(testfunc) self.assertIsNotNone(ex) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-13-17-18-13.gh-issue-126795._JBX9e.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-13-17-18-13.gh-issue-126795._JBX9e.rst new file mode 100644 index 00000000000000..16799534ef37cf --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-13-17-18-13.gh-issue-126795._JBX9e.rst @@ -0,0 +1,2 @@ +Increase the threshold for JIT code warmup. Depending on platform and workload, +this can result in performance gains of 1-9% and memory savings of 3-5%. diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index 35cb215418e0cd..5e21d96876e1d5 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -2229,7 +2229,7 @@ module_exec(PyObject *module) } if (PyModule_Add(module, "TIER2_THRESHOLD", - PyLong_FromLong(JUMP_BACKWARD_INITIAL_VALUE)) < 0) { + PyLong_FromLong(JUMP_BACKWARD_INITIAL_VALUE + 1)) < 0) { return 1; } From 0063f5f314350ad5122a86f31df65f5dff4f4e5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns=20=F0=9F=87=B5=F0=9F=87=B8?= Date: Mon, 18 Nov 2024 23:50:01 +0000 Subject: [PATCH 218/219] getpath: fix warning typo (#126978) --- Modules/getpath.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/getpath.py b/Modules/getpath.py index 1f1bfcb4f64dd4..1c1eb6cbf3ee22 100644 --- a/Modules/getpath.py +++ b/Modules/getpath.py @@ -420,7 +420,7 @@ def search_up(prefix, *landmarks, test=isfile): # Only warn if the file actually exists and was unresolvable # Otherwise users who specify a fake executable may get spurious warnings. if isfile(real_executable): - warn(f'Failed to find real location of {base_executable}') + warn(f'Failed to find real location of {real_executable}') if not executable_dir and os_name == 'darwin' and library: # QUIRK: macOS checks adjacent to its library early From d6b3e78504b3168c432b20002dbcf8ec9a435e61 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 18 Nov 2024 17:11:12 -0700 Subject: [PATCH 219/219] gh-126986: Drop _PyInterpreterState_FailIfNotRunning() (gh-126988) We replace it with _PyErr_SetInterpreterAlreadyRunning(). --- Include/internal/pycore_pystate.h | 2 +- Python/crossinterp.c | 3 +-- Python/pystate.c | 20 ++++++++------------ 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/Include/internal/pycore_pystate.h b/Include/internal/pycore_pystate.h index edcd75a55b686b..f4fbf3734e2d44 100644 --- a/Include/internal/pycore_pystate.h +++ b/Include/internal/pycore_pystate.h @@ -82,7 +82,7 @@ PyAPI_FUNC(PyObject *) _PyInterpreterState_GetIDObject(PyInterpreterState *); PyAPI_FUNC(int) _PyInterpreterState_SetRunningMain(PyInterpreterState *); PyAPI_FUNC(void) _PyInterpreterState_SetNotRunningMain(PyInterpreterState *); PyAPI_FUNC(int) _PyInterpreterState_IsRunningMain(PyInterpreterState *); -PyAPI_FUNC(int) _PyInterpreterState_FailIfRunningMain(PyInterpreterState *); +PyAPI_FUNC(void) _PyErr_SetInterpreterAlreadyRunning(void); extern int _PyThreadState_IsRunningMain(PyThreadState *); extern void _PyInterpreterState_ReinitRunningMain(PyThreadState *); diff --git a/Python/crossinterp.c b/Python/crossinterp.c index fe7d75f6b72f68..7aaa045f375cf0 100644 --- a/Python/crossinterp.c +++ b/Python/crossinterp.c @@ -983,8 +983,7 @@ _PyXI_ApplyErrorCode(_PyXI_errcode code, PyInterpreterState *interp) break; case _PyXI_ERR_ALREADY_RUNNING: assert(interp != NULL); - assert(_PyInterpreterState_IsRunningMain(interp)); - _PyInterpreterState_FailIfRunningMain(interp); + _PyErr_SetInterpreterAlreadyRunning(); break; case _PyXI_ERR_MAIN_NS_FAILURE: PyErr_SetString(PyExc_InterpreterError, diff --git a/Python/pystate.c b/Python/pystate.c index 24ee73c145cbcc..a209a26f16f840 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1047,10 +1047,17 @@ get_main_thread(PyInterpreterState *interp) return _Py_atomic_load_ptr_relaxed(&interp->threads.main); } +void +_PyErr_SetInterpreterAlreadyRunning(void) +{ + PyErr_SetString(PyExc_InterpreterError, "interpreter already running"); +} + int _PyInterpreterState_SetRunningMain(PyInterpreterState *interp) { - if (_PyInterpreterState_FailIfRunningMain(interp) < 0) { + if (get_main_thread(interp) != NULL) { + _PyErr_SetInterpreterAlreadyRunning(); return -1; } PyThreadState *tstate = current_fast_get(); @@ -1096,17 +1103,6 @@ _PyThreadState_IsRunningMain(PyThreadState *tstate) return get_main_thread(interp) == tstate; } -int -_PyInterpreterState_FailIfRunningMain(PyInterpreterState *interp) -{ - if (get_main_thread(interp) != NULL) { - PyErr_SetString(PyExc_InterpreterError, - "interpreter already running"); - return -1; - } - return 0; -} - void _PyInterpreterState_ReinitRunningMain(PyThreadState *tstate) {