From 28878140cdbf27fb610cbfe3c16ed45505736b0c Mon Sep 17 00:00:00 2001 From: Phil Thompson Date: Mon, 1 Jun 2026 13:24:54 +0100 Subject: [PATCH 001/185] Document that `PyErr_SetRaisedException()` accepts `NULL` Documented that `PyErr_SetRaisedException()` accepts `NULL` as the value of the exception being set. --- Doc/c-api/exceptions.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst index fd9ea6272df7d84..a6b2d90314e3afe 100644 --- a/Doc/c-api/exceptions.rst +++ b/Doc/c-api/exceptions.rst @@ -503,7 +503,8 @@ Querying the error indicator .. warning:: - This call steals a reference to *exc*, which must be a valid exception. + *exc* must be a valid exception (in which case a reference is stolen) or + ``NULL``. .. versionadded:: 3.12 From 4d0717287f0e5de8fa063f7b5e5565d2912cc9d0 Mon Sep 17 00:00:00 2001 From: Phil Thompson Date: Wed, 3 Jun 2026 11:09:03 +0100 Subject: [PATCH 002/185] Document that `PyErr_SetRaisedException()` accepts `NULL` Removed the warning and expanded the main part of the explanation. --- Doc/c-api/exceptions.rst | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst index 2b199f33cbf3110..40b75ad34efc3bb 100644 --- a/Doc/c-api/exceptions.rst +++ b/Doc/c-api/exceptions.rst @@ -498,13 +498,9 @@ Querying the error indicator .. c:function:: void PyErr_SetRaisedException(PyObject *exc) - Set *exc* as the exception currently being raised, - clearing the existing exception if one is set. - - .. warning:: - - *exc* must be a valid exception (in which case a reference is stolen) or - ``NULL``. + A stolen reference to *exc* is set as the exception currently being raised, + clearing the existing exception if one is set. *exc* may be ``NULL`` in + which case any current exception is cleared. .. versionadded:: 3.12 From e1bf6a23ddab9cc432ca78538559bf822cffa0a7 Mon Sep 17 00:00:00 2001 From: Edward Xu Date: Wed, 3 Jun 2026 19:28:26 +0800 Subject: [PATCH 003/185] gh-148613: Fix race in `gc_set_threshold` and `gc_get_threshold` (#150356) --- Lib/test/test_free_threading/test_gc.py | 30 +++++++++++++++++++ ...-05-24-22-46-49.gh-issue-148613.PLpmyd.rst | 2 ++ Modules/gcmodule.c | 3 ++ 3 files changed, 35 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-05-24-22-46-49.gh-issue-148613.PLpmyd.rst diff --git a/Lib/test/test_free_threading/test_gc.py b/Lib/test/test_free_threading/test_gc.py index 8b45b6e2150c288..cc1888dae48bc03 100644 --- a/Lib/test/test_free_threading/test_gc.py +++ b/Lib/test/test_free_threading/test_gc.py @@ -94,6 +94,36 @@ def evil(): thread.start() thread.join() + def test_set_threshold(self): + # GH-148613: Setting the GC threshold from another thread could cause a + # race between the `gc_should_collect` and `gc_set_threshold` functions. + NUM_THREADS = 8 + NUM_ITERS = 100_000 + barrier = threading.Barrier(NUM_THREADS) + + class CyclicReference: + def __init__(self): + self.r = self + + def allocator(): + barrier.wait() + for _ in range(NUM_ITERS): + CyclicReference() + + def setter(): + barrier.wait() + for i in range(NUM_ITERS): + gc.set_threshold(100 + (i % 100), 10 + (i % 10), 10 + (i % 10)) + + current_threshold = gc.get_threshold() + try: + threads = [Thread(target=allocator) for _ in range(NUM_THREADS - 1)] + threads.append(Thread(target=setter)) + with threading_helper.start_threads(threads): + pass + finally: + gc.set_threshold(*current_threshold) + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-05-24-22-46-49.gh-issue-148613.PLpmyd.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-24-22-46-49.gh-issue-148613.PLpmyd.rst new file mode 100644 index 000000000000000..71a701bf3eb3551 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-24-22-46-49.gh-issue-148613.PLpmyd.rst @@ -0,0 +1,2 @@ +Fix a data race in the free-threaded build between :func:`gc.set_threshold` +and garbage collection scheduling during object allocation. diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index 12f93ac0fdea14b..8762e592b258104 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -167,6 +167,8 @@ gc_set_threshold_impl(PyObject *module, int threshold0, int group_right_1, gcstate->generations[2].threshold = threshold2; } #else + PyInterpreterState *interp = _PyInterpreterState_GET(); + _PyEval_StopTheWorld(interp); gcstate->young.threshold = threshold0; if (group_right_1) { gcstate->old[0].threshold = threshold1; @@ -174,6 +176,7 @@ gc_set_threshold_impl(PyObject *module, int threshold0, int group_right_1, if (group_right_2) { gcstate->old[1].threshold = threshold2; } + _PyEval_StartTheWorld(interp); #endif Py_RETURN_NONE; } From 7c4b466ac42c1f6c866f9ae5f362a19c4c9e0bbc Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 3 Jun 2026 14:02:36 +0200 Subject: [PATCH 004/185] gh-146636: Add Free-threaded Stable ABI migration guide (GH-150580) Co-authored-by: Charlie Lin Co-authored-by: da-woods Co-authored-by: Stan Ulbrych Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- Doc/bugs.rst | 3 + Doc/howto/abi3t-migration.rst | 614 ++++++++++++++++++++++++++ Doc/howto/index.rst | 2 + Doc/tools/extensions/c_annotations.py | 2 +- Doc/whatsnew/3.15.rst | 7 +- 5 files changed, 623 insertions(+), 5 deletions(-) create mode 100644 Doc/howto/abi3t-migration.rst diff --git a/Doc/bugs.rst b/Doc/bugs.rst index 254a22f2622bd8e..a6ea0a72e76f9db 100644 --- a/Doc/bugs.rst +++ b/Doc/bugs.rst @@ -12,6 +12,9 @@ It can be sometimes faster to fix bugs yourself and contribute patches to Python as it streamlines the process and involves fewer people. Learn how to :ref:`contribute `. + +.. _reporting-documentation-bugs: + Documentation bugs ================== diff --git a/Doc/howto/abi3t-migration.rst b/Doc/howto/abi3t-migration.rst new file mode 100644 index 000000000000000..ed7a324c4af6f0a --- /dev/null +++ b/Doc/howto/abi3t-migration.rst @@ -0,0 +1,614 @@ +.. highlight:: c + +.. _abi3t-migration-howto: + +****************************************************** +Migrating to Stable ABI for free threading (``abi3t``) +****************************************************** + +Starting with the 3.15 release, CPython supports a variant of the Stable ABI +that supports :term:`free-threaded ` Python: +Stable ABI for Free-Threaded Builds, or ``abi3t`` for short. +This document describes how to adapt C API extensions to support free threading. + +Why do this +=========== + +The typical reason to use Stable ABI is to reduce the number of artifacts that +you need to build and distribute for each version of your library. + +Without the Stable ABI, you must build a separate shared library, and typically +a *wheel* distribution, for each feature version of CPython you wish +to support. +For example, each tag in the following table represents a separate +library/wheel: + ++-----------------+-----------------------+------------------------+ +| CPython version | Non-free-threaded | Free-threaded | ++=================+=======================+========================+ +| 3.12 | ``cpython-312`` | --- | ++-----------------+-----------------------+------------------------+ +| 3.13 | ``cpython-313`` | ``cpython-313t`` | ++-----------------+-----------------------+------------------------+ +| 3.14 | ``cpython-314`` | ``cpython-314t`` | ++-----------------+-----------------------+------------------------+ +| 3.15 | ``cpython-315`` | ``cpython-315t`` | ++-----------------+-----------------------+------------------------+ +| 3.16 | ``cpython-316`` | ``cpython-316t`` | ++-----------------+-----------------------+------------------------+ +| Later versions | :samp:`cpython-3{XX}` | :samp:`cpython-3{XX}t` | ++-----------------+-----------------------+------------------------+ + +That's a lot of builds, especially when multiplied by the number +of supported platforms. + +With the Stable ABI (``abi3``, introduced in CPython 3.2), a single extension +(per platform) can cover all *non-free-threaded* builds of CPython: + ++-----------------+-------------------+------------------------+ +| CPython version | Non-free-threaded | Free-threaded | ++=================+===================+========================+ +| 3.12 | ``abi3`` | --- | ++-----------------+ +------------------------+ +| 3.13 | | ``cpython-313t`` | ++-----------------+ +------------------------+ +| 3.14 | | ``cpython-314t`` | ++-----------------+ +------------------------+ +| 3.15 | | ``cpython-315t`` | ++-----------------+ +------------------------+ +| 3.16 | | ``cpython-316t`` | ++-----------------+ +------------------------+ +| Later versions | | :samp:`cpython-3{XX}t` | ++-----------------+-------------------+------------------------+ + +The Stable ABI for free-threaded builds (``abi3t``), introduced in +CPython 3.15, does the same for free-threaded builds. +And it's compatible with non-free-threaded ones as well: + ++-----------------+-------------------+------------------+ +| CPython version | Non-free-threaded | Free-threaded | ++=================+===================+==================+ +| 3.12 | ``abi3`` * | --- | ++-----------------+ +------------------+ +| 3.13 | | ``cpython-313t`` | ++-----------------+ +------------------+ +| 3.14 | | ``cpython-314t`` | ++-----------------+-------------------+------------------+ +| 3.15 | ``abi3t`` | ++-----------------+ + +| 3.16 | | ++-----------------+ + +| Later versions | | ++-----------------+-------------------+------------------+ + +\* (As above, the ``abi3`` extension is compatible with all non-free-threaded +builds; even the 3.15+ ones that this table "attributes" to ``abi3t``.) + +Why *not* do this +----------------- + +There are two main downsides to Stable ABI. + +First, you extension may become slower, since Stable ABI prioritizes +compatibility over performance. +The difference is usually not noticeable, and often can be mitigated by +using the same source to build both a Stable ABI build and a few +version-specific ones for "tier 1" CPython versions. + +Second, not all of the C API is available. +Extensions need to be ported to build for Stable ABI, which may be difficult +or, in rare cases, impossible. + +Specifically, ``abi3t`` requires APIs added in CPython 3.15. +If you want to build your extension for older versions of CPython from the +same source, you have two main options: + +- Use preprocessor conditionals. + + When following this guide, use ``#ifdef Py_TARGET_ABI3T`` blocks whenever + you are told to do a change that breaks the build on CPython versions you + care about. Keep the pre-existing code in ``#else`` blocks. + + For hand-written C extensions, this approach is reasonable down to + CPython 3.12, due to additions introduced in :pep:`697`. + Keeping compatibility with 3.11 and below may be worth it for code + generators (for example, Cython). + +- Do not port to ``abi3t``, and continue building separate extensions for + each version of CPython, until you can drop support for the older versions. + + This is a valid approach. Not all extensions need to switch to ``abi3t`` + right now. + + +Prerequisites +============= + +This guide assumes that you have an extension written directly in C (or C++), +which you want to port to ``abi3t``. + +If your extenstion uses a code generator (like Cython) or language binding +(like PyO3), it's best to wait until that tool has support for ``abi3t``. +If you maintain such a tool, you might be able to adapt the instructions +here for your tool. + +Non-free-threaded Stable ABI +---------------------------- + +Your extension should support the Stable ABI (``abi3t``). +If not, either port it first, or follow this guide but be prepared to fix +issues it does not mention. + +Free-threading support +---------------------- + +While it's technically not a hard prerequisite, you will most likely want to +prepare your extension for free threading before you port it to ``abi3t``. +See :ref:`freethreading-extensions-howto` for instructions. + +.. seealso:: + + `Porting Extension Modules to Support Free-Threading + `__: + A community-maintained porting guide for extension authors. + +Isolating extension modules +--------------------------- + +Your module should use :ref:`multi-phase initialization `, +and it should either be isolated or limit itself to be loaded at most once +per process. +If it is not your case, follow :ref:`isolating-extensions-howto` first. +(See the :ref:`opt-out section ` for a shortcut.) + +Avoiding variable-sized types +----------------------------- + +If your extension defines variable-sized types (using :c:macro:`Py_tp_itemsize` +or :c:member:`PyTypeObject.tp_itemsize`), it cannot be ported to +``abi3t`` 3.15. + + +Setting up the build +==================== + +If you use a build tool (such as setuptools, meson-python, scikit-build-core), +search its documentation for a way to select ``abi3t``. +At the time of writing, not all of them have this; but if your tool does, +use it. +You may want to verify that it set the right flag by temporarily adding the +following just after ``#include ``:: + + #if Py_TARGET_ABI3T+0 <= 0x30f0000 + #error "abi3t define is not set!" + #endif + +This should result in a different error than "``abt3t`` define is not set". + +.. note:: + + If your build tool doesn't support ``abi3t`` yet, set the following macro + before including ``Python.h``:: + + #define Py_TARGET_ABI3T 0x30f0000 + + or specify it as a compiler flag, for example:: + + -DPy_TARGET_ABI3T=0x30f0000 + + Once your extension builds with this setting, it will be compatible with + CPython 3.15 and above. + + If you set this macro manually, you will later need to name and tag the + resulting extension manually as well. + This is covered in :ref:`abi3t-migration-tagging` below. + +This guide will ask you to make a series of changes. +After each one, verify that your extension still builds in the original +(non-``abi3t``) configuration, and ideally run tests on all Python +versions you support. +This will ensure that nothing breaks as you are porting. + + +Module export hook +================== + +Unless you've done this step already, your extension module defines a +:ref:`module initialization function ` +named :samp:`PyInit_{}`. +You will need to port it to a :ref:`module export hook `, +:samp:`PyModExport_{}`, a feature added in CPython 3.15 in +:pep:`793`. + +Your existing init function should look like this (with your own names +for ```` and ````): + +.. code-block:: + :class: bad + + PyMODINIT_FUNC + PyInit_(void) + { + return PyModuleDef_Init(&); + } + +If there is some code before the ``return``, move it to +a :c:macro:`Py_mod_create` or :c:macro:`Py_mod_exec` slot function. +See the :ref:`PyInit documentation ` for related information. + +The function references a ``PyModuleDef`` object (```` in the code +above). +Its definition should be similar to the following, with different values +and perhaps some fields unnnamed or left out: + +.. code-block:: + :class: bad + + static PyModuleDef = { + PyModuleDef_HEAD_INIT, + .m_name = "my_module", + .m_doc = "my docstring", + .m_size = sizeof(my_state_struct), + .m_methods = my_methods, + .m_slots = my_slots, + .m_traverse = my_traverse, + .m_clear = my_clear, + .m_free = my_free, + }; + +Remove this definition and the ``PyInit`` function (or put them in +an ``#ifndef Py_TARGET_ABI3T`` block, to retain backwards compatibility), +and replace them with the following: + +.. code-block:: + :class: good + + PyABIInfo_VAR(abi_info); + + static PySlot my_slot_array[] = { + PySlot_STATIC_DATA(Py_mod_abi, &abi_info), + PySlot_STATIC_DATA(Py_mod_name, "my_module"), + PySlot_STATIC_DATA(Py_mod_doc, "my docstring"), + PySlot_SIZE(Py_mod_state_size, sizeof(my_state_struct)), + PySlot_STATIC_DATA(Py_mod_methods, my_methods), + PySlot_STATIC_DATA(Py_mod_slots, my_slots), + PySlot_FUNC(Py_mod_state_traverse, my_traverse), + PySlot_FUNC(Py_mod_state_clear, my_clear), + PySlot_FUNC(Py_mod_state_free, my_free), + PySlot_END + }; + + PyMODEXPORT_FUNC + PyModExport_(void) + { + return my_slot_array; + } + +Leave out any fields that were missing (except the new :c:macro:`Py_mod_abi`), +and substitute your own values. + +See the :c:type:`PySlot` and :c:ref:`export hook ` +documentation for details on this API. + +Associated ``PyModuleDef`` +-------------------------- + +Since the new API does not use a :c:type:`!PyModuleDef` structure, a definition +will not be associated with the resulting module. +This changes the behavior of the following functions: + +- :c:func:`PyModule_GetDef` +- :c:func:`PyType_GetModuleByDef` + +Check your code for these. +If you do not use them, you can skip this section. + +These functions are typically used for two purposes: + +1. To get the definition the module was created with. + This is no longer possible using the new API. + Modules no longer keep a reference to the definition, so you will need to + figure out a different way to pass the relevant data around. + +.. _abi3t-migration-module-token: + +2. To check if a given module object is “yours”. + This use case is now served by :ref:`module tokens ` -- + opaque pointers that identify a module. + To use a token, declare (or reuse) a unique static variable, for example: + + .. code-block:: + :class: good + + static char my_token; + + and add a pointer to it in a new entry to your module's ``PySlot`` array: + + .. code-block:: + :class: good + :emphasize-lines: 3 + + static PySlot my_slot_array[] = { + ... + PySlot_STATIC_DATA(Py_mod_token, &my_token), + PySlot_END + } + + Then, switch from :c:func:`PyModule_GetDef` calls such as: + + .. code-block:: + :class: bad + + PyModuleDef *def = PyModule_GetDef(module); + + to :c:func:`PyModule_GetToken` (which uses an output argument and may fail + with an exception): + + .. code-block:: + :class: good + + void *token; + if (PyModule_GetToken(module, &token) < 0) { + /* handle error */ + } + + and from :c:func:`PyType_GetModuleByDef` calls such as: + + .. code-block:: + :class: bad + + PyObject *module = PyType_GetModuleByDef(type, my_def); + /* handle error; use module */ + + to :c:func:`PyType_GetModuleByToken` (which returns a strong reference): + + .. code-block:: + :class: good + + PyObject *module = PyType_GetModuleByToken(type, my_token); + /* handle error; use module */ + Py_XDECREF(module); + +``PyObject`` opaqueness +======================= + +The :c:type:`PyObject` and :c:type:`PyVarObject` structures are opaque +in ``abi3t``. + +Accessing their members is prohibited. +If you do this, switch to getter/setter functions mentioned in +their documentation: + +- :c:member:`PyObject.ob_type` +- :c:member:`PyObject.ob_refcnt` +- :c:member:`PyVarObject.ob_size` + +Also, the *size* of the :c:type:`PyObject` structures is +unknown to the compiler. +It can -- and *does* -- change between different CPython builds. + +.. note:: + + While the size is available at runtime (for example as + ``sys.getsizeof(object())`` in Python code), you should resist the + temptation to calculate pointer offsets from it. + The object memory layout is subject to change in future + ``abi3t`` implementations. + + +Custom type definitions +----------------------- + +Since :c:type:`!PyObject` is opaque, the traditional way of defining +custom types no longer works: + +.. code-block:: + :class: bad + + typedef struct { + PyObject_HEAD // expands to `PyObject ob_base;` which has unknown size + + int my_data; + } CustomObject; + + static PyType_Spec CustomType_spec = { + ... + .basicsize = sizeof(CustomObject), + ... + }; + +Most likely, all your class definitions, *and* all code that accesses +your classes' data, will need to be rewritten. +This will probably be the biggest change you need to support ``abi3t``. + +For each such type, instead of defining a ``struct`` for the entire instance, +define one with only the “additional” fields -- ones specific to your class, +not its superclasses: + +.. code-block:: + :class: good + + typedef struct { + int my_data; + } CustomObjectData; + +Change the name. +Almost all code that uses the struct will need to change +(notably, pointers to the new structure cannot be cast to/from ``PyObject*``), +and changing the name will highlight the usages as compiler errors. +(If you use ``typeof``, C++ ``auto``, or similar ways to avoid +typing the type name, this won't work. Be extra careful, and consider running +tools to detect undefined behavior.) + +Then, to create the class, use *negative* ``basicsize`` to indicate +“extra” storage space rather than *total* instance size: + +.. code-block:: + :class: good + + static PyType_Spec CustomType_spec = { + ... + .basicsize = -sizeof(CustomObjectData), /* note the minus sign */ + ... + }; + +If you use :c:macro:`Py_tp_members`, set the :c:macro:`Py_RELATIVE_OFFSET` +flag on each member and specify the :c:member:`~PyMemberDef.offset` +relative to your new struct. + + +Custom type data access +----------------------- + +Then comes the hard part: in all code that needs to access this struct, +you will need an additional :c:func:`PyObject_GetTypeData` call to +retrieve a ``CustomObjectData *`` pointer from ``PyObject *``: + +.. code-block:: + :class: good + + PyObject *obj = ...; + CustomObjectData *data = PyObject_GetTypeData(obj, cls); + +Note that this call requires the *type object* for your class (``cls``). + +If your class is not subclassable (that is, it does not use the +:c:macro:`Py_TPFLAGS_BASETYPE` flag), ``cls`` will be ``Py_TYPE(obj)``. +Otherwise, **DO NOT USE** ``Py_TYPE`` with :c:func:`!PyObject_GetTypeData`: +it might return memory reserved to an unrelated subclass! +For example, if a user makes a subclass like this: + +.. code-block:: python + + class Sub(YourCustomClass): + __slots__ = ('a', 'b') + +then ``Py_TYPE(obj)`` is ``YourCustomClass``, and the underlying memory may +look like this: + +.. code-block:: text + + ╭─ PyObject *obj + │ ╭─ the pointer you want + │ │ ╭─ PyObject_GetTypeData(obj, Py_TYPE(obj)) + ▼ ▼ ▼ + ┌──────────┬───┬────────────────┬───┬─────────────┬───┬─────────────┐ + │ PyObject │...│ CustomTypeData │...│ PyObject *a │...│ PyObject *b │ + └──────────┴───┴────────────────┴───┴─────────────┴───┴─────────────┘ + +(Ellipses indicate possible padding. +Note that this memory layout is not guaranteed: future versions of Python may +add different padding or even switch the order of the structures.) + +There are two main ways to get the right class: + +- In instance methods, your implementation may use the :c:type:`PyCMethod` + signature (and the :c:macro:`METH_METHOD` bit in + :c:member:`PyMethodDef.ml_flags`), + and get the class as the ``defining_class`` argument. +- Otherwise, give your class a unique static token using the + :c:macro:`Py_tp_token` slot, and use: + + .. code-block:: + :class: good + + PyTypeObject cls; + if (PyType_GetBaseByToken(Py_TYPE(obj), my_tp_token, &cls) < 0) { + /* handle error */ + } + CustomObjectData *data = PyObject_GetTypeData(obj, cls); + + Type tokens work similarly to module tokens covered :ref:`earlier in this + guide `. + + + +Avoid build-time conditionals +============================= + +Check your code for API that identifies the version of Python used to +*build* your extension. +This no longer corresponds to the Python your extension runs on, so code +that uses this information often needs changing. +The macros to check for are: + +- :c:macro:`PY_VERSION_HEX`, :c:macro:`PY_MAJOR_VERSION`, + :c:macro:`PY_MINOR_VERSION`: + + - to get the run-time version, use :c:data:`Py_Version`; + - to determine what C API is available, use :c:macro:`Py_TARGET_ABI3T`. + This macro is set to the minimum supported version. + +- :c:macro:`Py_GIL_DISABLED`: under ``abi3t``, this macro is always defined. + Code that works with free-threaded Python *should* also work with + the GIL enabled (since the GIL can be enabled at run time), + and usually *does* (unless it, for some reason, requires more than one + :term:`attached thread state ` at one time). + + +Further code changes +==================== + +If you are still left with compiler errors or warnings, find a way to fix them. +Alas, this guide is limited, and cannot cover all possible code +changes extensions may need. + +If you find a problem that other extension authors might run into, +consider :ref:`reporting an issue ` (or sending +a pull request) for this guide. + +It is possible your issue cannot be fixed for the current version of ``abi3t``. +In that case, reporting it may help it get prioritized for the next version +of CPython. + + +.. _abi3t-migration-tagging: + +Tagging and distribution +======================== + +If you are using a build tool with ``abi3t`` support, your extension is ready, +but you might want to check that it was built correctly. + +Extensions built with ``abi3t`` should have the following extension: + +- On Windows: ``.pyd`` (like any other extension); +- Linux, macOS, and other systems that use the ``.so`` suffix: ``.abi3t.so`` + (**not** ``.cpython-315t.so`` or ``.abi3.so``). + Note that both free-threaded and non-free-threaded builds will + load ``.abi3t.so`` extensions; +- Other systems: consult your distributor, and perhaps update this guide. + +If you distribute the extension as a *wheel*, use the following tags: + +* Python tag: :samp:`cp3{XX}`, where *XX* is the minimum Python version + the extension is built for. + (For example, ``cp315`` if you set ``Py_TARGET_ABI3T`` to ``0x30f0000``. + See :ref:`abi3-compiling` for more values.) +* ABI tag: ``abi3.abi3t``. This is a *compressed tag set* that indicates + support for both non-free-threaded and free-threaded builds. + +For example, the wheel filename may look like this: + +.. code-block:: text + + myproject-1.0-cp315-abi3.abi3t-macosx_11_0_arm64.whl + +.. seealso:: `Platform Compatibility Tags `__ in the PyPA package distribution metadata. + +If the filename or tags are incorrect, fix them. + + +Testing +======= + +Note that when you build an extension compatible with multiple versions of +CPython, you should always *test* it with each version it supports (for +example, 3.15, 3.16, and so on). +Stable ABI only guarantees *ABI* compatibility; there may also be behavior +changes -- both intentional ones (covered by :pep:`387`) and bugs. + +Be sure to run tests on both free-threaded and non-free-threaded builds +of CPython. + +If they pass, congratulations! You have an ``abi3t`` extension. diff --git a/Doc/howto/index.rst b/Doc/howto/index.rst index 81fc7e63f35bd79..57e2d6e0752447f 100644 --- a/Doc/howto/index.rst +++ b/Doc/howto/index.rst @@ -37,6 +37,7 @@ Python Library Reference. mro.rst free-threading-python.rst free-threading-extensions.rst + abi3t-migration.rst remote_debugging.rst General: @@ -61,6 +62,7 @@ Advanced development: * :ref:`freethreading-python-howto` * :ref:`freethreading-extensions-howto` * :ref:`isolating-extensions-howto` +* :ref:`abi3t-migration-howto` * :ref:`python_2.3_mro` * :ref:`socket-howto` * :ref:`timerfd-howto` diff --git a/Doc/tools/extensions/c_annotations.py b/Doc/tools/extensions/c_annotations.py index 1409c77aed9c6bc..0e762042979c2b7 100644 --- a/Doc/tools/extensions/c_annotations.py +++ b/Doc/tools/extensions/c_annotations.py @@ -394,7 +394,7 @@ def run(self) -> list[nodes.Node]: current_minor = int(self.config.version.removeprefix('3.')) for minor in range(current_minor - 5, current_minor + 1): value = (3 << 24) | (minor << 16) - content.append(f' {value:#x} /* Py_PACK_VERSION(3.{minor}) */') + content.append(f' {value:#x} /* Py_PACK_VERSION(3,{minor}) */') node = nodes.paragraph() self.state.nested_parse(StringList(content), 0, node) return [node] diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 0f76c66144d6899..ccfe0f59cb5615c 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -513,10 +513,6 @@ specifically: purpose in :pep:`793`, with a new :c:type:`PySlot` structure introduced in :pep:`820`. -The reference documentation for these features is complete, but currently -aimed at early adopters. -A migration guide is planned for an upcoming beta release. - Note that Stable ABI does not offer all the functionality that CPython has to offer. Extensions that cannot switch to ``abi3t`` should continue to build for @@ -532,6 +528,9 @@ If not using a build tool -- or when writing such a tool -- you can select ``abi3t`` by setting the macro :c:macro:`!Py_TARGET_ABI3T` as discussed in :ref:`abi3-compiling`. +A practical :ref:`migration guide ` for switching to +``abi3t`` is available. + .. seealso:: :pep:`803` for further details. From d502edf1cee04f5077156331b8a96fa629b2af19 Mon Sep 17 00:00:00 2001 From: Robsdedude Date: Wed, 3 Jun 2026 14:18:06 +0200 Subject: [PATCH 005/185] Fix a typo in `SSLSocket` docs (GH-150839) --- Doc/library/ssl.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index c0f3757e583e95d..41a101e84ac4d75 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -1121,7 +1121,7 @@ SSL sockets :meth:`SSLContext.wrap_socket` to wrap a socket. .. versionchanged:: 3.7 - :class:`SSLSocket` instances must to created with + :class:`SSLSocket` instances must be created with :meth:`~SSLContext.wrap_socket`. In earlier versions, it was possible to create instances directly. This was never documented or officially supported. From c605ffe39e64b3a99d7050ae1515881ccf7baf6f Mon Sep 17 00:00:00 2001 From: Yashraj Date: Wed, 3 Jun 2026 18:06:25 +0530 Subject: [PATCH 006/185] gh-141004: Document unstable perf map functions in `ceval.h` (GH-143492) --- Doc/c-api/perfmaps.rst | 40 ++++++++++++++++++++++++ Tools/check-c-api-docs/ignored_c_api.txt | 4 --- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/Doc/c-api/perfmaps.rst b/Doc/c-api/perfmaps.rst index bd05e628faaaa10..a962c4ee09ad77d 100644 --- a/Doc/c-api/perfmaps.rst +++ b/Doc/c-api/perfmaps.rst @@ -49,3 +49,43 @@ Note that holding an :term:`attached thread state` is not required for these API This is called by the runtime itself during interpreter shut-down. In general, there shouldn't be a reason to explicitly call this, except to handle specific scenarios such as forking. + +.. c:function:: int PyUnstable_CopyPerfMapFile(const char *parent_filename) + + Open the ``/tmp/perf-$pid.map`` file and append the content of *parent_filename* + to it. + + This function is available on all platforms but only generates output on platforms + that support perf maps (currently only Linux). On other platforms, it does nothing. + + .. versionadded:: 3.13 + +.. c:function:: int PyUnstable_PerfTrampoline_CompileCode(PyCodeObject *code) + + Compile the given code object using the current perf trampoline. + + The "current" trampoline is the one set by the runtime or the most recent + :c:func:`PyUnstable_PerfTrampoline_SetPersistAfterFork` call. + + If no trampoline is set, falls back to normal compilation (no perf map entry). + + :param code: The code object to compile. + :return: 0 on success, -1 on failure. + + .. versionadded:: 3.13 + +.. c:function:: int PyUnstable_PerfTrampoline_SetPersistAfterFork(int enable) + + Set whether the perf trampoline should persist after a fork. + + * If ``enable`` is true (non-zero): perf map file remains open/valid post-fork. + Child process inherits all existing perf map entries. + * If ``enable`` is false (zero): perf map closes post-fork. + Child process gets empty perf map. + + Default: false (clears on fork). + + :param enable: 1 to enable, 0 to disable. + :return: 0 on success, -1 on failure. + + .. versionadded:: 3.13 diff --git a/Tools/check-c-api-docs/ignored_c_api.txt b/Tools/check-c-api-docs/ignored_c_api.txt index dfec0524cfe016d..fa53b205c4ff6af 100644 --- a/Tools/check-c-api-docs/ignored_c_api.txt +++ b/Tools/check-c-api-docs/ignored_c_api.txt @@ -43,10 +43,6 @@ PyDescr_TYPE PyWrapperFlag_KEYWORDS # cpython/fileobject.h Py_UniversalNewlineFgets -# cpython/ceval.h -PyUnstable_CopyPerfMapFile -PyUnstable_PerfTrampoline_CompileCode -PyUnstable_PerfTrampoline_SetPersistAfterFork # cpython/pyframe.h PyUnstable_EXECUTABLE_KINDS PyUnstable_EXECUTABLE_KIND_BUILTIN_FUNCTION From 2872c95e1335d9ca81c59e947848b908d035bdf7 Mon Sep 17 00:00:00 2001 From: Victorien <65306057+Viicos@users.noreply.github.com> Date: Wed, 3 Jun 2026 15:14:47 +0200 Subject: [PATCH 007/185] gh-105812: Make use of the Sphinx `deco` role in documentation (#139598) --- Doc/c-api/exceptions.rst | 2 +- Doc/c-api/structures.rst | 6 ++-- Doc/deprecations/pending-removal-in-3.15.rst | 2 +- Doc/faq/programming.rst | 2 +- Doc/glossary.rst | 4 +-- Doc/howto/annotations.rst | 2 +- Doc/howto/descriptor.rst | 12 +++---- Doc/howto/enum.rst | 4 +-- Doc/howto/sorting.rst | 2 +- Doc/library/abc.rst | 38 ++++++++++---------- Doc/library/bisect.rst | 2 +- Doc/library/collections.rst | 2 +- Doc/library/contextlib.rst | 18 +++++----- Doc/library/ctypes.rst | 2 +- Doc/library/enum.rst | 14 ++++---- Doc/library/fnmatch.rst | 2 +- Doc/library/functions.rst | 4 +-- Doc/library/functools.rst | 18 +++++----- Doc/library/tarfile.rst | 2 +- Doc/library/test.rst | 2 +- Doc/library/typing.rst | 12 +++---- Doc/library/unittest.rst | 14 ++++---- Doc/reference/compound_stmts.rst | 2 +- Doc/reference/datamodel.rst | 4 +-- Doc/reference/expressions.rst | 2 +- Doc/reference/simple_stmts.rst | 2 +- Doc/whatsnew/2.5.rst | 2 +- Doc/whatsnew/2.6.rst | 7 ++-- Doc/whatsnew/2.7.rst | 2 +- Doc/whatsnew/3.10.rst | 6 ++-- Doc/whatsnew/3.11.rst | 12 +++---- Doc/whatsnew/3.12.rst | 8 ++--- Doc/whatsnew/3.13.rst | 2 +- Doc/whatsnew/3.15.rst | 2 +- Doc/whatsnew/3.2.rst | 20 +++++------ Doc/whatsnew/3.3.rst | 26 +++++++------- Doc/whatsnew/3.4.rst | 8 ++--- Doc/whatsnew/3.5.rst | 6 ++-- Doc/whatsnew/3.7.rst | 6 ++-- Doc/whatsnew/3.8.rst | 10 +++--- 40 files changed, 147 insertions(+), 146 deletions(-) diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst index 40b75ad34efc3bb..841754e60881a83 100644 --- a/Doc/c-api/exceptions.rst +++ b/Doc/c-api/exceptions.rst @@ -1035,7 +1035,7 @@ Properly implementing :c:member:`~PyTypeObject.tp_repr` for container types requ special recursion handling. In addition to protecting the stack, :c:member:`~PyTypeObject.tp_repr` also needs to track objects to prevent cycles. The following two functions facilitate this functionality. Effectively, -these are the C equivalent to :func:`reprlib.recursive_repr`. +these are the C equivalent to :deco:`reprlib.recursive_repr`. .. c:function:: int Py_ReprEnter(PyObject *object) diff --git a/Doc/c-api/structures.rst b/Doc/c-api/structures.rst index aeca412610317fe..1ddde5d136b3bb1 100644 --- a/Doc/c-api/structures.rst +++ b/Doc/c-api/structures.rst @@ -454,8 +454,8 @@ method. The method will be passed the type object as the first parameter rather than an instance of the type. This is used to create *class methods*, - similar to what is created when using the :func:`classmethod` built-in - function. + similar to what is created when using the :deco:`classmethod` built-in + decorator. .. c:macro:: METH_STATIC @@ -464,7 +464,7 @@ method. The method will be passed ``NULL`` as the first parameter rather than an instance of the type. This is used to create *static methods*, similar to - what is created when using the :func:`staticmethod` built-in function. + what is created when using the :deco:`staticmethod` built-in decorator. One other constant controls whether a method is loaded in place of another definition with the same method name. diff --git a/Doc/deprecations/pending-removal-in-3.15.rst b/Doc/deprecations/pending-removal-in-3.15.rst index 1d9a3095813a6de..ac4014da52b9a48 100644 --- a/Doc/deprecations/pending-removal-in-3.15.rst +++ b/Doc/deprecations/pending-removal-in-3.15.rst @@ -82,7 +82,7 @@ Pending removal in Python 3.15 Use ``class TD(TypedDict): pass`` or ``TD = TypedDict("TD", {})`` to create a TypedDict with zero field. - * The :func:`!typing.no_type_check_decorator` decorator function + * The :deco:`!typing.no_type_check_decorator` decorator function has been deprecated since Python 3.13. After eight years in the :mod:`typing` module, it has yet to be supported by any major type checker. diff --git a/Doc/faq/programming.rst b/Doc/faq/programming.rst index 591565cbc013575..c2f8f72ee1f2c4b 100644 --- a/Doc/faq/programming.rst +++ b/Doc/faq/programming.rst @@ -2003,7 +2003,7 @@ How do I cache method calls? ---------------------------- The two principal tools for caching methods are -:func:`functools.cached_property` and :func:`functools.lru_cache`. The +:deco:`functools.cached_property` and :deco:`functools.lru_cache`. The former stores results at the instance level and the latter at the class level. diff --git a/Doc/glossary.rst b/Doc/glossary.rst index 56bc799d945e7b0..3ac622709241717 100644 --- a/Doc/glossary.rst +++ b/Doc/glossary.rst @@ -413,7 +413,7 @@ Glossary decorator A function returning another function, usually applied as a function transformation using the ``@wrapper`` syntax. Common examples for - decorators are :func:`classmethod` and :func:`staticmethod`. + decorators are :deco:`classmethod` and :deco:`staticmethod`. The decorator syntax is merely syntactic sugar, the following two function definitions are semantically equivalent:: @@ -676,7 +676,7 @@ Glossary determined by the dispatch algorithm. See also the :term:`single dispatch` glossary entry, the - :func:`functools.singledispatch` decorator, and :pep:`443`. + :deco:`functools.singledispatch` decorator, and :pep:`443`. generic type A :term:`type` that can be parameterized; typically a diff --git a/Doc/howto/annotations.rst b/Doc/howto/annotations.rst index d7deb6c6bc1768f..e2a5aa708d7b630 100644 --- a/Doc/howto/annotations.rst +++ b/Doc/howto/annotations.rst @@ -154,7 +154,7 @@ on an arbitrary object ``o``: as the ``globals``, and ``dict(vars(o))`` as the ``locals``, when calling :func:`eval`. * If ``o`` is a wrapped callable using :func:`functools.update_wrapper`, - :func:`functools.wraps`, or :func:`functools.partial`, iteratively + :deco:`functools.wraps`, or :func:`functools.partial`, iteratively unwrap it by accessing either ``o.__wrapped__`` or ``o.func`` as appropriate, until you have found the root unwrapped function. * If ``o`` is a callable (but not a class), use diff --git a/Doc/howto/descriptor.rst b/Doc/howto/descriptor.rst index a7a68281860cb5d..60dff850a61e2b1 100644 --- a/Doc/howto/descriptor.rst +++ b/Doc/howto/descriptor.rst @@ -28,7 +28,7 @@ This guide has four major sections: 4) The last section has pure Python equivalents for built-in descriptors that are written in C. Read this if you're curious about how functions turn into bound methods or about the implementation of common tools like - :func:`classmethod`, :func:`staticmethod`, :func:`property`, and + :deco:`classmethod`, :deco:`staticmethod`, :deco:`property`, and :term:`__slots__`. @@ -317,8 +317,8 @@ Descriptors invert that relationship and allow the data being looked-up to have a say in the matter. Descriptors are used throughout the language. It is how functions turn into -bound methods. Common tools like :func:`classmethod`, :func:`staticmethod`, -:func:`property`, and :func:`functools.cached_property` are all implemented as +bound methods. Common tools like :deco:`classmethod`, :deco:`staticmethod`, +:deco:`property`, and :deco:`functools.cached_property` are all implemented as descriptors. @@ -1326,7 +1326,7 @@ example calls are unexciting: 30 Using the non-data descriptor protocol, a pure Python version of -:func:`staticmethod` would look like this: +:deco:`staticmethod` would look like this: .. testcode:: @@ -1466,7 +1466,7 @@ Now a new dictionary of unique keys can be constructed like this: {'a': None, 'b': None, 'r': None, 'c': None, 'd': None} Using the non-data descriptor protocol, a pure Python version of -:func:`classmethod` would look like this: +:deco:`classmethod` would look like this: .. testcode:: @@ -1604,7 +1604,7 @@ matters when a large number of instances are going to be created. 4. Improves speed. Reading instance variables is 35% faster with ``__slots__`` (as measured with Python 3.10 on an Apple M1 processor). -5. Blocks tools like :func:`functools.cached_property` which require an +5. Blocks tools like :deco:`functools.cached_property` which require an instance dictionary to function correctly: .. testcode:: diff --git a/Doc/howto/enum.rst b/Doc/howto/enum.rst index 2fe5814bb04a736..adb9dc9a4879eb5 100644 --- a/Doc/howto/enum.rst +++ b/Doc/howto/enum.rst @@ -256,7 +256,7 @@ Ensuring unique enumeration values ---------------------------------- By default, enumerations allow multiple names as aliases for the same value. -When this behavior isn't desired, you can use the :func:`unique` decorator:: +When this behavior isn't desired, you can use the :deco:`unique` decorator:: >>> from enum import Enum, unique >>> @unique @@ -509,7 +509,7 @@ to use the standard :func:`repr`. .. note:: - Adding :func:`~dataclasses.dataclass` decorator to :class:`Enum` + Adding :deco:`~dataclasses.dataclass` decorator to :class:`Enum` and its subclasses is not supported. It will not raise any errors, but it will produce very strange results at runtime, such as members being equal to each other:: diff --git a/Doc/howto/sorting.rst b/Doc/howto/sorting.rst index 70c34cde8a06592..65e862e13e817a1 100644 --- a/Doc/howto/sorting.rst +++ b/Doc/howto/sorting.rst @@ -375,7 +375,7 @@ Odds and Ends :meth:`~object.__lt__` is not implemented (see :func:`object.__lt__` for details on the mechanics). To avoid surprises, :pep:`8` recommends that all six comparison methods be implemented. - The :func:`~functools.total_ordering` decorator is provided to make that + The :deco:`~functools.total_ordering` decorator is provided to make that task easier. * Key functions need not depend directly on the objects being sorted. A key diff --git a/Doc/library/abc.rst b/Doc/library/abc.rst index 904eff2e31e6ac2..be25a94e7e94f6f 100644 --- a/Doc/library/abc.rst +++ b/Doc/library/abc.rst @@ -166,17 +166,17 @@ The :mod:`!abc` module also provides the following decorator: or is derived from it. A class that has a metaclass derived from :class:`!ABCMeta` cannot be instantiated unless all of its abstract methods and properties are overridden. The abstract methods can be called using any - of the normal 'super' call mechanisms. :func:`!abstractmethod` may be used + of the normal 'super' call mechanisms. :deco:`!abstractmethod` may be used to declare abstract methods for properties and descriptors. Dynamically adding abstract methods to a class, or attempting to modify the abstraction status of a method or class once it is created, are only supported using the :func:`update_abstractmethods` function. The - :func:`!abstractmethod` only affects subclasses derived using regular + :deco:`!abstractmethod` only affects subclasses derived using regular inheritance; "virtual subclasses" registered with the ABC's :meth:`~ABCMeta.register` method are not affected. - When :func:`!abstractmethod` is applied in combination with other method + When :deco:`!abstractmethod` is applied in combination with other method descriptors, it should be applied as the innermost decorator, as shown in the following usage examples:: @@ -214,7 +214,7 @@ The :mod:`!abc` module also provides the following decorator: the descriptor must identify itself as abstract using :attr:`!__isabstractmethod__`. In general, this attribute should be ``True`` if any of the methods used to compose the descriptor are abstract. For - example, Python's built-in :class:`property` does the equivalent of:: + example, Python's built-in :deco:`property` does the equivalent of:: class Descriptor: ... @@ -238,13 +238,13 @@ The :mod:`!abc` module also supports the following legacy decorators: .. versionadded:: 3.2 .. deprecated-removed:: 3.3 3.21 - It is now possible to use :class:`classmethod` with - :func:`abstractmethod`, making this decorator redundant. + It is now possible to use :deco:`classmethod` with + :deco:`abstractmethod`, making this decorator redundant. - A subclass of the built-in :func:`classmethod`, indicating an abstract - classmethod. Otherwise it is similar to :func:`abstractmethod`. + A subclass of the built-in :class:`classmethod`, indicating an abstract + classmethod. Otherwise it is similar to :deco:`abstractmethod`. - This special case is deprecated, as the :func:`classmethod` decorator + This special case is deprecated, as the :deco:`classmethod` decorator is now correctly identified as abstract when applied to an abstract method:: @@ -259,13 +259,13 @@ The :mod:`!abc` module also supports the following legacy decorators: .. versionadded:: 3.2 .. deprecated-removed:: 3.3 3.21 - It is now possible to use :class:`staticmethod` with - :func:`abstractmethod`, making this decorator redundant. + It is now possible to use :deco:`staticmethod` with + :deco:`abstractmethod`, making this decorator redundant. - A subclass of the built-in :func:`staticmethod`, indicating an abstract - staticmethod. Otherwise it is similar to :func:`abstractmethod`. + A subclass of the built-in :class:`staticmethod`, indicating an abstract + staticmethod. Otherwise it is similar to :deco:`abstractmethod`. - This special case is deprecated, as the :func:`staticmethod` decorator + This special case is deprecated, as the :deco:`staticmethod` decorator is now correctly identified as abstract when applied to an abstract method:: @@ -279,14 +279,14 @@ The :mod:`!abc` module also supports the following legacy decorators: .. decorator:: abstractproperty .. deprecated-removed:: 3.3 3.21 - It is now possible to use :class:`property`, :meth:`property.getter`, - :meth:`property.setter` and :meth:`property.deleter` with - :func:`abstractmethod`, making this decorator redundant. + It is now possible to use :deco:`property`, :deco:`property.getter`, + :deco:`property.setter` and :deco:`property.deleter` with + :deco:`abstractmethod`, making this decorator redundant. - A subclass of the built-in :func:`property`, indicating an abstract + A subclass of the built-in :class:`property`, indicating an abstract property. - This special case is deprecated, as the :func:`property` decorator + This special case is deprecated, as the :deco:`property` decorator is now correctly identified as abstract when applied to an abstract method:: diff --git a/Doc/library/bisect.rst b/Doc/library/bisect.rst index 2c29a5ec992737e..84c009907ed9a3c 100644 --- a/Doc/library/bisect.rst +++ b/Doc/library/bisect.rst @@ -129,7 +129,7 @@ thoughts in mind: they are used. Consequently, if the search functions are used in a loop, the key function may be called again and again on the same array elements. If the key function isn't fast, consider wrapping it with - :py:func:`functools.cache` to avoid duplicate computations. Alternatively, + :py:deco:`functools.cache` to avoid duplicate computations. Alternatively, consider searching an array of precomputed keys to locate the insertion point (as shown in the examples section below). diff --git a/Doc/library/collections.rst b/Doc/library/collections.rst index 25e4a71b03c6c85..bd04063232e527e 100644 --- a/Doc/library/collections.rst +++ b/Doc/library/collections.rst @@ -1228,7 +1228,7 @@ original insertion position is changed and moved to the end:: self.move_to_end(key) An :class:`OrderedDict` would also be useful for implementing -variants of :func:`functools.lru_cache`: +variants of :deco:`functools.lru_cache`: .. testcode:: diff --git a/Doc/library/contextlib.rst b/Doc/library/contextlib.rst index 02be00f575e725b..666f85997448881 100644 --- a/Doc/library/contextlib.rst +++ b/Doc/library/contextlib.rst @@ -88,11 +88,11 @@ Functions and classes provided: the exception has been handled, and execution will resume with the statement immediately following the :keyword:`!with` statement. - :func:`contextmanager` uses :class:`ContextDecorator` so the context managers + :deco:`contextmanager` uses :class:`ContextDecorator` so the context managers it creates can be used as decorators as well as in :keyword:`with` statements. When used as a decorator, a new generator instance is implicitly created on each function call (this allows the otherwise "one-shot" context managers - created by :func:`contextmanager` to meet the requirement that context + created by :deco:`contextmanager` to meet the requirement that context managers support multiple invocations in order to be used as decorators). .. versionchanged:: 3.2 @@ -101,7 +101,7 @@ Functions and classes provided: .. decorator:: asynccontextmanager - Similar to :func:`~contextlib.contextmanager`, but creates an + Similar to :deco:`~contextlib.contextmanager`, but creates an :ref:`asynchronous context manager `. This function is a :term:`decorator` that can be used to define a factory @@ -128,7 +128,7 @@ Functions and classes provided: .. versionadded:: 3.7 - Context managers defined with :func:`asynccontextmanager` can be used + Context managers defined with :deco:`asynccontextmanager` can be used either as decorators or with :keyword:`async with` statements:: import time @@ -148,11 +148,11 @@ Functions and classes provided: When used as a decorator, a new generator instance is implicitly created on each function call. This allows the otherwise "one-shot" context managers - created by :func:`asynccontextmanager` to meet the requirement that context + created by :deco:`asynccontextmanager` to meet the requirement that context managers support multiple invocations in order to be used as decorators. .. versionchanged:: 3.10 - Async context managers created with :func:`asynccontextmanager` can + Async context managers created with :deco:`asynccontextmanager` can be used as decorators. @@ -400,7 +400,7 @@ Functions and classes provided: ``__exit__`` retains its optional exception handling even when used as a decorator. - ``ContextDecorator`` is used by :func:`contextmanager`, so you get this + ``ContextDecorator`` is used by :deco:`contextmanager`, so you get this functionality automatically. Example of ``ContextDecorator``:: @@ -702,7 +702,7 @@ Functions and classes provided: Similar to :meth:`ExitStack.close` but properly handles awaitables. - Continuing the example for :func:`asynccontextmanager`:: + Continuing the example for :deco:`asynccontextmanager`:: async with AsyncExitStack() as stack: connections = [await stack.enter_async_context(get_connection()) @@ -960,7 +960,7 @@ Files are an example of effectively single use context managers, since the first :keyword:`with` statement will close the file, preventing any further IO operations using that file object. -Context managers created using :func:`contextmanager` are also single use +Context managers created using :deco:`contextmanager` are also single use context managers, and will complain about the underlying generator failing to yield if an attempt is made to use them a second time:: diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index d6e704825924d95..51f08fdc0544c96 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -551,7 +551,7 @@ object with an :attr:`!_as_parameter_` attribute:: >>> If you don't want to store the instance's data in the :attr:`!_as_parameter_` -instance variable, you could define a :class:`property` which makes the +instance variable, you could define a :deco:`property` which makes the attribute available on request. diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst index 04b41ad6eadf38c..65bb0b483350c52 100644 --- a/Doc/library/enum.rst +++ b/Doc/library/enum.rst @@ -115,30 +115,30 @@ Module contents :class:`StrEnum` defaults to the lower-cased version of the member name, while other Enums default to 1 and increase from there. - :func:`~enum.property` + :deco:`~enum.property` Allows :class:`Enum` members to have attributes without conflicting with member names. The ``value`` and ``name`` attributes are implemented this way. - :func:`unique` + :deco:`unique` Enum class decorator that ensures only one name is bound to any one value. - :func:`verify` + :deco:`verify` Enum class decorator that checks user-selectable constraints on an enumeration. - :func:`member` + :deco:`member` Make ``obj`` a member. Can be used as a decorator. - :func:`nonmember` + :deco:`nonmember` Do not make ``obj`` a member. Can be used as a decorator. - :func:`global_enum` + :deco:`global_enum` Modify the :class:`str() ` and :func:`repr` of an enum to show its members as belonging to the module instead of its class, @@ -1006,7 +1006,7 @@ Utilities and decorators .. decorator:: property - A decorator similar to the built-in *property*, but specifically for + A decorator similar to the built-in :deco:`property`, but specifically for enumerations. It allows member attributes to have the same names as members themselves. diff --git a/Doc/library/fnmatch.rst b/Doc/library/fnmatch.rst index a213679e4e2dd76..fc99b7fd1e7523a 100644 --- a/Doc/library/fnmatch.rst +++ b/Doc/library/fnmatch.rst @@ -51,7 +51,7 @@ Unless stated otherwise, "filename string" and "pattern string" either refer to functions documented below do not allow to mix a :class:`!bytes` pattern with a :class:`!str` filename, and vice-versa. -Finally, note that :func:`functools.lru_cache` with a *maxsize* of 32768 +Finally, note that :deco:`functools.lru_cache` with a *maxsize* of 32768 is used to cache the (typed) compiled regex patterns in the following functions: :func:`fnmatch`, :func:`fnmatchcase`, :func:`.filter`, :func:`.filterfalse`. diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index def2a211d1b3b4d..ebf2dea1c1f80c1 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -1680,7 +1680,7 @@ are always available. They are listed here in alphabetical order. If given, *doc* will be the docstring of the property attribute. Otherwise, the property will copy *fget*'s docstring (if it exists). This makes it possible to - create read-only properties easily using :func:`property` as a :term:`decorator`:: + create read-only properties easily using :deco:`property` as a :term:`decorator`:: class Parrot: def __init__(self): @@ -1984,7 +1984,7 @@ are always available. They are listed here in alphabetical order. be used in the class definition (such as ``f()``). Static methods in Python are similar to those found in Java or C++. Also, see - :func:`classmethod` for a variant that is useful for creating alternate class + :deco:`classmethod` for a variant that is useful for creating alternate class constructors. Like all decorators, it is also possible to call ``staticmethod`` as diff --git a/Doc/library/functools.rst b/Doc/library/functools.rst index 7da59cba5170b35..2b46978f058102b 100644 --- a/Doc/library/functools.rst +++ b/Doc/library/functools.rst @@ -27,7 +27,7 @@ The :mod:`!functools` module defines the following functions: Returns the same as ``lru_cache(maxsize=None)``, creating a thin wrapper around a dictionary lookup for the function arguments. Because it never needs to evict old values, this is smaller and faster than - :func:`lru_cache` with a size limit. + :deco:`lru_cache` with a size limit. For example:: @@ -61,7 +61,7 @@ The :mod:`!functools` module defines the following functions: Transform a method of a class into a property whose value is computed once and then cached as a normal attribute for the life of the instance. Similar - to :func:`property`, with the addition of caching. Useful for expensive + to :deco:`property`, with the addition of caching. Useful for expensive computed properties of instances that are otherwise effectively immutable. Example:: @@ -75,8 +75,8 @@ The :mod:`!functools` module defines the following functions: def stdev(self): return statistics.stdev(self._data) - The mechanics of :func:`cached_property` are somewhat different from - :func:`property`. A regular property blocks attribute writes unless a + The mechanics of :deco:`cached_property` are somewhat different from + :deco:`property`. A regular property blocks attribute writes unless a setter is defined. In contrast, a *cached_property* allows writes. The *cached_property* decorator only runs on lookups and only when an @@ -108,14 +108,14 @@ The :mod:`!functools` module defines the following functions: (as such classes don't provide a ``__dict__`` attribute at all). If a mutable mapping is not available or if space-efficient key sharing is - desired, an effect similar to :func:`cached_property` can also be achieved by - stacking :func:`property` on top of :func:`lru_cache`. See - :ref:`faq-cache-method-calls` for more details on how this differs from :func:`cached_property`. + desired, an effect similar to :deco:`cached_property` can also be achieved by + stacking :deco:`property` on top of :deco:`lru_cache`. See + :ref:`faq-cache-method-calls` for more details on how this differs from :deco:`cached_property`. .. versionadded:: 3.8 .. versionchanged:: 3.12 - Prior to Python 3.12, ``cached_property`` included an undocumented lock to + Prior to Python 3.12, :deco:`!cached_property` included an undocumented lock to ensure that in multi-threaded usage the getter function was guaranteed to run only once per instance. However, the lock was per-property, not per-instance, which could result in unacceptably high lock contention. In @@ -732,7 +732,7 @@ The :mod:`!functools` module defines the following functions: function's :attr:`~function.__dict__`, i.e. the instance dictionary). To allow access to the original function for introspection and other purposes - (e.g. bypassing a caching decorator such as :func:`lru_cache`), this function + (e.g. bypassing a caching decorator such as :deco:`lru_cache`), this function automatically adds a ``__wrapped__`` attribute to the wrapper that refers to the function being wrapped. diff --git a/Doc/library/tarfile.rst b/Doc/library/tarfile.rst index 9b9783d8e58013f..29a329fdfeab15b 100644 --- a/Doc/library/tarfile.rst +++ b/Doc/library/tarfile.rst @@ -670,7 +670,7 @@ be finalized; only the internally used file object will be closed. See the it is best practice to only do so in top-level applications or :mod:`site configuration `. To set a global default this way, a filter function needs to be wrapped in - :func:`staticmethod` to prevent injection of a ``self`` argument. + :deco:`staticmethod` to prevent injection of a ``self`` argument. .. versionchanged:: 3.14 diff --git a/Doc/library/test.rst b/Doc/library/test.rst index 7ae3fabf1cec644..4e21e1ded82724c 100644 --- a/Doc/library/test.rst +++ b/Doc/library/test.rst @@ -718,7 +718,7 @@ The :mod:`!test.support` module defines the following functions: .. decorator:: anticipate_failure(condition) A decorator to conditionally mark tests with - :func:`unittest.expectedFailure`. Any use of this decorator should + :deco:`unittest.expectedFailure`. Any use of this decorator should have an associated comment identifying the relevant tracker issue. diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index ef6016d45c1f8bc..c909b8bad6d726c 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -2583,7 +2583,7 @@ types. func(C()) # Passes static type check See :pep:`544` for more details. Protocol classes decorated with - :func:`runtime_checkable` (described later) act as simple-minded runtime + :deco:`runtime_checkable` (described later) act as simple-minded runtime protocols that check only the presence of given attributes, ignoring their type signatures. Protocol classes without this decorator cannot be used as the second argument to :func:`isinstance` or :func:`issubclass`. @@ -2648,7 +2648,7 @@ types. .. note:: - :func:`!runtime_checkable` will check only the presence of the required + :deco:`!runtime_checkable` will check only the presence of the required methods or attributes, not their type signatures or types. For example, :class:`ssl.SSLObject` is a class, therefore it passes an :func:`issubclass` @@ -3154,7 +3154,7 @@ Functions and decorators Decorator to mark an object as providing :func:`dataclass `-like behavior. - ``dataclass_transform`` may be used to + ``@dataclass_transform`` may be used to decorate a class, metaclass, or a function that is itself a decorator. The presence of ``@dataclass_transform()`` tells a static type checker that the decorated object performs runtime "magic" that @@ -3209,7 +3209,7 @@ Functions and decorators ``kw_only``, and ``slots``. It must be possible for the value of these arguments (``True`` or ``False``) to be statically evaluated. - The arguments to the ``dataclass_transform`` decorator can be used to + The arguments to the ``@dataclass_transform`` decorator can be used to customize the default behaviors of the decorated class, metaclass, or function: @@ -3273,8 +3273,8 @@ Functions and decorators keyword-only. If ``True``, the field will be keyword-only. If ``False``, it will not be keyword-only. If unspecified, the value of the ``kw_only`` parameter on the object decorated with - ``dataclass_transform`` will be used, or if that is unspecified, the - value of ``kw_only_default`` on ``dataclass_transform`` will be used. + ``@dataclass_transform`` will be used, or if that is unspecified, the + value of ``kw_only_default`` on ``@dataclass_transform`` will be used. * - ``alias`` - Provides an alternative name for the field. This alternative name is used in the synthesized ``__init__`` method. diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst index ff619f979233251..7afcdb368a3562e 100644 --- a/Doc/library/unittest.rst +++ b/Doc/library/unittest.rst @@ -530,7 +530,7 @@ tests. In addition, it supports marking a test as an "expected failure," a test that is broken and will fail, but shouldn't be counted as a failure on a :class:`TestResult`. -Skipping a test is simply a matter of using the :func:`skip` :term:`decorator` +Skipping a test is simply a matter of using the :deco:`skip` :term:`decorator` or one of its conditional variants, calling :meth:`TestCase.skipTest` within a :meth:`~TestCase.setUp` or test method, or raising :exc:`SkipTest` directly. @@ -581,7 +581,7 @@ Classes can be skipped just like methods:: :meth:`TestCase.setUp` can also skip the test. This is useful when a resource that needs to be set up is not available. -Expected failures use the :func:`expectedFailure` decorator. :: +Expected failures use the :deco:`expectedFailure` decorator. :: class ExpectedFailureTestCase(unittest.TestCase): @unittest.expectedFailure @@ -763,7 +763,7 @@ Test cases A class method called before tests in an individual class are run. ``setUpClass`` is called with the class as the only argument - and must be decorated as a :func:`classmethod`:: + and must be decorated as a :deco:`classmethod`:: @classmethod def setUpClass(cls): @@ -778,7 +778,7 @@ Test cases A class method called after tests in an individual class have run. ``tearDownClass`` is called with the class as the only argument - and must be decorated as a :meth:`classmethod`:: + and must be decorated as a :deco:`classmethod`:: @classmethod def tearDownClass(cls): @@ -2156,7 +2156,7 @@ Loading and running tests .. versionchanged:: 3.4 Returns ``False`` if there were any :attr:`unexpectedSuccesses` - from tests marked with the :func:`expectedFailure` decorator. + from tests marked with the :deco:`expectedFailure` decorator. .. method:: stop() @@ -2239,7 +2239,7 @@ Loading and running tests .. method:: addExpectedFailure(test, err) Called when the test case *test* fails or errors, but was marked with - the :func:`expectedFailure` decorator. + the :deco:`expectedFailure` decorator. The default implementation appends a tuple ``(test, formatted_err)`` to the instance's :attr:`expectedFailures` attribute, where *formatted_err* @@ -2249,7 +2249,7 @@ Loading and running tests .. method:: addUnexpectedSuccess(test) Called when the test case *test* was marked with the - :func:`expectedFailure` decorator, but succeeded. + :deco:`expectedFailure` decorator, but succeeded. The default implementation appends the test to the instance's :attr:`unexpectedSuccesses` attribute. diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index 63baefd33e88c50..6710c0acf77e350 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -2010,7 +2010,7 @@ Annotations are conventionally used for :term:`type hints `, but this is not enforced by the language, and in general annotations may contain arbitrary expressions. The presence of annotations does not change the runtime semantics of the code, except if some mechanism is used that introspects and uses the annotations -(such as :mod:`dataclasses` or :func:`functools.singledispatch`). +(such as :mod:`dataclasses` or :deco:`functools.singledispatch`). By default, annotations are lazily evaluated in an :ref:`annotation scope `. This means that they are not evaluated when the code containing the annotation is evaluated. diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index a8614128c85dada..dd8f3e33128a97f 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -2197,7 +2197,7 @@ Basic customization :data:`!NotImplemented`. There are no other implied relationships among the comparison operators or default implementations; for example, the truth of ``(x>> from functools import singledispatch @@ -1164,7 +1164,7 @@ For major changes, see :ref:`new-feat-related-type-hints-311`. type checker errors related to highly dynamic class, such as mocks. (Contributed by Shantanu Jain in :gh:`91154`.) -* The :func:`typing.final` decorator now sets the ``__final__`` attributed on +* The :deco:`typing.final` decorator now sets the ``__final__`` attributed on the decorated object. (Contributed by Jelle Zijlstra in :gh:`90500`.) @@ -1197,7 +1197,7 @@ For major changes, see :ref:`new-feat-related-type-hints-311`. :data:`~typing.ClassVar` annotations. (Contributed by Gregory Beauregard in :gh:`90711`.) -* :func:`typing.no_type_check` no longer modifies external classes and functions. +* :deco:`typing.no_type_check` no longer modifies external classes and functions. It also now correctly marks classmethods as not to be type checked. (Contributed by Nikita Sobolev in :gh:`90729`.) diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 0378ada72804c9e..9e48b3ab7393c87 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -146,7 +146,7 @@ New typing features: * :ref:`PEP 692 `, using :class:`~typing.TypedDict` to annotate :term:`**kwargs ` -* :ref:`PEP 698 `, :func:`typing.override` decorator +* :ref:`PEP 698 `, :deco:`typing.override` decorator Important deprecations, removals or restrictions: @@ -511,7 +511,7 @@ See :pep:`692` for more details. PEP 698: Override Decorator for Static Typing --------------------------------------------- -A new decorator :func:`typing.override` has been added to the :mod:`typing` +A new decorator :deco:`typing.override` has been added to the :mod:`typing` module. It indicates to type checkers that the method is intended to override a method in a superclass. This allows type checkers to catch mistakes where a method that is intended to override something in a base class @@ -1021,7 +1021,7 @@ typing ``__orig_bases__`` attribute. (Contributed by Adrian Garcia Badaracco in :gh:`103699`.) -* Add ``frozen_default`` parameter to :func:`typing.dataclass_transform`. +* Add ``frozen_default`` parameter to :deco:`typing.dataclass_transform`. (Contributed by Erik De Bonte in :gh:`99957`.) unicodedata @@ -1749,7 +1749,7 @@ Changes in the Python API around process-global resources, which are best managed from the main interpreter. (Contributed by Donghee Na in :gh:`99127`.) -* The undocumented locking behavior of :func:`~functools.cached_property` +* The undocumented locking behavior of :deco:`~functools.cached_property` is removed, because it locked across all instances of the class, leading to high lock contention. This means that a cached property getter function could now run more than once for a single instance, if two threads race. For most simple diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 0db8ec55e5c9f8e..d8845fe921ededa 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -1994,7 +1994,7 @@ New Deprecations use ``class TD(TypedDict): pass`` or ``TD = TypedDict("TD", {})``. (Contributed by Alex Waygood in :gh:`105566` and :gh:`105570`.) - * Deprecate the :func:`!typing.no_type_check_decorator` decorator function, + * Deprecate the :deco:`!typing.no_type_check_decorator` decorator function, to be removed in Python 3.15. After eight years in the :mod:`typing` module, it has yet to be supported by any major type checker. diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index ccfe0f59cb5615c..7f9a0f0e286645d 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -2151,7 +2151,7 @@ typing or ``TD = TypedDict("TD", {})`` instead. (Contributed by Bénédikt Tran in :gh:`133823`.) -* Deprecated :func:`!typing.no_type_check_decorator` has been removed. +* Deprecated :deco:`!typing.no_type_check_decorator` has been removed. (Contributed by Nikita Sobolev in :gh:`133601`.) diff --git a/Doc/whatsnew/3.2.rst b/Doc/whatsnew/3.2.rst index 48c461d891e8614..c61eb659ccc0f62 100644 --- a/Doc/whatsnew/3.2.rst +++ b/Doc/whatsnew/3.2.rst @@ -756,7 +756,7 @@ functools --------- * The :mod:`functools` module includes a new decorator for caching function - calls. :func:`functools.lru_cache` can save repeated queries to an external + calls. :deco:`functools.lru_cache` can save repeated queries to an external resource whenever the results are expected to be the same. For example, adding a caching decorator to a database query function can save @@ -789,7 +789,7 @@ functools `_\, :issue:`10586`, and :issue:`10593`.) -* The :func:`functools.wraps` decorator now adds a :attr:`__wrapped__` attribute +* The :deco:`functools.wraps` decorator now adds a :attr:`__wrapped__` attribute pointing to the original callable function. This allows wrapped functions to be introspected. It also copies :attr:`~function.__annotations__` if defined. And now it also gracefully skips over missing attributes such as @@ -805,11 +805,11 @@ functools :issue:`8814`.) * To help write classes with rich comparison methods, a new decorator - :func:`functools.total_ordering` will use existing equality and inequality + :deco:`functools.total_ordering` will use existing equality and inequality methods to fill in the remaining methods. For example, supplying *__eq__* and *__lt__* will enable - :func:`~functools.total_ordering` to fill-in *__le__*, *__gt__* and *__ge__*:: + :deco:`~functools.total_ordering` to fill-in *__le__*, *__gt__* and *__ge__*:: @total_ordering class Student: @@ -1097,11 +1097,11 @@ logarithm of the gamma function: abc --- -The :mod:`abc` module now supports :func:`~abc.abstractclassmethod` and -:func:`~abc.abstractstaticmethod`. +The :mod:`abc` module now supports :deco:`~abc.abstractclassmethod` and +:deco:`~abc.abstractstaticmethod`. These tools make it possible to define an :term:`abstract base class` that -requires a particular :func:`classmethod` or :func:`staticmethod` to be +requires a particular :deco:`classmethod` or :deco:`staticmethod` to be implemented:: class Temperature(metaclass=abc.ABCMeta): @@ -1155,7 +1155,7 @@ self-reference by displaying "..." in the recursive part of the representation string. To help write such :meth:`~object.__repr__` methods, the :mod:`reprlib` module has a new -decorator, :func:`~reprlib.recursive_repr`, for detecting recursive calls to +decorator, :deco:`~reprlib.recursive_repr`, for detecting recursive calls to :meth:`!__repr__` and substituting a placeholder string instead:: >>> class MyList(list): @@ -1241,7 +1241,7 @@ There is a new and slightly mind-blowing tool :term:`context manager` that does double duty as a function decorator. As a convenience, this new functionality is used by -:func:`~contextlib.contextmanager` so that no extra effort is needed to support +:deco:`~contextlib.contextmanager` so that no extra effort is needed to support both roles. The basic idea is that both context managers and function decorators can be used @@ -1253,7 +1253,7 @@ write a pre-action or post-action wrapper that can be used in either role. For example, it is sometimes useful to wrap functions or groups of statements with a logger that can track the time of entry and time of exit. Rather than writing both a function decorator and a context manager for the task, the -:func:`~contextlib.contextmanager` provides both capabilities in a single +:deco:`~contextlib.contextmanager` provides both capabilities in a single definition:: from contextlib import contextmanager diff --git a/Doc/whatsnew/3.3.rst b/Doc/whatsnew/3.3.rst index 1bb79bce2c3e972..02fd264e53e1b06 100644 --- a/Doc/whatsnew/3.3.rst +++ b/Doc/whatsnew/3.3.rst @@ -919,12 +919,12 @@ abstract methods. The recommended approach to declaring abstract descriptors is now to provide :attr:`!__isabstractmethod__` as a dynamically updated property. The built-in descriptors have been updated accordingly. -* :class:`abc.abstractproperty` has been deprecated, use :class:`property` - with :func:`abc.abstractmethod` instead. -* :class:`abc.abstractclassmethod` has been deprecated, use - :class:`classmethod` with :func:`abc.abstractmethod` instead. -* :class:`abc.abstractstaticmethod` has been deprecated, use - :class:`staticmethod` with :func:`abc.abstractmethod` instead. +* :deco:`abc.abstractproperty` has been deprecated, use :deco:`property` + with :deco:`abc.abstractmethod` instead. +* :deco:`abc.abstractclassmethod` has been deprecated, use + :deco:`classmethod` with :deco:`abc.abstractmethod` instead. +* :deco:`abc.abstractstaticmethod` has been deprecated, use + :deco:`staticmethod` with :deco:`abc.abstractmethod` instead. (Contributed by Darren Dale in :issue:`11610`.) @@ -1390,7 +1390,7 @@ ftplib functools --------- -The :func:`functools.lru_cache` decorator now accepts a ``typed`` keyword +The :deco:`functools.lru_cache` decorator now accepts a ``typed`` keyword argument (that defaults to ``False`` to ensure that it caches values of different types that compare equal in separate cache slots. (Contributed by Raymond Hettinger in :issue:`13227`.) @@ -2244,12 +2244,12 @@ Deprecated Python modules, functions and methods * The :func:`!os.stat_float_times` function is deprecated. * :mod:`abc` module: - * :class:`abc.abstractproperty` has been deprecated, use :class:`property` - with :func:`abc.abstractmethod` instead. - * :class:`abc.abstractclassmethod` has been deprecated, use - :class:`classmethod` with :func:`abc.abstractmethod` instead. - * :class:`abc.abstractstaticmethod` has been deprecated, use - :class:`staticmethod` with :func:`abc.abstractmethod` instead. + * :deco:`abc.abstractproperty` has been deprecated, use :deco:`property` + with :deco:`abc.abstractmethod` instead. + * :deco:`abc.abstractclassmethod` has been deprecated, use + :deco:`classmethod` with :deco:`abc.abstractmethod` instead. + * :deco:`abc.abstractstaticmethod` has been deprecated, use + :deco:`staticmethod` with :deco:`abc.abstractmethod` instead. * :mod:`importlib` package: diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst index a390211ddb50215..0067491c569cc05 100644 --- a/Doc/whatsnew/3.4.rst +++ b/Doc/whatsnew/3.4.rst @@ -858,7 +858,7 @@ to behave like normal instance methods when included in a class definition. .. _whatsnew-singledispatch: -The new :func:`~functools.singledispatch` decorator brings support for +The new :deco:`~functools.singledispatch` decorator brings support for single-dispatch generic functions to the Python standard library. Where object oriented programming focuses on grouping multiple operations on a common set of data into a class, a generic function focuses on grouping @@ -870,7 +870,7 @@ multiple implementations of an operation that allows it to work with :pep:`443` -- Single-dispatch generic functions PEP written and implemented by Łukasz Langa. -:func:`~functools.total_ordering` now supports a return value of +:deco:`~functools.total_ordering` now supports a return value of :data:`NotImplemented` from the underlying comparison function. (Contributed by Katie Miller in :issue:`10042`.) @@ -1029,7 +1029,7 @@ information for modules, classes and functions. (Contributed by Claudiu Popa and Nick Coghlan in :issue:`18626`.) :func:`~inspect.unwrap` makes it easy to unravel wrapper function chains -created by :func:`functools.wraps` (and any other API that sets the +created by :deco:`functools.wraps` (and any other API that sets the ``__wrapped__`` attribute on a wrapper function). (Contributed by Daniel Urban, Aaron Iles and Nick Coghlan in :issue:`13266`.) @@ -2317,7 +2317,7 @@ Changes in the Python API wish to continue to ignore syntax or decoding issues, catch all three exceptions now. -* :func:`functools.update_wrapper` and :func:`functools.wraps` now correctly +* :func:`functools.update_wrapper` and :deco:`functools.wraps` now correctly set the ``__wrapped__`` attribute to the function being wrapped, even if that function also had its ``__wrapped__`` attribute set. This means ``__wrapped__`` attributes now correctly link a stack of decorated diff --git a/Doc/whatsnew/3.5.rst b/Doc/whatsnew/3.5.rst index 6009dd8a71eea52..24a2ffbefd0cfba 100644 --- a/Doc/whatsnew/3.5.rst +++ b/Doc/whatsnew/3.5.rst @@ -121,7 +121,7 @@ Significant improvements in the standard library: :ref:`better and significantly faster way ` of directory traversal. -* :func:`functools.lru_cache` has been mostly +* :deco:`functools.lru_cache` has been mostly :ref:`reimplemented in C `, yielding much better performance. @@ -1148,7 +1148,7 @@ functools .. _whatsnew-lrucache: -Most of the :func:`~functools.lru_cache` machinery is now implemented in C, making +Most of the :deco:`~functools.lru_cache` machinery is now implemented in C, making it significantly faster. (Contributed by Matt Joiner, Alexey Kachayev, and Serhiy Storchaka in :issue:`14373`.) @@ -2158,7 +2158,7 @@ improvement in some benchmarks. Objects from the :mod:`random` module now use 50% less memory on 64-bit builds. (Contributed by Serhiy Storchaka in :issue:`23488`.) -The :func:`property` getter calls are up to 25% faster. +The :deco:`property` getter calls are up to 25% faster. (Contributed by Joe Jevnik in :issue:`23910`.) Instantiation of :class:`fractions.Fraction` is now up to 30% faster. diff --git a/Doc/whatsnew/3.7.rst b/Doc/whatsnew/3.7.rst index 5dd47cdac96a5c0..3af3e6ec9cac08f 100644 --- a/Doc/whatsnew/3.7.rst +++ b/Doc/whatsnew/3.7.rst @@ -571,7 +571,7 @@ decimal operations to work with the correct context in asynchronous code. dataclasses ----------- -The new :func:`~dataclasses.dataclass` decorator provides a way to declare +The new :deco:`~dataclasses.dataclass` decorator provides a way to declare *data classes*. A data class describes its attributes using class variable annotations. Its constructor and other magic methods, such as :meth:`~object.__repr__`, :meth:`~object.__eq__`, and @@ -830,7 +830,7 @@ The new :func:`~contextlib.nullcontext` is a simpler and faster no-op context manager than :class:`~contextlib.ExitStack`. (Contributed by Jesse-Bakker in :issue:`10049`.) -The new :func:`~contextlib.asynccontextmanager`, +The new :deco:`~contextlib.asynccontextmanager`, :class:`~contextlib.AbstractAsyncContextManager`, and :class:`~contextlib.AsyncExitStack` have been added to complement their synchronous counterparts. (Contributed @@ -921,7 +921,7 @@ return :const:`False` instead and are deprecated. functools --------- -:func:`functools.singledispatch` now supports registering implementations +:deco:`functools.singledispatch` now supports registering implementations using type annotations. (Contributed by Łukasz Langa in :issue:`32227`.) diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst index 5078fc30ac111e4..42d98ff94ec0d1b 100644 --- a/Doc/whatsnew/3.8.rst +++ b/Doc/whatsnew/3.8.rst @@ -764,7 +764,7 @@ these are the inverse of each class's ``isocalendar`` method. functools --------- -:func:`functools.lru_cache` can now be used as a straight decorator rather +:deco:`functools.lru_cache` can now be used as a straight decorator rather than as a function returning a decorator. So both of these are now supported:: @lru_cache @@ -777,7 +777,7 @@ than as a function returning a decorator. So both of these are now supported:: (Contributed by Raymond Hettinger in :issue:`36772`.) -Added a new :func:`functools.cached_property` decorator, for computed properties +Added a new :deco:`functools.cached_property` decorator, for computed properties cached for the life of the instance. :: import functools @@ -893,7 +893,7 @@ inspect The :func:`inspect.getdoc` function can now find docstrings for ``__slots__`` if that attribute is a :class:`dict` where the values are docstrings. This provides documentation options similar to what we already have -for :func:`property`, :func:`classmethod`, and :func:`staticmethod`:: +for :deco:`property`, :deco:`classmethod`, and :deco:`staticmethod`:: class AudioClip: __slots__ = {'bit_rate': 'expressed in kilohertz to one decimal place', @@ -1333,14 +1333,14 @@ The :mod:`typing` module incorporates several new features: ... * "Final" variables, functions, methods and classes. See :pep:`591`, - :class:`typing.Final` and :func:`typing.final`. + :class:`typing.Final` and :deco:`typing.final`. The final qualifier instructs a static type checker to restrict subclassing, overriding, or reassignment:: pi: Final[float] = 3.1415926536 * Protocol definitions. See :pep:`544`, :class:`typing.Protocol` and - :func:`typing.runtime_checkable`. Simple ABCs like + :deco:`typing.runtime_checkable`. Simple ABCs like :class:`typing.SupportsInt` are now ``Protocol`` subclasses. * New protocol class :class:`typing.SupportsIndex`. From fc2227da04014f9948019f3959460defa473ff8f Mon Sep 17 00:00:00 2001 From: da-woods Date: Wed, 3 Jun 2026 14:20:35 +0100 Subject: [PATCH 008/185] Correct Stable ABI documentation for METH_FASTCALL (GH-149593) * Correct Stable ABI documentation for METH_FASTCALL The current documentation says: > > METH_FASTCALL > Part of the Stable ABI since version 3.7. > > [...] > > Added in version 3.7. > > Changed in version 3.10: METH_FASTCALL is now part of the stable ABI. so is contradictory about when it was added to the Stable ABI. Looking at the header it seems like 3.10 is right. --- Doc/data/stable_abi.dat | 2 +- Misc/stable_abi.toml | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Doc/data/stable_abi.dat b/Doc/data/stable_abi.dat index 2d4278c9d97c859..86080fac7163838 100644 --- a/Doc/data/stable_abi.dat +++ b/Doc/data/stable_abi.dat @@ -1,7 +1,7 @@ role,name,added,ifdef_note,struct_abi_kind macro,METH_CLASS,3.2,, macro,METH_COEXIST,3.2,, -macro,METH_FASTCALL,3.7,, +macro,METH_FASTCALL,3.10,, macro,METH_METHOD,3.7,, macro,METH_NOARGS,3.2,, macro,METH_O,3.2,, diff --git a/Misc/stable_abi.toml b/Misc/stable_abi.toml index 8fd7aba09241e63..d59a7c788fa9e02 100644 --- a/Misc/stable_abi.toml +++ b/Misc/stable_abi.toml @@ -1813,7 +1813,6 @@ [const.METH_COEXIST] added = '3.2' # METH_STACKLESS is undocumented -# METH_FASTCALL is not part of limited API. # The following are defined in private headers, but historically # they were exported as part of the stable ABI. @@ -2149,8 +2148,6 @@ # New method flags in 3.7 (PEP 590): -[const.METH_FASTCALL] - added = '3.7' [const.METH_METHOD] added = '3.7' @@ -2300,6 +2297,10 @@ [data.PyStructSequence_UnnamedField] added = '3.11' +# Added in 3.7 but in the Stable ABI from 3.10 +[const.METH_FASTCALL] + added = '3.10' + # Add stable Py_buffer API in Python 3.11 (https://bugs.python.org/issue45459) [struct.Py_buffer] added = '3.11' From 9fa24359e4045a2de70db20911fd68dbfd83eea5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abduaziz=20=CF=80?= Date: Wed, 3 Jun 2026 18:35:25 +0500 Subject: [PATCH 009/185] gh-132467: Document and test that generic aliases are not classes (#133504) Co-authored-by: Jelle Zijlstra --- Doc/library/stdtypes.rst | 9 ++++ Lib/test/test_typing.py | 21 ++++++++ Lib/typing.py | 112 ++++++++++++++++++++------------------- 3 files changed, 88 insertions(+), 54 deletions(-) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index c8f2cca484ab311..ba896212925d89b 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -5927,6 +5927,15 @@ creation:: >>> type(l) + +Instances of ``GenericAlias`` are not classes at runtime, even though they behave like classes (they can be instantiated and subclassed):: + + >>> import inspect + >>> inspect.isclass(list[int]) + False + +This is true for :ref:`user-defined generics ` also. + Calling :func:`repr` or :func:`str` on a generic shows the parameterized type:: >>> repr(list[int]) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index ad644bb31288098..042604ed7c1a423 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -5848,6 +5848,27 @@ def foo(x: T): foo(42) + def test_genericalias_instance_isclass(self): + # test against user-defined generic classes + T = TypeVar('T') + + class Node(Generic[T]): + def __init__(self, label: T, + left: 'Node[T] | None' = None, + right: 'Node[T] | None' = None): + self.label = label + self.left = left + self.right = right + + self.assertTrue(inspect.isclass(Node)) + self.assertFalse(inspect.isclass(Node[int])) + self.assertFalse(inspect.isclass(Node[str])) + + # test against standard generic classes + self.assertFalse(inspect.isclass(set[int])) + self.assertFalse(inspect.isclass(list[bytes])) + self.assertFalse(inspect.isclass(dict[str, str])) + def test_implicit_any(self): T = TypeVar('T') diff --git a/Lib/typing.py b/Lib/typing.py index 715d08e0e1603e6..1579f492003f748 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1343,32 +1343,35 @@ def __dir__(self): class _GenericAlias(_BaseGenericAlias, _root=True): - # The type of parameterized generics. - # - # That is, for example, `type(List[int])` is `_GenericAlias`. - # - # Objects which are instances of this class include: - # * Parameterized container types, e.g. `Tuple[int]`, `List[int]`. - # * Note that native container types, e.g. `tuple`, `list`, use - # `types.GenericAlias` instead. - # * Parameterized classes: - # class C[T]: pass - # # C[int] is a _GenericAlias - # * `Callable` aliases, generic `Callable` aliases, and - # parameterized `Callable` aliases: - # T = TypeVar('T') - # # _CallableGenericAlias inherits from _GenericAlias. - # A = Callable[[], None] # _CallableGenericAlias - # B = Callable[[T], None] # _CallableGenericAlias - # C = B[int] # _CallableGenericAlias - # * Parameterized `Final`, `ClassVar`, `TypeForm`, `TypeGuard`, and `TypeIs`: - # # All _GenericAlias - # Final[int] - # ClassVar[float] - # TypeForm[bytes] - # TypeGuard[bool] - # TypeIs[range] - + """The type of parameterized generics. + + That is, for example, `type(List[int])` is `_GenericAlias`. + + Objects which are instances of this class include: + * Parameterized container types, e.g. `Tuple[int]`, `List[int]`. + * Note that native container types, e.g. `tuple`, `list`, use + `types.GenericAlias` instead. + * Parameterized classes: + class C[T]: pass + # C[int] is a _GenericAlias + * `Callable` aliases, generic `Callable` aliases, and + parameterized `Callable` aliases: + T = TypeVar('T') + # _CallableGenericAlias inherits from _GenericAlias. + A = Callable[[], None] # _CallableGenericAlias + B = Callable[[T], None] # _CallableGenericAlias + C = B[int] # _CallableGenericAlias + * Parameterized `Final`, `ClassVar`, `TypeForm`, `TypeGuard`, and `TypeIs`: + # All _GenericAlias + Final[int] + ClassVar[float] + TypeForm[bytearray] + TypeGuard[bool] + TypeIs[range] + + Note that instances of this class are not classes (e.g by `inspect.isclass`), + even though they behave like them. + """ def __init__(self, origin, args, *, inst=True, name=None): super().__init__(origin, inst=inst, name=name) if not isinstance(args, tuple): @@ -1400,20 +1403,21 @@ def __ror__(self, left): @_tp_cache def __getitem__(self, args): - # Parameterizes an already-parameterized object. - # - # For example, we arrive here doing something like: - # T1 = TypeVar('T1') - # T2 = TypeVar('T2') - # T3 = TypeVar('T3') - # class A(Generic[T1]): pass - # B = A[T2] # B is a _GenericAlias - # C = B[T3] # Invokes _GenericAlias.__getitem__ - # - # We also arrive here when parameterizing a generic `Callable` alias: - # T = TypeVar('T') - # C = Callable[[T], None] - # C[int] # Invokes _GenericAlias.__getitem__ + """Parameterizes an already-parameterized object. + + For example, we arrive here doing something like: + T1 = TypeVar('T1') + T2 = TypeVar('T2') + T3 = TypeVar('T3') + class A(Generic[T1]): pass + B = A[T2] # B is a _GenericAlias + C = B[T3] # Invokes _GenericAlias.__getitem__ + + We also arrive here when parameterizing a generic `Callable` alias: + T = TypeVar('T') + C = Callable[[T], None] + C[int] # Invokes _GenericAlias.__getitem__ + """ if self.__origin__ in (Generic, Protocol): # Can't subscript Generic[...] or Protocol[...]. @@ -1430,20 +1434,20 @@ def __getitem__(self, args): return r def _determine_new_args(self, args): - # Determines new __args__ for __getitem__. - # - # For example, suppose we had: - # T1 = TypeVar('T1') - # T2 = TypeVar('T2') - # class A(Generic[T1, T2]): pass - # T3 = TypeVar('T3') - # B = A[int, T3] - # C = B[str] - # `B.__args__` is `(int, T3)`, so `C.__args__` should be `(int, str)`. - # Unfortunately, this is harder than it looks, because if `T3` is - # anything more exotic than a plain `TypeVar`, we need to consider - # edge cases. - + """Determines new __args__ for __getitem__. + + For example, suppose we had: + T1 = TypeVar('T1') + T2 = TypeVar('T2') + class A(Generic[T1, T2]): pass + T3 = TypeVar('T3') + B = A[int, T3] + C = B[str] + `B.__args__` is `(int, T3)`, so `C.__args__` should be `(int, str)`. + Unfortunately, this is harder than it looks, because if `T3` is + anything more exotic than a plain `TypeVar`, we need to consider + edge cases. + """ params = self.__parameters__ # In the example above, this would be {T3: str} for param in params: From 1fadf7fad05db6f3964ee2fd87aafb522eba851b Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Wed, 3 Jun 2026 16:37:09 +0300 Subject: [PATCH 010/185] Docs: Replace hardcoded `SOURCE_URI` with `patchlevel` check (#150850) --- Doc/conf.py | 12 +++++++----- Doc/tools/extensions/pyspecific.py | 11 +---------- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/Doc/conf.py b/Doc/conf.py index 3856f4fe9b375c4..9b103a594b235cf 100644 --- a/Doc/conf.py +++ b/Doc/conf.py @@ -8,15 +8,13 @@ import os import sys -from importlib import import_module from importlib.util import find_spec # Make our custom extensions available to Sphinx sys.path.append(os.path.abspath('tools/extensions')) sys.path.append(os.path.abspath('includes')) -# Python specific content from Doc/Tools/extensions/pyspecific.py -from pyspecific import SOURCE_URI +from patchlevel import get_header_version_info, get_version_info # General configuration # --------------------- @@ -78,7 +76,7 @@ # We look for the Include/patchlevel.h file in the current Python source tree # and replace the values accordingly. # See Doc/tools/extensions/patchlevel.py -version, release = import_module('patchlevel').get_version_info() +version, release = get_version_info() rst_epilog = f""" .. |python_version_literal| replace:: ``Python {version}`` @@ -555,16 +553,20 @@ r'https://unix.org/version2/whatsnew/lp64_wp.html', ] + # Options for sphinx.ext.extlinks # ------------------------------- +v = get_header_version_info() +branch = "main" if v.releaselevel == "alpha" else f"{v.major}.{v.minor}" + # This config is a dictionary of external sites, # mapping unique short aliases to a base URL and a prefix. # https://www.sphinx-doc.org/en/master/usage/extensions/extlinks.html extlinks = { "oss-fuzz": ("https://issues.oss-fuzz.com/issues/%s", "#%s"), "pypi": ("https://pypi.org/project/%s/", "%s"), - "source": (SOURCE_URI, "%s"), + "source": (f"https://github.com/python/cpython/tree/{branch}/%s", "%s"), } extlinks_detect_hardcoded_links = True diff --git a/Doc/tools/extensions/pyspecific.py b/Doc/tools/extensions/pyspecific.py index f9bf273e7624a04..9b335f2976a2fa3 100644 --- a/Doc/tools/extensions/pyspecific.py +++ b/Doc/tools/extensions/pyspecific.py @@ -10,19 +10,10 @@ """ import re -import io -from os import getenv, path -from docutils import nodes -from docutils.parsers.rst import directives -from docutils.utils import unescape from sphinx import addnodes -from sphinx.domains.python import PyFunction, PyMethod, PyModule -from sphinx.locale import _ as sphinx_gettext -from sphinx.util.docutils import SphinxDirective +from sphinx.domains.python import PyFunction, PyMethod -# Used in conf.py and updated here by python/release-tools/run_release.py -SOURCE_URI = 'https://github.com/python/cpython/tree/main/%s' class PyAwaitableMixin(object): def handle_signature(self, sig, signode): From cf354f3822a2f06a5581cbb918a2eaecde2e3e43 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Wed, 3 Jun 2026 15:29:13 +0100 Subject: [PATCH 011/185] gh-150032: Tidy up 3.15 pending removals (#149594) Co-authored-by: Savannah Ostrowski Co-authored-by: Jelle Zijlstra Co-authored-by: Brett Cannon --- Doc/c-api/interp-lifecycle.rst | 42 ++++----- Doc/c-api/unicode.rst | 2 +- .../c-api-pending-removal-in-3.15.rst | 85 ------------------- .../c-api-pending-removal-in-3.16.rst | 85 ++++++++++++++++++- Doc/deprecations/index.rst | 4 - Doc/deprecations/pending-removal-in-3.15.rst | 5 -- Doc/deprecations/pending-removal-in-3.16.rst | 7 ++ Doc/reference/datamodel.rst | 2 +- Doc/tools/removed-ids.txt | 3 + 9 files changed, 117 insertions(+), 118 deletions(-) diff --git a/Doc/c-api/interp-lifecycle.rst b/Doc/c-api/interp-lifecycle.rst index 8dee601d04876b8..46f5b1dd33963c0 100644 --- a/Doc/c-api/interp-lifecycle.rst +++ b/Doc/c-api/interp-lifecycle.rst @@ -104,7 +104,7 @@ to 1 and ``-bb`` sets :c:data:`Py_BytesWarningFlag` to 2. Set by the :option:`-b` option. - .. deprecated-removed:: 3.12 3.15 + .. deprecated-removed:: 3.12 3.16 .. c:var:: int Py_DebugFlag @@ -119,7 +119,7 @@ to 1 and ``-bb`` sets :c:data:`Py_BytesWarningFlag` to 2. Set by the :option:`-d` option and the :envvar:`PYTHONDEBUG` environment variable. - .. deprecated-removed:: 3.12 3.15 + .. deprecated-removed:: 3.12 3.16 .. c:var:: int Py_DontWriteBytecodeFlag @@ -134,7 +134,7 @@ to 1 and ``-bb`` sets :c:data:`Py_BytesWarningFlag` to 2. Set by the :option:`-B` option and the :envvar:`PYTHONDONTWRITEBYTECODE` environment variable. - .. deprecated-removed:: 3.12 3.15 + .. deprecated-removed:: 3.12 3.16 .. c:var:: int Py_FrozenFlag @@ -145,7 +145,7 @@ to 1 and ``-bb`` sets :c:data:`Py_BytesWarningFlag` to 2. Private flag used by ``_freeze_module`` and ``frozenmain`` programs. - .. deprecated-removed:: 3.12 3.15 + .. deprecated-removed:: 3.12 3.16 .. c:var:: int Py_HashRandomizationFlag @@ -161,7 +161,7 @@ to 1 and ``-bb`` sets :c:data:`Py_BytesWarningFlag` to 2. If the flag is non-zero, read the :envvar:`PYTHONHASHSEED` environment variable to initialize the secret hash seed. - .. deprecated-removed:: 3.12 3.15 + .. deprecated-removed:: 3.12 3.16 .. c:var:: int Py_IgnoreEnvironmentFlag @@ -175,7 +175,7 @@ to 1 and ``-bb`` sets :c:data:`Py_BytesWarningFlag` to 2. Set by the :option:`-E` and :option:`-I` options. - .. deprecated-removed:: 3.12 3.15 + .. deprecated-removed:: 3.12 3.16 .. c:var:: int Py_InspectFlag @@ -191,7 +191,7 @@ to 1 and ``-bb`` sets :c:data:`Py_BytesWarningFlag` to 2. Set by the :option:`-i` option and the :envvar:`PYTHONINSPECT` environment variable. - .. deprecated-removed:: 3.12 3.15 + .. deprecated-removed:: 3.12 3.16 .. c:var:: int Py_InteractiveFlag @@ -202,7 +202,7 @@ to 1 and ``-bb`` sets :c:data:`Py_BytesWarningFlag` to 2. Set by the :option:`-i` option. - .. deprecated-removed:: 3.12 3.15 + .. deprecated-removed:: 3.12 3.16 .. c:var:: int Py_IsolatedFlag @@ -218,7 +218,7 @@ to 1 and ``-bb`` sets :c:data:`Py_BytesWarningFlag` to 2. .. versionadded:: 3.4 - .. deprecated-removed:: 3.12 3.15 + .. deprecated-removed:: 3.12 3.16 .. c:var:: int Py_LegacyWindowsFSEncodingFlag @@ -238,7 +238,7 @@ to 1 and ``-bb`` sets :c:data:`Py_BytesWarningFlag` to 2. .. availability:: Windows. - .. deprecated-removed:: 3.12 3.15 + .. deprecated-removed:: 3.12 3.16 .. c:var:: int Py_LegacyWindowsStdioFlag @@ -257,7 +257,7 @@ to 1 and ``-bb`` sets :c:data:`Py_BytesWarningFlag` to 2. .. availability:: Windows. - .. deprecated-removed:: 3.12 3.15 + .. deprecated-removed:: 3.12 3.16 .. c:var:: int Py_NoSiteFlag @@ -273,7 +273,7 @@ to 1 and ``-bb`` sets :c:data:`Py_BytesWarningFlag` to 2. Set by the :option:`-S` option. - .. deprecated-removed:: 3.12 3.15 + .. deprecated-removed:: 3.12 3.16 .. c:var:: int Py_NoUserSiteDirectory @@ -288,7 +288,7 @@ to 1 and ``-bb`` sets :c:data:`Py_BytesWarningFlag` to 2. Set by the :option:`-s` and :option:`-I` options, and the :envvar:`PYTHONNOUSERSITE` environment variable. - .. deprecated-removed:: 3.12 3.15 + .. deprecated-removed:: 3.12 3.16 .. c:var:: int Py_OptimizeFlag @@ -300,7 +300,7 @@ to 1 and ``-bb`` sets :c:data:`Py_BytesWarningFlag` to 2. Set by the :option:`-O` option and the :envvar:`PYTHONOPTIMIZE` environment variable. - .. deprecated-removed:: 3.12 3.15 + .. deprecated-removed:: 3.12 3.16 .. c:var:: int Py_QuietFlag @@ -315,7 +315,7 @@ to 1 and ``-bb`` sets :c:data:`Py_BytesWarningFlag` to 2. .. versionadded:: 3.2 - .. deprecated-removed:: 3.12 3.15 + .. deprecated-removed:: 3.12 3.16 .. c:var:: int Py_UnbufferedStdioFlag @@ -329,7 +329,7 @@ to 1 and ``-bb`` sets :c:data:`Py_BytesWarningFlag` to 2. Set by the :option:`-u` option and the :envvar:`PYTHONUNBUFFERED` environment variable. - .. deprecated-removed:: 3.12 3.15 + .. deprecated-removed:: 3.12 3.16 .. c:var:: int Py_VerboseFlag @@ -346,7 +346,7 @@ to 1 and ``-bb`` sets :c:data:`Py_BytesWarningFlag` to 2. Set by the :option:`-v` option and the :envvar:`PYTHONVERBOSE` environment variable. - .. deprecated-removed:: 3.12 3.15 + .. deprecated-removed:: 3.12 3.16 Initializing and finalizing the interpreter @@ -804,7 +804,7 @@ Process-wide parameters Use :c:func:`Py_DecodeLocale` to decode a bytes string to get a :c:expr:`wchar_t*` string. - .. deprecated-removed:: 3.11 3.15 + .. deprecated-removed:: 3.11 3.16 .. c:function:: const char* Py_GetVersion() @@ -929,7 +929,7 @@ Process-wide parameters .. versionadded:: 3.1.3 - .. deprecated-removed:: 3.11 3.15 + .. deprecated-removed:: 3.11 3.16 .. c:function:: void PySys_SetArgv(int argc, wchar_t **argv) @@ -950,7 +950,7 @@ Process-wide parameters .. versionchanged:: 3.4 The *updatepath* value depends on :option:`-I`. - .. deprecated-removed:: 3.11 3.15 + .. deprecated-removed:: 3.11 3.16 .. c:function:: void Py_SetPythonHome(const wchar_t *home) @@ -971,4 +971,4 @@ Process-wide parameters Use :c:func:`Py_DecodeLocale` to decode a bytes string to get a :c:expr:`wchar_t*` string. - .. deprecated-removed:: 3.11 3.15 + .. deprecated-removed:: 3.11 3.16 diff --git a/Doc/c-api/unicode.rst b/Doc/c-api/unicode.rst index 401c99ebeb0fec6..634dcbce7a57915 100644 --- a/Doc/c-api/unicode.rst +++ b/Doc/c-api/unicode.rst @@ -1953,7 +1953,7 @@ The following API is deprecated. whether you selected a "narrow" or "wide" Unicode version of Python at build time. - .. deprecated-removed:: 3.13 3.15 + .. deprecated-removed:: 3.13 3.16 .. c:function:: int PyUnicode_READY(PyObject *unicode) diff --git a/Doc/deprecations/c-api-pending-removal-in-3.15.rst b/Doc/deprecations/c-api-pending-removal-in-3.15.rst index 789ec83d2d957aa..6d0d47403ff2ed4 100644 --- a/Doc/deprecations/c-api-pending-removal-in-3.15.rst +++ b/Doc/deprecations/c-api-pending-removal-in-3.15.rst @@ -46,88 +46,3 @@ Pending removal in Python 3.15 The `pythoncapi-compat project `__ can be used to get :c:func:`PyConfig_Get` on Python 3.13 and older. - -* Functions to configure Python's initialization, deprecated in Python 3.11: - - * :c:func:`!PySys_SetArgvEx()`: - Set :c:member:`PyConfig.argv` instead. - * :c:func:`!PySys_SetArgv()`: - Set :c:member:`PyConfig.argv` instead. - * :c:func:`!Py_SetProgramName()`: - Set :c:member:`PyConfig.program_name` instead. - * :c:func:`!Py_SetPythonHome()`: - Set :c:member:`PyConfig.home` instead. - * :c:func:`!PySys_ResetWarnOptions`: - Clear :data:`sys.warnoptions` and :data:`!warnings.filters` instead. - - The :c:func:`Py_InitializeFromConfig` API should be used with - :c:type:`PyConfig` instead. - -* Global configuration variables: - - * :c:var:`Py_DebugFlag`: - Use :c:member:`PyConfig.parser_debug` or - :c:func:`PyConfig_Get("parser_debug") ` instead. - * :c:var:`Py_VerboseFlag`: - Use :c:member:`PyConfig.verbose` or - :c:func:`PyConfig_Get("verbose") ` instead. - * :c:var:`Py_QuietFlag`: - Use :c:member:`PyConfig.quiet` or - :c:func:`PyConfig_Get("quiet") ` instead. - * :c:var:`Py_InteractiveFlag`: - Use :c:member:`PyConfig.interactive` or - :c:func:`PyConfig_Get("interactive") ` instead. - * :c:var:`Py_InspectFlag`: - Use :c:member:`PyConfig.inspect` or - :c:func:`PyConfig_Get("inspect") ` instead. - * :c:var:`Py_OptimizeFlag`: - Use :c:member:`PyConfig.optimization_level` or - :c:func:`PyConfig_Get("optimization_level") ` instead. - * :c:var:`Py_NoSiteFlag`: - Use :c:member:`PyConfig.site_import` or - :c:func:`PyConfig_Get("site_import") ` instead. - * :c:var:`Py_BytesWarningFlag`: - Use :c:member:`PyConfig.bytes_warning` or - :c:func:`PyConfig_Get("bytes_warning") ` instead. - * :c:var:`Py_FrozenFlag`: - Use :c:member:`PyConfig.pathconfig_warnings` or - :c:func:`PyConfig_Get("pathconfig_warnings") ` instead. - * :c:var:`Py_IgnoreEnvironmentFlag`: - Use :c:member:`PyConfig.use_environment` or - :c:func:`PyConfig_Get("use_environment") ` instead. - * :c:var:`Py_DontWriteBytecodeFlag`: - Use :c:member:`PyConfig.write_bytecode` or - :c:func:`PyConfig_Get("write_bytecode") ` instead. - * :c:var:`Py_NoUserSiteDirectory`: - Use :c:member:`PyConfig.user_site_directory` or - :c:func:`PyConfig_Get("user_site_directory") ` instead. - * :c:var:`Py_UnbufferedStdioFlag`: - Use :c:member:`PyConfig.buffered_stdio` or - :c:func:`PyConfig_Get("buffered_stdio") ` instead. - * :c:var:`Py_HashRandomizationFlag`: - Use :c:member:`PyConfig.use_hash_seed` - and :c:member:`PyConfig.hash_seed` or - :c:func:`PyConfig_Get("hash_seed") ` instead. - * :c:var:`Py_IsolatedFlag`: - Use :c:member:`PyConfig.isolated` or - :c:func:`PyConfig_Get("isolated") ` instead. - * :c:var:`Py_LegacyWindowsFSEncodingFlag`: - Use :c:member:`PyPreConfig.legacy_windows_fs_encoding` or - :c:func:`PyConfig_Get("legacy_windows_fs_encoding") ` instead. - * :c:var:`Py_LegacyWindowsStdioFlag`: - Use :c:member:`PyConfig.legacy_windows_stdio` or - :c:func:`PyConfig_Get("legacy_windows_stdio") ` instead. - * :c:var:`!Py_FileSystemDefaultEncoding`, :c:var:`!Py_HasFileSystemDefaultEncoding`: - Use :c:member:`PyConfig.filesystem_encoding` or - :c:func:`PyConfig_Get("filesystem_encoding") ` instead. - * :c:var:`!Py_FileSystemDefaultEncodeErrors`: - Use :c:member:`PyConfig.filesystem_errors` or - :c:func:`PyConfig_Get("filesystem_errors") ` instead. - * :c:var:`!Py_UTF8Mode`: - Use :c:member:`PyPreConfig.utf8_mode` or - :c:func:`PyConfig_Get("utf8_mode") ` instead. - (see :c:func:`Py_PreInitialize`) - - The :c:func:`Py_InitializeFromConfig` API should be used with - :c:type:`PyConfig` to set these options. Or :c:func:`PyConfig_Get` can be - used to get these options at runtime. diff --git a/Doc/deprecations/c-api-pending-removal-in-3.16.rst b/Doc/deprecations/c-api-pending-removal-in-3.16.rst index 9453f83799c43d7..fe2d91cf316b18f 100644 --- a/Doc/deprecations/c-api-pending-removal-in-3.16.rst +++ b/Doc/deprecations/c-api-pending-removal-in-3.16.rst @@ -1,4 +1,87 @@ Pending removal in Python 3.16 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -* The bundled copy of ``libmpdec``. +* Functions to configure Python's initialization, deprecated in Python 3.11: + + * :c:func:`!PySys_SetArgvEx()`: + Set :c:member:`PyConfig.argv` instead. + * :c:func:`!PySys_SetArgv()`: + Set :c:member:`PyConfig.argv` instead. + * :c:func:`!Py_SetProgramName()`: + Set :c:member:`PyConfig.program_name` instead. + * :c:func:`!Py_SetPythonHome()`: + Set :c:member:`PyConfig.home` instead. + + The :c:func:`Py_InitializeFromConfig` API should be used with + :c:type:`PyConfig` instead. + +* Global configuration variables: + + * :c:var:`Py_DebugFlag`: + Use :c:member:`PyConfig.parser_debug` or + :c:func:`PyConfig_Get("parser_debug") ` instead. + * :c:var:`Py_VerboseFlag`: + Use :c:member:`PyConfig.verbose` or + :c:func:`PyConfig_Get("verbose") ` instead. + * :c:var:`Py_QuietFlag`: + Use :c:member:`PyConfig.quiet` or + :c:func:`PyConfig_Get("quiet") ` instead. + * :c:var:`Py_InteractiveFlag`: + Use :c:member:`PyConfig.interactive` or + :c:func:`PyConfig_Get("interactive") ` instead. + * :c:var:`Py_InspectFlag`: + Use :c:member:`PyConfig.inspect` or + :c:func:`PyConfig_Get("inspect") ` instead. + * :c:var:`Py_OptimizeFlag`: + Use :c:member:`PyConfig.optimization_level` or + :c:func:`PyConfig_Get("optimization_level") ` instead. + * :c:var:`Py_NoSiteFlag`: + Use :c:member:`PyConfig.site_import` or + :c:func:`PyConfig_Get("site_import") ` instead. + * :c:var:`Py_BytesWarningFlag`: + Use :c:member:`PyConfig.bytes_warning` or + :c:func:`PyConfig_Get("bytes_warning") ` instead. + * :c:var:`Py_FrozenFlag`: + Use :c:member:`PyConfig.pathconfig_warnings` or + :c:func:`PyConfig_Get("pathconfig_warnings") ` instead. + * :c:var:`Py_IgnoreEnvironmentFlag`: + Use :c:member:`PyConfig.use_environment` or + :c:func:`PyConfig_Get("use_environment") ` instead. + * :c:var:`Py_DontWriteBytecodeFlag`: + Use :c:member:`PyConfig.write_bytecode` or + :c:func:`PyConfig_Get("write_bytecode") ` instead. + * :c:var:`Py_NoUserSiteDirectory`: + Use :c:member:`PyConfig.user_site_directory` or + :c:func:`PyConfig_Get("user_site_directory") ` instead. + * :c:var:`Py_UnbufferedStdioFlag`: + Use :c:member:`PyConfig.buffered_stdio` or + :c:func:`PyConfig_Get("buffered_stdio") ` instead. + * :c:var:`Py_HashRandomizationFlag`: + Use :c:member:`PyConfig.use_hash_seed` + and :c:member:`PyConfig.hash_seed` or + :c:func:`PyConfig_Get("hash_seed") ` instead. + * :c:var:`Py_IsolatedFlag`: + Use :c:member:`PyConfig.isolated` or + :c:func:`PyConfig_Get("isolated") ` instead. + * :c:var:`Py_LegacyWindowsFSEncodingFlag`: + Use :c:member:`PyPreConfig.legacy_windows_fs_encoding` or + :c:func:`PyConfig_Get("legacy_windows_fs_encoding") ` instead. + * :c:var:`Py_LegacyWindowsStdioFlag`: + Use :c:member:`PyConfig.legacy_windows_stdio` or + :c:func:`PyConfig_Get("legacy_windows_stdio") ` instead. + * :c:var:`!Py_FileSystemDefaultEncoding`, :c:var:`!Py_HasFileSystemDefaultEncoding`: + Use :c:member:`PyConfig.filesystem_encoding` or + :c:func:`PyConfig_Get("filesystem_encoding") ` instead. + * :c:var:`!Py_FileSystemDefaultEncodeErrors`: + Use :c:member:`PyConfig.filesystem_errors` or + :c:func:`PyConfig_Get("filesystem_errors") ` instead. + * :c:var:`!Py_UTF8Mode`: + Use :c:member:`PyPreConfig.utf8_mode` or + :c:func:`PyConfig_Get("utf8_mode") ` instead. + (see :c:func:`Py_PreInitialize`) + + The :c:func:`Py_InitializeFromConfig` API should be used with + :c:type:`PyConfig` to set these options. Or :c:func:`PyConfig_Get` can be + used to get these options at runtime. + +* :c:type:`Py_UNICODE` which was deprecated by :pep:`393`. diff --git a/Doc/deprecations/index.rst b/Doc/deprecations/index.rst index 50e1b126fd1346d..0ac0a0289d5fd40 100644 --- a/Doc/deprecations/index.rst +++ b/Doc/deprecations/index.rst @@ -1,8 +1,6 @@ Deprecations ============ -.. include:: pending-removal-in-3.15.rst - .. include:: pending-removal-in-3.16.rst .. include:: pending-removal-in-3.17.rst @@ -22,8 +20,6 @@ Deprecations C API deprecations ------------------ -.. include:: c-api-pending-removal-in-3.15.rst - .. include:: c-api-pending-removal-in-3.16.rst .. include:: c-api-pending-removal-in-3.18.rst diff --git a/Doc/deprecations/pending-removal-in-3.15.rst b/Doc/deprecations/pending-removal-in-3.15.rst index ac4014da52b9a48..5c0e592f4caeda1 100644 --- a/Doc/deprecations/pending-removal-in-3.15.rst +++ b/Doc/deprecations/pending-removal-in-3.15.rst @@ -8,11 +8,6 @@ Pending removal in Python 3.15 is deprecated. In Python 3.15, ``__cached__`` will cease to be set or take into consideration by the import system or standard library. (:gh:`97879`) - * Setting :attr:`~module.__package__` on a module while - failing to set :attr:`__spec__.parent ` - is deprecated. In Python 3.15, :attr:`!__package__` will cease to be set or - take into consideration by the import system or standard library. (:gh:`97879`) - * :mod:`ctypes`: * The undocumented :func:`!ctypes.SetPointerType` function diff --git a/Doc/deprecations/pending-removal-in-3.16.rst b/Doc/deprecations/pending-removal-in-3.16.rst index 7606daba16dcf82..5a28cc766a95961 100644 --- a/Doc/deprecations/pending-removal-in-3.16.rst +++ b/Doc/deprecations/pending-removal-in-3.16.rst @@ -8,6 +8,13 @@ Pending removal in Python 3.16 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. + * Setting :attr:`~module.__package__` on a module while + failing to set :attr:`__spec__.parent ` + is deprecated. In Python 3.16, :attr:`!__package__` will cease to be + taken into consideration by the import system or standard library. (:gh:`97879`) + +* The bundled copy of ``libmpdec``. + * :mod:`array`: * The ``'u'`` format code (:c:type:`wchar_t`) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index dd8f3e33128a97f..8d1704a448e6394 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -1042,7 +1042,7 @@ this approach. Raise :exc:`DeprecationWarning` instead of :exc:`ImportWarning` when falling back to :attr:`!__package__` during import resolution. - .. deprecated-removed:: 3.13 3.15 + .. deprecated-removed:: 3.13 3.16 :attr:`!__package__` will cease to be set or taken into consideration by the import system or standard library. diff --git a/Doc/tools/removed-ids.txt b/Doc/tools/removed-ids.txt index be00d48bf042f63..05fc89d9fe2d278 100644 --- a/Doc/tools/removed-ids.txt +++ b/Doc/tools/removed-ids.txt @@ -4,7 +4,10 @@ c-api/allocation.html: deprecated-aliases c-api/file.html: deprecated-api +# Removed sections library/asyncio-task.html: terminating-a-task-group +deprecations/index.html: pending-removal-in-python-3-15 +deprecations/index.html: c-api-pending-removal-in-python-3-15 # Removed libmpdec using/configure.html: cmdoption-with-system-libmpdec From bb4cd7955fb0ffe8e87c611a0f5e5d6febd384db Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Wed, 3 Jun 2026 17:00:51 -0700 Subject: [PATCH 012/185] gh-145177: Fix Emscripten help text (#150874) Removes some stray commas in help text. --- Platforms/emscripten/__main__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Platforms/emscripten/__main__.py b/Platforms/emscripten/__main__.py index c1eac8005474fda..c2fb1c4c36e6087 100644 --- a/Platforms/emscripten/__main__.py +++ b/Platforms/emscripten/__main__.py @@ -650,7 +650,7 @@ def add_cross_build_dir_option(subcommand): help=( "Path to the cross-build directory " f"(default: {DEFAULT_CROSS_BUILD_DIR}). " - "Can also be set with the CROSS_BUILD_DIR environment variable.", + "Can also be set with the CROSS_BUILD_DIR environment variable." ), ) @@ -743,7 +743,7 @@ def main(): nargs=argparse.REMAINDER, help=( "Arguments to pass to the emscripten Python " - "(use '--' to separate from run options)", + "(use '--' to separate from run options)" ), ) add_cross_build_dir_option(run) From 445d45cdfa349654604d0c9a2f531e97b48cf5e4 Mon Sep 17 00:00:00 2001 From: Stefano Rivera Date: Thu, 4 Jun 2026 00:35:04 -0700 Subject: [PATCH 013/185] Manpage formatting tweaks & lint (GH-132338) Following on from GH-132291 this is the second part of the patch from https://bugs.debian.org/1101406 This tweaks the formatting of a few bits of the manpage. --- Misc/python.man | 80 +++++++++++++++++++++++++++++-------------------- 1 file changed, 47 insertions(+), 33 deletions(-) diff --git a/Misc/python.man b/Misc/python.man index a65fb98a697b503..5e4f3c3debe9358 100644 --- a/Misc/python.man +++ b/Misc/python.man @@ -1,4 +1,4 @@ -.TH PYTHON "1" +.TH PYTHON 1 .\" To view this file while editing, run it through groff: .\" groff -Tascii -man python.man | less @@ -162,7 +162,7 @@ compilation options). Ignore environment variables like PYTHONPATH and PYTHONHOME that modify the behavior of the interpreter. .TP -.B \-h ", " \-? ", "\-\-help +.BR \-h ", " \-? ", " \-\-help Prints the usage for the interpreter executable and exits. .TP .B "\-\-help\-env" @@ -171,7 +171,6 @@ Prints help about Python-specific environment variables and exits. .B "\-\-help\-xoptions" Prints help about implementation-specific \fB\-X\fP options and exits. .TP -.TP .B "\-\-help\-all" Prints complete usage information and exits. .TP @@ -243,7 +242,7 @@ twice, print a message for each file that is checked for when searching for a module. Also provides information on module cleanup at exit. .TP -.B \-V ", " \-\-version +.BR \-V ", " \-\-version Prints the Python version number of the executable and exits. When given twice, print more information about the build. @@ -255,23 +254,38 @@ to The simplest settings apply a particular action unconditionally to all warnings emitted by a process (even those that are otherwise ignored by default): - - -Wdefault # Warn once per call location - -Werror # Convert to exceptions - -Walways # Warn every time - -Wall # Same as -Walways - -Wmodule # Warn once per calling module - -Wonce # Warn once per Python process - -Wignore # Never warn - +.RS +.TP +.B \-Wdefault +Warn once per call location +.TP +.B \-Werror +Convert to exceptions +.TP +.B \-Walways +Warn every time +.TP +.B \-Wall +Same as \-Walways +.TP +.B \-Wmodule +Warn once per calling module +.TP +.B \-Wonce +Warn once per Python process +.TP +.B \-Wignore +Never warn +.RE +.IP The action names can be abbreviated as desired and the interpreter will resolve them to the appropriate action name. For example, .B \-Wi is the same as -.B \-Wignore . +.BR \-Wignore . The full form of argument is: -.IB action:message:category:module:lineno +.IB action : message : category : module : lineno Empty fields match all values; trailing empty fields may be omitted. For example @@ -457,7 +471,7 @@ is an empty string; if is used, .I sys.argv[0] contains the string -.I '\-c'. +.RI ' \-c '. Note that options interpreted by the Python interpreter itself are not placed in .IR sys.argv . @@ -531,12 +545,12 @@ the \fB\-d\fP option. If set to an integer, it is equivalent to specifying \fB\-d\fP multiple times. .IP PYTHONEXECUTABLE If this environment variable is set, -.IB sys.argv[0] +.I sys.argv[0] will be set to its value instead of the value got through the C runtime. Only works on Mac OS X. .IP PYTHONFAULTHANDLER If this environment variable is set to a non-empty string, -.IR faulthandler.enable() +.I faulthandler.enable() is called at startup: install a handler for SIGSEGV, SIGFPE, SIGABRT, SIGBUS and SIGILL signals to dump the Python traceback. .IP @@ -584,8 +598,8 @@ purpose is to allow repeatable hashing, such as for selftests for the interpreter itself, or to allow a cluster of python processes to share hash values. -The integer must be a decimal number in the range [0,4294967295]. Specifying -the value 0 will disable hash randomization. +The integer must be a decimal number in the range [0,4\|294\|967\|295]. +Specifying the value 0 will disable hash randomization. .IP PYTHONHOME Change the location of the standard Python libraries. By default, the libraries are searched in ${prefix}/lib/python and @@ -607,16 +621,16 @@ This is equivalent to the \fB\-X int_max_str_digits=\fINUMBER\fR option. .IP PYTHONIOENCODING If this is set before running the interpreter, it overrides the encoding used for stdin/stdout/stderr, in the syntax -.IB encodingname ":" errorhandler +.IB encodingname : errorhandler The -.IB errorhandler +.I errorhandler part is optional and has the same meaning as in str.encode. For stderr, the -.IB errorhandler +.I errorhandler part is ignored; the handler will always be \'backslashreplace\'. .IP PYTHONMALLOC Set the Python memory allocators and/or install debug hooks. The available memory allocators are -.IR malloc +.I malloc and .IR pymalloc . The available debug hooks are @@ -626,7 +640,7 @@ and .IR pymalloc_debug . .IP When Python is compiled in debug mode, the default is -.IR pymalloc_debug +.I pymalloc_debug and the debug hooks are automatically used. Otherwise, the default is .IR pymalloc . .IP PYTHONMALLOCSTATS @@ -707,14 +721,14 @@ Python memory allocations using the tracemalloc module. .IP The value of the variable is the maximum number of frames stored in a traceback of a trace. For example, -.IB PYTHONTRACEMALLOC=1 +.I PYTHONTRACEMALLOC=1 stores only the most recent frame. .IP PYTHONUNBUFFERED If this is set to a non-empty string it is equivalent to specifying the \fB\-u\fP option. .IP PYTHONUSERBASE Defines the user base directory, which is used to compute the path of the user -.IR site\-packages +.I site\-packages directory and installation paths for .IR "python \-m pip install \-\-user" . .IP PYTHONUTF8 @@ -750,17 +764,17 @@ This is equivalent to the \fB\-X presite=\fImodule\fR option. .SH AUTHOR The Python Software Foundation: https://www.python.org/psf/ .SH INTERNET RESOURCES -Main website: https://www.python.org/ +Main website: https://www.python.org/ .br -Documentation: https://docs.python.org/ +Documentation: https://docs.python.org/ .br -Developer resources: https://devguide.python.org/ +Developer resources: https://devguide.python.org/ .br -Downloads: https://www.python.org/downloads/ +Downloads: https://www.python.org/downloads/ .br -Module repository: https://pypi.org/ +Module repository: https://pypi.org/ .br -Newsgroups: comp.lang.python, comp.lang.python.announce +Newsgroups: comp.lang.python, comp.lang.python.announce .SH LICENSING Python is distributed under an Open Source license. See the file "LICENSE" in the Python source distribution for information on terms & From e334aa168d56e160b2fced6f9df61b1d1c3a075b Mon Sep 17 00:00:00 2001 From: jb2170 Date: Thu, 4 Jun 2026 10:06:02 +0100 Subject: [PATCH 014/185] gh-119670: Add `force` keyword only argument to `shlex.quote` (#148846) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are propositions to add a single-quote-double-quote switch (gh-90630), so to avoid hiccups of people passing `force` as a positional and it being used for the single-double switch, we make kwargs kwargs-only. Co-authored-by: Bartosz Sławecki --- Doc/library/shlex.rst | 20 ++++++++++++++++++- Doc/whatsnew/3.16.rst | 7 +++++++ Lib/shlex.py | 14 +++++++++---- Lib/test/test_shlex.py | 8 ++++++++ ...-04-21-06-30-59.gh-issue-119670.pMWZfY.rst | 2 ++ 5 files changed, 46 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-04-21-06-30-59.gh-issue-119670.pMWZfY.rst diff --git a/Doc/library/shlex.rst b/Doc/library/shlex.rst index 2ab12f2f6f9169e..2dfb0246d5d90c0 100644 --- a/Doc/library/shlex.rst +++ b/Doc/library/shlex.rst @@ -44,12 +44,15 @@ The :mod:`!shlex` module defines the following functions: .. versionadded:: 3.8 -.. function:: quote(s) +.. function:: quote(s, *, force=False) Return a shell-escaped version of the string *s*. The returned value is a string that can safely be used as one token in a shell command line, for cases where you cannot use a list. + If *force* is :const:`True`, then *s* is unconditionally quoted, + even if it is already safe for a shell without being quoted. + .. _shlex-quote-warning: .. warning:: @@ -91,8 +94,23 @@ The :mod:`!shlex` module defines the following functions: >>> command ['ls', '-l', 'somefile; rm -rf ~'] + The *force* keyword can be used to produce consistent behavior when + escaping multiple strings: + + >>> from shlex import quote + >>> filenames = ['my first file', 'file2', 'file 3'] + >>> filenames_some_escaped = [quote(f) for f in filenames] + >>> filenames_some_escaped + ["'my first file'", 'file2', "'file 3'"] + >>> filenames_all_escaped = [quote(f, force=True) for f in filenames] + >>> filenames_all_escaped + ["'my first file'", "'file2'", "'file 3'"] + .. versionadded:: 3.3 + .. versionchanged:: next + The *force* keyword was added. + The :mod:`!shlex` module defines the following class: diff --git a/Doc/whatsnew/3.16.rst b/Doc/whatsnew/3.16.rst index 0aff48dba61449c..a055113dec0494c 100644 --- a/Doc/whatsnew/3.16.rst +++ b/Doc/whatsnew/3.16.rst @@ -109,6 +109,13 @@ os process via a pidfd. Available on Linux 5.6+. (Contributed by Maurycy Pawłowski-Wieroński in :gh:`149464`.) +shlex +----- + +* Add keyword-only parameter *force* to :func:`shlex.quote` to force quoting + a string, even if it is already safe for a shell without being quoted. + (Contributed by Jay Berry in :gh:`148846`.) + xml --- diff --git a/Lib/shlex.py b/Lib/shlex.py index 5959f52dd12639d..c7ffc918d53961c 100644 --- a/Lib/shlex.py +++ b/Lib/shlex.py @@ -317,8 +317,12 @@ def join(split_command): return ' '.join(quote(arg) for arg in split_command) -def quote(s): - """Return a shell-escaped version of the string *s*.""" +def quote(s, *, force=False): + """Return a shell-escaped version of the string *s*. + + If *force* is *True*, then *s* is unconditionally quoted, + even if it is already safe for a shell without being quoted. + """ if not s: return "''" @@ -329,8 +333,10 @@ def quote(s): safe_chars = (b'%+,-./0123456789:=@' b'ABCDEFGHIJKLMNOPQRSTUVWXYZ_' b'abcdefghijklmnopqrstuvwxyz') - # No quoting is needed if `s` is an ASCII string consisting only of `safe_chars` - if s.isascii() and not s.encode().translate(None, delete=safe_chars): + # No quoting is needed if we are not forcing quoting + # and `s` is an ASCII string consisting only of `safe_chars`. + if (not force + and s.isascii() and not s.encode().translate(None, delete=safe_chars)): return s # use single quotes, and put single quotes into double quotes diff --git a/Lib/test/test_shlex.py b/Lib/test/test_shlex.py index 2a355abdeeb30fb..2adaee81b063085 100644 --- a/Lib/test/test_shlex.py +++ b/Lib/test/test_shlex.py @@ -342,6 +342,14 @@ def testQuote(self): self.assertRaises(TypeError, shlex.quote, 42) self.assertRaises(TypeError, shlex.quote, b"abc") + def testForceQuote(self): + self.assertEqual(shlex.quote("spam"), "spam") + self.assertEqual(shlex.quote("spam", force=False), "spam") + self.assertEqual(shlex.quote("spam", force=True), "'spam'") + self.assertEqual(shlex.quote("spam eggs", force=False), "'spam eggs'") + self.assertEqual(shlex.quote("spam eggs", force=True), "'spam eggs'") + self.assertEqual(shlex.quote("two's-complement", force=False), "'two'\"'\"'s-complement'") + def testJoin(self): for split_command, command in [ (['a ', 'b'], "'a ' b"), diff --git a/Misc/NEWS.d/next/Library/2026-04-21-06-30-59.gh-issue-119670.pMWZfY.rst b/Misc/NEWS.d/next/Library/2026-04-21-06-30-59.gh-issue-119670.pMWZfY.rst new file mode 100644 index 000000000000000..fc1941be4dc7925 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-04-21-06-30-59.gh-issue-119670.pMWZfY.rst @@ -0,0 +1,2 @@ +Add keyword-only parameter *force* to :func:`shlex.quote` to force quoting +a string, even if it is already safe for a shell without being quoted. From d9166afe23a3914a71012991a936d9fed9245003 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Thu, 4 Jun 2026 11:42:06 +0200 Subject: [PATCH 015/185] Fix 2 broken links in documentation (#150892) Co-authored-by: Stan Ulbrych --- Doc/c-api/complex.rst | 2 +- Doc/extending/first-extension-module.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/c-api/complex.rst b/Doc/c-api/complex.rst index 629312bd771beb2..10f96c7cb75e882 100644 --- a/Doc/c-api/complex.rst +++ b/Doc/c-api/complex.rst @@ -130,7 +130,7 @@ rather than dereferencing them through pointers. Please note, that these functions are :term:`soft deprecated` since Python 3.15. Avoid using this API in a new code to do complex arithmetic: either use -the `Number Protocol `_ API or use native complex types, like +the :ref:`Number Protocol ` API or use native complex types, like :c:expr:`double complex`. diff --git a/Doc/extending/first-extension-module.rst b/Doc/extending/first-extension-module.rst index 894f5bdbb8f09c2..55a772e2aca24f5 100644 --- a/Doc/extending/first-extension-module.rst +++ b/Doc/extending/first-extension-module.rst @@ -164,7 +164,7 @@ Then, create ``meson.build`` containing the following: .. note:: - See `meson-python documentation `_ for details on + See the `meson-python documentation `_ for details on configuration. Now, build install the *project in the current directory* (``.``) via ``pip``: From de8db1f18170b91f3d0a54800c84088cfc013bd4 Mon Sep 17 00:00:00 2001 From: Batuhan Taskaya Date: Thu, 4 Jun 2026 13:58:51 +0300 Subject: [PATCH 016/185] bpo-38131: Improve messages when generating AST nodes from objects with wrong field values (GH-17715) --- Lib/test/test_ast/test_ast.py | 26 +- .../2019-12-27-19-20-33.bpo-38131.J5fv54.rst | 2 + Parser/asdl_c.py | 53 +- Python/Python-ast.c | 662 ++++++++++-------- 4 files changed, 437 insertions(+), 306 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2019-12-27-19-20-33.bpo-38131.J5fv54.rst diff --git a/Lib/test/test_ast/test_ast.py b/Lib/test/test_ast/test_ast.py index 0112c9163fd0cdb..fd3b33bab7f8336 100644 --- a/Lib/test/test_ast/test_ast.py +++ b/Lib/test/test_ast/test_ast.py @@ -625,7 +625,7 @@ def test_invalid_identifier(self): ast.fix_missing_locations(m) with self.assertRaises(TypeError) as cm: compile(m, "", "exec") - self.assertIn("identifier must be of type str", str(cm.exception)) + self.assertIn("expecting a string object", str(cm.exception)) def test_invalid_constant(self): for invalid_constant in int, (1, 2, int), frozenset((1, 2, int)): @@ -1081,6 +1081,30 @@ def test_none_checks(self) -> None: for node, attr, source in tests: self.assert_none_check(node, attr, source) + def test_required_field_messages(self): + binop = ast.BinOp( + left=ast.Constant(value=2), + right=ast.Constant(value=2), + op=ast.Add(), + ) + expr_without_position = ast.Expression(body=binop) + expr_with_wrong_body = ast.Expression(body=[binop]) + + with self.assertRaisesRegex(TypeError, "required field") as cm: + compile(expr_without_position, "", "eval") + with self.assertRaisesRegex( + TypeError, + "field 'body' was expecting node of type 'expr', got 'list'", + ): + compile(expr_with_wrong_body, "", "eval") + + constant = ast.parse("u'test'", mode="eval") + constant.body.kind = 0xFF + with self.assertRaisesRegex( + TypeError, "field 'kind' was expecting a string or bytes object" + ): + compile(constant, "", "eval") + def test_repr(self) -> None: snapshots = AST_REPR_DATA_FILE.read_text().split("\n") for test, snapshot in zip(ast_repr_get_test_cases(), snapshots, strict=True): diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2019-12-27-19-20-33.bpo-38131.J5fv54.rst b/Misc/NEWS.d/next/Core_and_Builtins/2019-12-27-19-20-33.bpo-38131.J5fv54.rst new file mode 100644 index 000000000000000..8b606c4335e033a --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2019-12-27-19-20-33.bpo-38131.J5fv54.rst @@ -0,0 +1,2 @@ +Produce more meaningful messages when compiling AST objects with wrong field +values. Patch by Batuhan Taskaya. diff --git a/Parser/asdl_c.py b/Parser/asdl_c.py index ed1d2e08df8f400..e2a57177d20afb7 100755 --- a/Parser/asdl_c.py +++ b/Parser/asdl_c.py @@ -487,7 +487,7 @@ def visitField(self, sum): class Obj2ModPrototypeVisitor(PickleVisitor): def visitProduct(self, prod, name): - code = "static int obj2ast_%s(struct ast_state *state, PyObject* obj, %s* out, PyArena* arena);" + code = "static int obj2ast_%s(struct ast_state *state, PyObject* obj, %s* out, const char* field, PyArena* arena);" self.emit(code % (name, get_c_type(name)), 0) visitSum = visitProduct @@ -511,7 +511,7 @@ def recursive_call(self, node, level): def funcHeader(self, name): ctype = get_c_type(name) self.emit("int", 0) - self.emit("obj2ast_%s(struct ast_state *state, PyObject* obj, %s* out, PyArena* arena)" % (name, ctype), 0) + self.emit("obj2ast_%s(struct ast_state *state, PyObject* obj, %s* out, const char* field, PyArena* arena)" % (name, ctype), 0) self.emit("{", 0) self.emit("int isinstance;", 1) self.emit("", 0) @@ -547,6 +547,18 @@ def simpleSum(self, sum, name): def buildArgs(self, fields): return ", ".join(fields + ["arena"]) + def typeCheck(self, name): + self.emit("tp = state->%s_type;" % name, 1) + self.emit("isinstance = PyObject_IsInstance(obj, tp);", 1) + self.emit("if (isinstance == -1) {", 1) + self.emit("return 1;", 2) + self.emit("}", 1) + self.emit("if (!isinstance && field != NULL) {", 1) + error = "field '%%s' was expecting node of type '%s', got '%%s'" % name + self.emit("PyErr_Format(PyExc_TypeError, \"%s\", field, _PyType_Name(Py_TYPE(obj)));" % error, 2, reflow=False) + self.emit("return 1;", 2) + self.emit("}", 1) + def complexSum(self, sum, name): self.funcHeader(name) self.emit("PyObject *tmp = NULL;", 1) @@ -559,6 +571,7 @@ def complexSum(self, sum, name): self.emit("*out = NULL;", 2) self.emit("return 0;", 2) self.emit("}", 1) + self.typeCheck(name) for a in sum.attributes: self.visitField(a, name, sum=sum, depth=1) for t in sum.types: @@ -593,7 +606,7 @@ def visitSum(self, sum, name): def visitProduct(self, prod, name): ctype = get_c_type(name) self.emit("int", 0) - self.emit("obj2ast_%s(struct ast_state *state, PyObject* obj, %s* out, PyArena* arena)" % (name, ctype), 0) + self.emit("obj2ast_%s(struct ast_state *state, PyObject* obj, %s* out, const char* field, PyArena* arena)" % (name, ctype), 0) self.emit("{", 0) self.emit("PyObject* tmp = NULL;", 1) for f in prod.fields: @@ -694,8 +707,8 @@ def visitField(self, field, name, sum=None, prod=None, depth=0): self.emit("%s val;" % ctype, depth+2) self.emit("PyObject *tmp2 = Py_NewRef(PyList_GET_ITEM(tmp, i));", depth+2) with self.recursive_call(name, depth+2): - self.emit("res = obj2ast_%s(state, tmp2, &val, arena);" % - field.type, depth+2, reflow=False) + self.emit("res = obj2ast_%s(state, tmp2, &val, \"%s\", arena);" % + (field.type, field.name), depth+2, reflow=False) self.emit("Py_DECREF(tmp2);", depth+2) self.emit("if (res != 0) goto failed;", depth+2) self.emit("if (len != PyList_GET_SIZE(tmp)) {", depth+2) @@ -709,8 +722,8 @@ def visitField(self, field, name, sum=None, prod=None, depth=0): self.emit("}", depth+1) else: with self.recursive_call(name, depth+1): - self.emit("res = obj2ast_%s(state, tmp, &%s, arena);" % - (field.type, field.name), depth+1) + self.emit("res = obj2ast_%s(state, tmp, &%s, \"%s\", arena);" % + (field.type, field.name, field.name), depth+1) self.emit("if (res != 0) goto failed;", depth+1) self.emit("Py_CLEAR(tmp);", depth+1) @@ -1701,7 +1714,9 @@ def visitModule(self, mod): /* Conversion Python -> AST */ -static int obj2ast_object(struct ast_state *Py_UNUSED(state), PyObject* obj, PyObject** out, PyArena* arena) +static int obj2ast_object(struct ast_state *Py_UNUSED(state), PyObject* obj, + PyObject** out, + const char* Py_UNUSED(field), PyArena* arena) { if (obj == Py_None) obj = NULL; @@ -1718,7 +1733,9 @@ def visitModule(self, mod): return 0; } -static int obj2ast_constant(struct ast_state *Py_UNUSED(state), PyObject* obj, PyObject** out, PyArena* arena) +static int obj2ast_constant(struct ast_state *Py_UNUSED(state), PyObject* obj, + PyObject** out, + const char* Py_UNUSED(field), PyArena* arena) { if (_PyArena_AddPyObject(arena, obj) < 0) { *out = NULL; @@ -1728,29 +1745,29 @@ def visitModule(self, mod): return 0; } -static int obj2ast_identifier(struct ast_state *state, PyObject* obj, PyObject** out, PyArena* arena) +static int obj2ast_identifier(struct ast_state *state, PyObject* obj, PyObject** out, const char* field, PyArena* arena) { if (!PyUnicode_CheckExact(obj) && obj != Py_None) { - PyErr_SetString(PyExc_TypeError, "AST identifier must be of type str"); + PyErr_Format(PyExc_TypeError, "field '%s' was expecting a string object", field); return -1; } - return obj2ast_object(state, obj, out, arena); + return obj2ast_object(state, obj, out, field, arena); } -static int obj2ast_string(struct ast_state *state, PyObject* obj, PyObject** out, PyArena* arena) +static int obj2ast_string(struct ast_state *state, PyObject* obj, PyObject** out, const char* field, PyArena* arena) { if (!PyUnicode_CheckExact(obj) && !PyBytes_CheckExact(obj)) { - PyErr_SetString(PyExc_TypeError, "AST string must be of type str"); + PyErr_Format(PyExc_TypeError, "field '%s' was expecting a string or bytes object", field); return -1; } - return obj2ast_object(state, obj, out, arena); + return obj2ast_object(state, obj, out, field, arena); } -static int obj2ast_int(struct ast_state* Py_UNUSED(state), PyObject* obj, int* out, PyArena* arena) +static int obj2ast_int(struct ast_state* Py_UNUSED(state), PyObject* obj, int* out, const char* field, PyArena* arena) { int i; if (!PyLong_Check(obj)) { - PyErr_Format(PyExc_ValueError, "invalid integer value: %R", obj); + PyErr_Format(PyExc_ValueError, "field \\"%s\\" got an invalid integer value: %R", field, obj); return -1; } @@ -2150,7 +2167,7 @@ class PartingShots(StaticVisitor): } mod_ty res = NULL; - if (obj2ast_mod(state, ast, &res, arena) != 0) + if (obj2ast_mod(state, ast, &res, NULL, arena) != 0) return NULL; else return res; diff --git a/Python/Python-ast.c b/Python/Python-ast.c index 5f3d9c4b17410fb..49b6bf1d12b6fab 100644 --- a/Python/Python-ast.c +++ b/Python/Python-ast.c @@ -6026,7 +6026,9 @@ static PyObject* ast2obj_int(struct ast_state *Py_UNUSED(state), long b) /* Conversion Python -> AST */ -static int obj2ast_object(struct ast_state *Py_UNUSED(state), PyObject* obj, PyObject** out, PyArena* arena) +static int obj2ast_object(struct ast_state *Py_UNUSED(state), PyObject* obj, + PyObject** out, + const char* Py_UNUSED(field), PyArena* arena) { if (obj == Py_None) obj = NULL; @@ -6043,7 +6045,9 @@ static int obj2ast_object(struct ast_state *Py_UNUSED(state), PyObject* obj, PyO return 0; } -static int obj2ast_constant(struct ast_state *Py_UNUSED(state), PyObject* obj, PyObject** out, PyArena* arena) +static int obj2ast_constant(struct ast_state *Py_UNUSED(state), PyObject* obj, + PyObject** out, + const char* Py_UNUSED(field), PyArena* arena) { if (_PyArena_AddPyObject(arena, obj) < 0) { *out = NULL; @@ -6053,29 +6057,29 @@ static int obj2ast_constant(struct ast_state *Py_UNUSED(state), PyObject* obj, P return 0; } -static int obj2ast_identifier(struct ast_state *state, PyObject* obj, PyObject** out, PyArena* arena) +static int obj2ast_identifier(struct ast_state *state, PyObject* obj, PyObject** out, const char* field, PyArena* arena) { if (!PyUnicode_CheckExact(obj) && obj != Py_None) { - PyErr_SetString(PyExc_TypeError, "AST identifier must be of type str"); + PyErr_Format(PyExc_TypeError, "field '%s' was expecting a string object", field); return -1; } - return obj2ast_object(state, obj, out, arena); + return obj2ast_object(state, obj, out, field, arena); } -static int obj2ast_string(struct ast_state *state, PyObject* obj, PyObject** out, PyArena* arena) +static int obj2ast_string(struct ast_state *state, PyObject* obj, PyObject** out, const char* field, PyArena* arena) { if (!PyUnicode_CheckExact(obj) && !PyBytes_CheckExact(obj)) { - PyErr_SetString(PyExc_TypeError, "AST string must be of type str"); + PyErr_Format(PyExc_TypeError, "field '%s' was expecting a string or bytes object", field); return -1; } - return obj2ast_object(state, obj, out, arena); + return obj2ast_object(state, obj, out, field, arena); } -static int obj2ast_int(struct ast_state* Py_UNUSED(state), PyObject* obj, int* out, PyArena* arena) +static int obj2ast_int(struct ast_state* Py_UNUSED(state), PyObject* obj, int* out, const char* field, PyArena* arena) { int i; if (!PyLong_Check(obj)) { - PyErr_Format(PyExc_ValueError, "invalid integer value: %R", obj); + PyErr_Format(PyExc_ValueError, "field \"%s\" got an invalid integer value: %R", field, obj); return -1; } @@ -6954,43 +6958,52 @@ init_types(void *arg) } static int obj2ast_mod(struct ast_state *state, PyObject* obj, mod_ty* out, - PyArena* arena); + const char* field, PyArena* arena); static int obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, - PyArena* arena); + const char* field, PyArena* arena); static int obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, - PyArena* arena); + const char* field, PyArena* arena); static int obj2ast_expr_context(struct ast_state *state, PyObject* obj, - expr_context_ty* out, PyArena* arena); + expr_context_ty* out, const char* field, + PyArena* arena); static int obj2ast_boolop(struct ast_state *state, PyObject* obj, boolop_ty* - out, PyArena* arena); + out, const char* field, PyArena* arena); static int obj2ast_operator(struct ast_state *state, PyObject* obj, - operator_ty* out, PyArena* arena); + operator_ty* out, const char* field, PyArena* + arena); static int obj2ast_unaryop(struct ast_state *state, PyObject* obj, unaryop_ty* - out, PyArena* arena); + out, const char* field, PyArena* arena); static int obj2ast_cmpop(struct ast_state *state, PyObject* obj, cmpop_ty* out, - PyArena* arena); + const char* field, PyArena* arena); static int obj2ast_comprehension(struct ast_state *state, PyObject* obj, - comprehension_ty* out, PyArena* arena); + comprehension_ty* out, const char* field, + PyArena* arena); static int obj2ast_excepthandler(struct ast_state *state, PyObject* obj, - excepthandler_ty* out, PyArena* arena); + excepthandler_ty* out, const char* field, + PyArena* arena); static int obj2ast_arguments(struct ast_state *state, PyObject* obj, - arguments_ty* out, PyArena* arena); + arguments_ty* out, const char* field, PyArena* + arena); static int obj2ast_arg(struct ast_state *state, PyObject* obj, arg_ty* out, - PyArena* arena); + const char* field, PyArena* arena); static int obj2ast_keyword(struct ast_state *state, PyObject* obj, keyword_ty* - out, PyArena* arena); + out, const char* field, PyArena* arena); static int obj2ast_alias(struct ast_state *state, PyObject* obj, alias_ty* out, - PyArena* arena); + const char* field, PyArena* arena); static int obj2ast_withitem(struct ast_state *state, PyObject* obj, - withitem_ty* out, PyArena* arena); + withitem_ty* out, const char* field, PyArena* + arena); static int obj2ast_match_case(struct ast_state *state, PyObject* obj, - match_case_ty* out, PyArena* arena); + match_case_ty* out, const char* field, PyArena* + arena); static int obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* - out, PyArena* arena); + out, const char* field, PyArena* arena); static int obj2ast_type_ignore(struct ast_state *state, PyObject* obj, - type_ignore_ty* out, PyArena* arena); + type_ignore_ty* out, const char* field, PyArena* + arena); static int obj2ast_type_param(struct ast_state *state, PyObject* obj, - type_param_ty* out, PyArena* arena); + type_param_ty* out, const char* field, PyArena* + arena); mod_ty _PyAST_Module(asdl_stmt_seq * body, asdl_type_ignore_seq * type_ignores, @@ -10855,7 +10868,8 @@ ast2obj_type_param(struct ast_state *state, void* _o) int -obj2ast_mod(struct ast_state *state, PyObject* obj, mod_ty* out, PyArena* arena) +obj2ast_mod(struct ast_state *state, PyObject* obj, mod_ty* out, const char* + field, PyArena* arena) { int isinstance; @@ -10866,6 +10880,15 @@ obj2ast_mod(struct ast_state *state, PyObject* obj, mod_ty* out, PyArena* arena) *out = NULL; return 0; } + tp = state->mod_type; + isinstance = PyObject_IsInstance(obj, tp); + if (isinstance == -1) { + return 1; + } + if (!isinstance && field != NULL) { + PyErr_Format(PyExc_TypeError, "field '%s' was expecting node of type 'mod', got '%s'", field, _PyType_Name(Py_TYPE(obj))); + return 1; + } tp = state->Module_type; isinstance = PyObject_IsInstance(obj, tp); if (isinstance == -1) { @@ -10901,7 +10924,7 @@ obj2ast_mod(struct ast_state *state, PyObject* obj, mod_ty* out, PyArena* arena) if (_Py_EnterRecursiveCall(" while traversing 'Module' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "body", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -10939,7 +10962,7 @@ obj2ast_mod(struct ast_state *state, PyObject* obj, mod_ty* out, PyArena* arena) if (_Py_EnterRecursiveCall(" while traversing 'Module' node")) { goto failed; } - res = obj2ast_type_ignore(state, tmp2, &val, arena); + res = obj2ast_type_ignore(state, tmp2, &val, "type_ignores", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -10989,7 +11012,7 @@ obj2ast_mod(struct ast_state *state, PyObject* obj, mod_ty* out, PyArena* arena) if (_Py_EnterRecursiveCall(" while traversing 'Interactive' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "body", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -11025,7 +11048,7 @@ obj2ast_mod(struct ast_state *state, PyObject* obj, mod_ty* out, PyArena* arena) if (_Py_EnterRecursiveCall(" while traversing 'Expression' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &body, arena); + res = obj2ast_expr(state, tmp, &body, "body", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -11069,7 +11092,7 @@ obj2ast_mod(struct ast_state *state, PyObject* obj, mod_ty* out, PyArena* arena) if (_Py_EnterRecursiveCall(" while traversing 'FunctionType' node")) { goto failed; } - res = obj2ast_expr(state, tmp2, &val, arena); + res = obj2ast_expr(state, tmp2, &val, "argtypes", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -11093,7 +11116,7 @@ obj2ast_mod(struct ast_state *state, PyObject* obj, mod_ty* out, PyArena* arena) if (_Py_EnterRecursiveCall(" while traversing 'FunctionType' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &returns, arena); + res = obj2ast_expr(state, tmp, &returns, "returns", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -11110,8 +11133,8 @@ obj2ast_mod(struct ast_state *state, PyObject* obj, mod_ty* out, PyArena* arena) } int -obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* - arena) +obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, const char* + field, PyArena* arena) { int isinstance; @@ -11126,6 +11149,15 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* *out = NULL; return 0; } + tp = state->stmt_type; + isinstance = PyObject_IsInstance(obj, tp); + if (isinstance == -1) { + return 1; + } + if (!isinstance && field != NULL) { + PyErr_Format(PyExc_TypeError, "field '%s' was expecting node of type 'stmt', got '%s'", field, _PyType_Name(Py_TYPE(obj))); + return 1; + } if (PyObject_GetOptionalAttr(obj, state->lineno, &tmp) < 0) { return -1; } @@ -11138,7 +11170,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'stmt' node")) { goto failed; } - res = obj2ast_int(state, tmp, &lineno, arena); + res = obj2ast_int(state, tmp, &lineno, "lineno", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -11155,7 +11187,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'stmt' node")) { goto failed; } - res = obj2ast_int(state, tmp, &col_offset, arena); + res = obj2ast_int(state, tmp, &col_offset, "col_offset", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -11172,7 +11204,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'stmt' node")) { goto failed; } - res = obj2ast_int(state, tmp, &end_lineno, arena); + res = obj2ast_int(state, tmp, &end_lineno, "end_lineno", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -11189,7 +11221,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'stmt' node")) { goto failed; } - res = obj2ast_int(state, tmp, &end_col_offset, arena); + res = obj2ast_int(state, tmp, &end_col_offset, "end_col_offset", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -11220,7 +11252,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'FunctionDef' node")) { goto failed; } - res = obj2ast_identifier(state, tmp, &name, arena); + res = obj2ast_identifier(state, tmp, &name, "name", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -11237,7 +11269,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'FunctionDef' node")) { goto failed; } - res = obj2ast_arguments(state, tmp, &args, arena); + res = obj2ast_arguments(state, tmp, &args, "args", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -11268,7 +11300,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'FunctionDef' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "body", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -11306,7 +11338,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'FunctionDef' node")) { goto failed; } - res = obj2ast_expr(state, tmp2, &val, arena); + res = obj2ast_expr(state, tmp2, &val, "decorator_list", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -11330,7 +11362,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'FunctionDef' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &returns, arena); + res = obj2ast_expr(state, tmp, &returns, "returns", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -11347,7 +11379,8 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'FunctionDef' node")) { goto failed; } - res = obj2ast_string(state, tmp, &type_comment, arena); + res = obj2ast_string(state, tmp, &type_comment, "type_comment", + arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -11378,7 +11411,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'FunctionDef' node")) { goto failed; } - res = obj2ast_type_param(state, tmp2, &val, arena); + res = obj2ast_type_param(state, tmp2, &val, "type_params", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -11423,7 +11456,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AsyncFunctionDef' node")) { goto failed; } - res = obj2ast_identifier(state, tmp, &name, arena); + res = obj2ast_identifier(state, tmp, &name, "name", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -11440,7 +11473,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AsyncFunctionDef' node")) { goto failed; } - res = obj2ast_arguments(state, tmp, &args, arena); + res = obj2ast_arguments(state, tmp, &args, "args", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -11471,7 +11504,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AsyncFunctionDef' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "body", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -11509,7 +11542,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AsyncFunctionDef' node")) { goto failed; } - res = obj2ast_expr(state, tmp2, &val, arena); + res = obj2ast_expr(state, tmp2, &val, "decorator_list", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -11533,7 +11566,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AsyncFunctionDef' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &returns, arena); + res = obj2ast_expr(state, tmp, &returns, "returns", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -11550,7 +11583,8 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AsyncFunctionDef' node")) { goto failed; } - res = obj2ast_string(state, tmp, &type_comment, arena); + res = obj2ast_string(state, tmp, &type_comment, "type_comment", + arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -11581,7 +11615,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AsyncFunctionDef' node")) { goto failed; } - res = obj2ast_type_param(state, tmp2, &val, arena); + res = obj2ast_type_param(state, tmp2, &val, "type_params", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -11625,7 +11659,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'ClassDef' node")) { goto failed; } - res = obj2ast_identifier(state, tmp, &name, arena); + res = obj2ast_identifier(state, tmp, &name, "name", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -11656,7 +11690,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'ClassDef' node")) { goto failed; } - res = obj2ast_expr(state, tmp2, &val, arena); + res = obj2ast_expr(state, tmp2, &val, "bases", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -11694,7 +11728,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'ClassDef' node")) { goto failed; } - res = obj2ast_keyword(state, tmp2, &val, arena); + res = obj2ast_keyword(state, tmp2, &val, "keywords", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -11732,7 +11766,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'ClassDef' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "body", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -11770,7 +11804,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'ClassDef' node")) { goto failed; } - res = obj2ast_expr(state, tmp2, &val, arena); + res = obj2ast_expr(state, tmp2, &val, "decorator_list", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -11808,7 +11842,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'ClassDef' node")) { goto failed; } - res = obj2ast_type_param(state, tmp2, &val, arena); + res = obj2ast_type_param(state, tmp2, &val, "type_params", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -11846,7 +11880,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Return' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &value, arena); + res = obj2ast_expr(state, tmp, &value, "value", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -11890,7 +11924,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Delete' node")) { goto failed; } - res = obj2ast_expr(state, tmp2, &val, arena); + res = obj2ast_expr(state, tmp2, &val, "targets", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -11943,7 +11977,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Assign' node")) { goto failed; } - res = obj2ast_expr(state, tmp2, &val, arena); + res = obj2ast_expr(state, tmp2, &val, "targets", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -11967,7 +12001,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Assign' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &value, arena); + res = obj2ast_expr(state, tmp, &value, "value", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -11984,7 +12018,8 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Assign' node")) { goto failed; } - res = obj2ast_string(state, tmp, &type_comment, arena); + res = obj2ast_string(state, tmp, &type_comment, "type_comment", + arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -12016,7 +12051,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'TypeAlias' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &name, arena); + res = obj2ast_expr(state, tmp, &name, "name", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -12047,7 +12082,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'TypeAlias' node")) { goto failed; } - res = obj2ast_type_param(state, tmp2, &val, arena); + res = obj2ast_type_param(state, tmp2, &val, "type_params", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -12071,7 +12106,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'TypeAlias' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &value, arena); + res = obj2ast_expr(state, tmp, &value, "value", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -12103,7 +12138,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AugAssign' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &target, arena); + res = obj2ast_expr(state, tmp, &target, "target", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -12120,7 +12155,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AugAssign' node")) { goto failed; } - res = obj2ast_operator(state, tmp, &op, arena); + res = obj2ast_operator(state, tmp, &op, "op", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -12137,7 +12172,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AugAssign' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &value, arena); + res = obj2ast_expr(state, tmp, &value, "value", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -12170,7 +12205,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AnnAssign' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &target, arena); + res = obj2ast_expr(state, tmp, &target, "target", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -12187,7 +12222,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AnnAssign' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &annotation, arena); + res = obj2ast_expr(state, tmp, &annotation, "annotation", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -12204,7 +12239,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AnnAssign' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &value, arena); + res = obj2ast_expr(state, tmp, &value, "value", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -12221,7 +12256,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AnnAssign' node")) { goto failed; } - res = obj2ast_int(state, tmp, &simple, arena); + res = obj2ast_int(state, tmp, &simple, "simple", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -12255,7 +12290,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'For' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &target, arena); + res = obj2ast_expr(state, tmp, &target, "target", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -12272,7 +12307,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'For' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &iter, arena); + res = obj2ast_expr(state, tmp, &iter, "iter", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -12303,7 +12338,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'For' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "body", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -12341,7 +12376,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'For' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "orelse", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -12365,7 +12400,8 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'For' node")) { goto failed; } - res = obj2ast_string(state, tmp, &type_comment, arena); + res = obj2ast_string(state, tmp, &type_comment, "type_comment", + arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -12399,7 +12435,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AsyncFor' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &target, arena); + res = obj2ast_expr(state, tmp, &target, "target", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -12416,7 +12452,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AsyncFor' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &iter, arena); + res = obj2ast_expr(state, tmp, &iter, "iter", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -12447,7 +12483,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AsyncFor' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "body", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -12485,7 +12521,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AsyncFor' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "orelse", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -12509,7 +12545,8 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AsyncFor' node")) { goto failed; } - res = obj2ast_string(state, tmp, &type_comment, arena); + res = obj2ast_string(state, tmp, &type_comment, "type_comment", + arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -12542,7 +12579,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'While' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &test, arena); + res = obj2ast_expr(state, tmp, &test, "test", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -12573,7 +12610,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'While' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "body", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -12611,7 +12648,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'While' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "orelse", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -12650,7 +12687,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'If' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &test, arena); + res = obj2ast_expr(state, tmp, &test, "test", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -12681,7 +12718,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'If' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "body", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -12719,7 +12756,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'If' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "orelse", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -12772,7 +12809,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'With' node")) { goto failed; } - res = obj2ast_withitem(state, tmp2, &val, arena); + res = obj2ast_withitem(state, tmp2, &val, "items", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -12810,7 +12847,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'With' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "body", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -12834,7 +12871,8 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'With' node")) { goto failed; } - res = obj2ast_string(state, tmp, &type_comment, arena); + res = obj2ast_string(state, tmp, &type_comment, "type_comment", + arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -12880,7 +12918,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AsyncWith' node")) { goto failed; } - res = obj2ast_withitem(state, tmp2, &val, arena); + res = obj2ast_withitem(state, tmp2, &val, "items", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -12918,7 +12956,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AsyncWith' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "body", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -12942,7 +12980,8 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AsyncWith' node")) { goto failed; } - res = obj2ast_string(state, tmp, &type_comment, arena); + res = obj2ast_string(state, tmp, &type_comment, "type_comment", + arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -12973,7 +13012,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Match' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &subject, arena); + res = obj2ast_expr(state, tmp, &subject, "subject", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -13004,7 +13043,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Match' node")) { goto failed; } - res = obj2ast_match_case(state, tmp2, &val, arena); + res = obj2ast_match_case(state, tmp2, &val, "cases", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -13042,7 +13081,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Raise' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &exc, arena); + res = obj2ast_expr(state, tmp, &exc, "exc", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -13059,7 +13098,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Raise' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &cause, arena); + res = obj2ast_expr(state, tmp, &cause, "cause", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -13106,7 +13145,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Try' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "body", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -13144,7 +13183,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Try' node")) { goto failed; } - res = obj2ast_excepthandler(state, tmp2, &val, arena); + res = obj2ast_excepthandler(state, tmp2, &val, "handlers", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -13182,7 +13221,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Try' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "orelse", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -13220,7 +13259,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Try' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "finalbody", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -13274,7 +13313,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'TryStar' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "body", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -13312,7 +13351,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'TryStar' node")) { goto failed; } - res = obj2ast_excepthandler(state, tmp2, &val, arena); + res = obj2ast_excepthandler(state, tmp2, &val, "handlers", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -13350,7 +13389,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'TryStar' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "orelse", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -13388,7 +13427,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'TryStar' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "finalbody", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -13426,7 +13465,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Assert' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &test, arena); + res = obj2ast_expr(state, tmp, &test, "test", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -13443,7 +13482,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Assert' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &msg, arena); + res = obj2ast_expr(state, tmp, &msg, "msg", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -13488,7 +13527,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Import' node")) { goto failed; } - res = obj2ast_alias(state, tmp2, &val, arena); + res = obj2ast_alias(state, tmp2, &val, "names", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -13512,7 +13551,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Import' node")) { goto failed; } - res = obj2ast_int(state, tmp, &is_lazy, arena); + res = obj2ast_int(state, tmp, &is_lazy, "is_lazy", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -13545,7 +13584,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'ImportFrom' node")) { goto failed; } - res = obj2ast_identifier(state, tmp, &module, arena); + res = obj2ast_identifier(state, tmp, &module, "module", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -13576,7 +13615,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'ImportFrom' node")) { goto failed; } - res = obj2ast_alias(state, tmp2, &val, arena); + res = obj2ast_alias(state, tmp2, &val, "names", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -13600,7 +13639,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'ImportFrom' node")) { goto failed; } - res = obj2ast_int(state, tmp, &level, arena); + res = obj2ast_int(state, tmp, &level, "level", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -13617,7 +13656,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'ImportFrom' node")) { goto failed; } - res = obj2ast_int(state, tmp, &is_lazy, arena); + res = obj2ast_int(state, tmp, &is_lazy, "is_lazy", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -13661,7 +13700,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Global' node")) { goto failed; } - res = obj2ast_identifier(state, tmp2, &val, arena); + res = obj2ast_identifier(state, tmp2, &val, "names", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -13712,7 +13751,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Nonlocal' node")) { goto failed; } - res = obj2ast_identifier(state, tmp2, &val, arena); + res = obj2ast_identifier(state, tmp2, &val, "names", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -13749,7 +13788,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Expr' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &value, arena); + res = obj2ast_expr(state, tmp, &value, "value", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -13803,8 +13842,8 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* } int -obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* - arena) +obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, const char* + field, PyArena* arena) { int isinstance; @@ -13819,6 +13858,15 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* *out = NULL; return 0; } + tp = state->expr_type; + isinstance = PyObject_IsInstance(obj, tp); + if (isinstance == -1) { + return 1; + } + if (!isinstance && field != NULL) { + PyErr_Format(PyExc_TypeError, "field '%s' was expecting node of type 'expr', got '%s'", field, _PyType_Name(Py_TYPE(obj))); + return 1; + } if (PyObject_GetOptionalAttr(obj, state->lineno, &tmp) < 0) { return -1; } @@ -13831,7 +13879,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'expr' node")) { goto failed; } - res = obj2ast_int(state, tmp, &lineno, arena); + res = obj2ast_int(state, tmp, &lineno, "lineno", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -13848,7 +13896,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'expr' node")) { goto failed; } - res = obj2ast_int(state, tmp, &col_offset, arena); + res = obj2ast_int(state, tmp, &col_offset, "col_offset", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -13865,7 +13913,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'expr' node")) { goto failed; } - res = obj2ast_int(state, tmp, &end_lineno, arena); + res = obj2ast_int(state, tmp, &end_lineno, "end_lineno", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -13882,7 +13930,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'expr' node")) { goto failed; } - res = obj2ast_int(state, tmp, &end_col_offset, arena); + res = obj2ast_int(state, tmp, &end_col_offset, "end_col_offset", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -13908,7 +13956,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'BoolOp' node")) { goto failed; } - res = obj2ast_boolop(state, tmp, &op, arena); + res = obj2ast_boolop(state, tmp, &op, "op", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -13939,7 +13987,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'BoolOp' node")) { goto failed; } - res = obj2ast_expr(state, tmp2, &val, arena); + res = obj2ast_expr(state, tmp2, &val, "values", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -13977,7 +14025,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'NamedExpr' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &target, arena); + res = obj2ast_expr(state, tmp, &target, "target", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -13994,7 +14042,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'NamedExpr' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &value, arena); + res = obj2ast_expr(state, tmp, &value, "value", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14026,7 +14074,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'BinOp' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &left, arena); + res = obj2ast_expr(state, tmp, &left, "left", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14043,7 +14091,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'BinOp' node")) { goto failed; } - res = obj2ast_operator(state, tmp, &op, arena); + res = obj2ast_operator(state, tmp, &op, "op", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14060,7 +14108,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'BinOp' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &right, arena); + res = obj2ast_expr(state, tmp, &right, "right", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14091,7 +14139,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'UnaryOp' node")) { goto failed; } - res = obj2ast_unaryop(state, tmp, &op, arena); + res = obj2ast_unaryop(state, tmp, &op, "op", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14108,7 +14156,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'UnaryOp' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &operand, arena); + res = obj2ast_expr(state, tmp, &operand, "operand", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14139,7 +14187,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Lambda' node")) { goto failed; } - res = obj2ast_arguments(state, tmp, &args, arena); + res = obj2ast_arguments(state, tmp, &args, "args", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14156,7 +14204,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Lambda' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &body, arena); + res = obj2ast_expr(state, tmp, &body, "body", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14188,7 +14236,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'IfExp' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &test, arena); + res = obj2ast_expr(state, tmp, &test, "test", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14205,7 +14253,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'IfExp' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &body, arena); + res = obj2ast_expr(state, tmp, &body, "body", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14222,7 +14270,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'IfExp' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &orelse, arena); + res = obj2ast_expr(state, tmp, &orelse, "orelse", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14267,7 +14315,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Dict' node")) { goto failed; } - res = obj2ast_expr(state, tmp2, &val, arena); + res = obj2ast_expr(state, tmp2, &val, "keys", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -14305,7 +14353,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Dict' node")) { goto failed; } - res = obj2ast_expr(state, tmp2, &val, arena); + res = obj2ast_expr(state, tmp2, &val, "values", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -14356,7 +14404,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Set' node")) { goto failed; } - res = obj2ast_expr(state, tmp2, &val, arena); + res = obj2ast_expr(state, tmp2, &val, "elts", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -14394,7 +14442,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'ListComp' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &elt, arena); + res = obj2ast_expr(state, tmp, &elt, "elt", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14425,7 +14473,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'ListComp' node")) { goto failed; } - res = obj2ast_comprehension(state, tmp2, &val, arena); + res = obj2ast_comprehension(state, tmp2, &val, "generators", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -14463,7 +14511,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'SetComp' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &elt, arena); + res = obj2ast_expr(state, tmp, &elt, "elt", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14494,7 +14542,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'SetComp' node")) { goto failed; } - res = obj2ast_comprehension(state, tmp2, &val, arena); + res = obj2ast_comprehension(state, tmp2, &val, "generators", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -14533,7 +14581,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'DictComp' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &key, arena); + res = obj2ast_expr(state, tmp, &key, "key", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14550,7 +14598,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'DictComp' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &value, arena); + res = obj2ast_expr(state, tmp, &value, "value", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14581,7 +14629,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'DictComp' node")) { goto failed; } - res = obj2ast_comprehension(state, tmp2, &val, arena); + res = obj2ast_comprehension(state, tmp2, &val, "generators", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -14619,7 +14667,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'GeneratorExp' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &elt, arena); + res = obj2ast_expr(state, tmp, &elt, "elt", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14650,7 +14698,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'GeneratorExp' node")) { goto failed; } - res = obj2ast_comprehension(state, tmp2, &val, arena); + res = obj2ast_comprehension(state, tmp2, &val, "generators", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -14687,7 +14735,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Await' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &value, arena); + res = obj2ast_expr(state, tmp, &value, "value", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14717,7 +14765,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Yield' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &value, arena); + res = obj2ast_expr(state, tmp, &value, "value", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14747,7 +14795,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'YieldFrom' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &value, arena); + res = obj2ast_expr(state, tmp, &value, "value", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14779,7 +14827,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Compare' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &left, arena); + res = obj2ast_expr(state, tmp, &left, "left", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14810,7 +14858,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Compare' node")) { goto failed; } - res = obj2ast_cmpop(state, tmp2, &val, arena); + res = obj2ast_cmpop(state, tmp2, &val, "ops", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -14848,7 +14896,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Compare' node")) { goto failed; } - res = obj2ast_expr(state, tmp2, &val, arena); + res = obj2ast_expr(state, tmp2, &val, "comparators", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -14887,7 +14935,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Call' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &func, arena); + res = obj2ast_expr(state, tmp, &func, "func", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14918,7 +14966,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Call' node")) { goto failed; } - res = obj2ast_expr(state, tmp2, &val, arena); + res = obj2ast_expr(state, tmp2, &val, "args", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -14956,7 +15004,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Call' node")) { goto failed; } - res = obj2ast_keyword(state, tmp2, &val, arena); + res = obj2ast_keyword(state, tmp2, &val, "keywords", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -14995,7 +15043,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'FormattedValue' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &value, arena); + res = obj2ast_expr(state, tmp, &value, "value", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15012,7 +15060,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'FormattedValue' node")) { goto failed; } - res = obj2ast_int(state, tmp, &conversion, arena); + res = obj2ast_int(state, tmp, &conversion, "conversion", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15029,7 +15077,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'FormattedValue' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &format_spec, arena); + res = obj2ast_expr(state, tmp, &format_spec, "format_spec", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15063,7 +15111,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Interpolation' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &value, arena); + res = obj2ast_expr(state, tmp, &value, "value", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15080,7 +15128,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Interpolation' node")) { goto failed; } - res = obj2ast_constant(state, tmp, &str, arena); + res = obj2ast_constant(state, tmp, &str, "str", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15097,7 +15145,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Interpolation' node")) { goto failed; } - res = obj2ast_int(state, tmp, &conversion, arena); + res = obj2ast_int(state, tmp, &conversion, "conversion", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15114,7 +15162,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Interpolation' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &format_spec, arena); + res = obj2ast_expr(state, tmp, &format_spec, "format_spec", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15159,7 +15207,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'JoinedStr' node")) { goto failed; } - res = obj2ast_expr(state, tmp2, &val, arena); + res = obj2ast_expr(state, tmp2, &val, "values", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -15210,7 +15258,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'TemplateStr' node")) { goto failed; } - res = obj2ast_expr(state, tmp2, &val, arena); + res = obj2ast_expr(state, tmp2, &val, "values", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -15248,7 +15296,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Constant' node")) { goto failed; } - res = obj2ast_constant(state, tmp, &value, arena); + res = obj2ast_constant(state, tmp, &value, "value", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15265,7 +15313,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Constant' node")) { goto failed; } - res = obj2ast_string(state, tmp, &kind, arena); + res = obj2ast_string(state, tmp, &kind, "kind", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15297,7 +15345,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Attribute' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &value, arena); + res = obj2ast_expr(state, tmp, &value, "value", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15314,7 +15362,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Attribute' node")) { goto failed; } - res = obj2ast_identifier(state, tmp, &attr, arena); + res = obj2ast_identifier(state, tmp, &attr, "attr", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15331,7 +15379,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Attribute' node")) { goto failed; } - res = obj2ast_expr_context(state, tmp, &ctx, arena); + res = obj2ast_expr_context(state, tmp, &ctx, "ctx", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15363,7 +15411,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Subscript' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &value, arena); + res = obj2ast_expr(state, tmp, &value, "value", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15380,7 +15428,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Subscript' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &slice, arena); + res = obj2ast_expr(state, tmp, &slice, "slice", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15397,7 +15445,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Subscript' node")) { goto failed; } - res = obj2ast_expr_context(state, tmp, &ctx, arena); + res = obj2ast_expr_context(state, tmp, &ctx, "ctx", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15428,7 +15476,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Starred' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &value, arena); + res = obj2ast_expr(state, tmp, &value, "value", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15445,7 +15493,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Starred' node")) { goto failed; } - res = obj2ast_expr_context(state, tmp, &ctx, arena); + res = obj2ast_expr_context(state, tmp, &ctx, "ctx", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15476,7 +15524,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Name' node")) { goto failed; } - res = obj2ast_identifier(state, tmp, &id, arena); + res = obj2ast_identifier(state, tmp, &id, "id", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15493,7 +15541,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Name' node")) { goto failed; } - res = obj2ast_expr_context(state, tmp, &ctx, arena); + res = obj2ast_expr_context(state, tmp, &ctx, "ctx", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15538,7 +15586,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'List' node")) { goto failed; } - res = obj2ast_expr(state, tmp2, &val, arena); + res = obj2ast_expr(state, tmp2, &val, "elts", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -15562,7 +15610,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'List' node")) { goto failed; } - res = obj2ast_expr_context(state, tmp, &ctx, arena); + res = obj2ast_expr_context(state, tmp, &ctx, "ctx", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15607,7 +15655,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Tuple' node")) { goto failed; } - res = obj2ast_expr(state, tmp2, &val, arena); + res = obj2ast_expr(state, tmp2, &val, "elts", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -15631,7 +15679,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Tuple' node")) { goto failed; } - res = obj2ast_expr_context(state, tmp, &ctx, arena); + res = obj2ast_expr_context(state, tmp, &ctx, "ctx", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15663,7 +15711,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Slice' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &lower, arena); + res = obj2ast_expr(state, tmp, &lower, "lower", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15680,7 +15728,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Slice' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &upper, arena); + res = obj2ast_expr(state, tmp, &upper, "upper", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15697,7 +15745,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Slice' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &step, arena); + res = obj2ast_expr(state, tmp, &step, "step", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15716,7 +15764,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* int obj2ast_expr_context(struct ast_state *state, PyObject* obj, expr_context_ty* - out, PyArena* arena) + out, const char* field, PyArena* arena) { int isinstance; @@ -15750,8 +15798,8 @@ obj2ast_expr_context(struct ast_state *state, PyObject* obj, expr_context_ty* } int -obj2ast_boolop(struct ast_state *state, PyObject* obj, boolop_ty* out, PyArena* - arena) +obj2ast_boolop(struct ast_state *state, PyObject* obj, boolop_ty* out, const + char* field, PyArena* arena) { int isinstance; @@ -15778,7 +15826,7 @@ obj2ast_boolop(struct ast_state *state, PyObject* obj, boolop_ty* out, PyArena* int obj2ast_operator(struct ast_state *state, PyObject* obj, operator_ty* out, - PyArena* arena) + const char* field, PyArena* arena) { int isinstance; @@ -15892,8 +15940,8 @@ obj2ast_operator(struct ast_state *state, PyObject* obj, operator_ty* out, } int -obj2ast_unaryop(struct ast_state *state, PyObject* obj, unaryop_ty* out, - PyArena* arena) +obj2ast_unaryop(struct ast_state *state, PyObject* obj, unaryop_ty* out, const + char* field, PyArena* arena) { int isinstance; @@ -15935,8 +15983,8 @@ obj2ast_unaryop(struct ast_state *state, PyObject* obj, unaryop_ty* out, } int -obj2ast_cmpop(struct ast_state *state, PyObject* obj, cmpop_ty* out, PyArena* - arena) +obj2ast_cmpop(struct ast_state *state, PyObject* obj, cmpop_ty* out, const + char* field, PyArena* arena) { int isinstance; @@ -16027,7 +16075,7 @@ obj2ast_cmpop(struct ast_state *state, PyObject* obj, cmpop_ty* out, PyArena* int obj2ast_comprehension(struct ast_state *state, PyObject* obj, comprehension_ty* - out, PyArena* arena) + out, const char* field, PyArena* arena) { PyObject* tmp = NULL; expr_ty target; @@ -16047,7 +16095,7 @@ obj2ast_comprehension(struct ast_state *state, PyObject* obj, comprehension_ty* if (_Py_EnterRecursiveCall(" while traversing 'comprehension' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &target, arena); + res = obj2ast_expr(state, tmp, &target, "target", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16064,7 +16112,7 @@ obj2ast_comprehension(struct ast_state *state, PyObject* obj, comprehension_ty* if (_Py_EnterRecursiveCall(" while traversing 'comprehension' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &iter, arena); + res = obj2ast_expr(state, tmp, &iter, "iter", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16095,7 +16143,7 @@ obj2ast_comprehension(struct ast_state *state, PyObject* obj, comprehension_ty* if (_Py_EnterRecursiveCall(" while traversing 'comprehension' node")) { goto failed; } - res = obj2ast_expr(state, tmp2, &val, arena); + res = obj2ast_expr(state, tmp2, &val, "ifs", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -16119,7 +16167,7 @@ obj2ast_comprehension(struct ast_state *state, PyObject* obj, comprehension_ty* if (_Py_EnterRecursiveCall(" while traversing 'comprehension' node")) { goto failed; } - res = obj2ast_int(state, tmp, &is_async, arena); + res = obj2ast_int(state, tmp, &is_async, "is_async", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16134,7 +16182,7 @@ obj2ast_comprehension(struct ast_state *state, PyObject* obj, comprehension_ty* int obj2ast_excepthandler(struct ast_state *state, PyObject* obj, excepthandler_ty* - out, PyArena* arena) + out, const char* field, PyArena* arena) { int isinstance; @@ -16149,6 +16197,15 @@ obj2ast_excepthandler(struct ast_state *state, PyObject* obj, excepthandler_ty* *out = NULL; return 0; } + tp = state->excepthandler_type; + isinstance = PyObject_IsInstance(obj, tp); + if (isinstance == -1) { + return 1; + } + if (!isinstance && field != NULL) { + PyErr_Format(PyExc_TypeError, "field '%s' was expecting node of type 'excepthandler', got '%s'", field, _PyType_Name(Py_TYPE(obj))); + return 1; + } if (PyObject_GetOptionalAttr(obj, state->lineno, &tmp) < 0) { return -1; } @@ -16161,7 +16218,7 @@ obj2ast_excepthandler(struct ast_state *state, PyObject* obj, excepthandler_ty* if (_Py_EnterRecursiveCall(" while traversing 'excepthandler' node")) { goto failed; } - res = obj2ast_int(state, tmp, &lineno, arena); + res = obj2ast_int(state, tmp, &lineno, "lineno", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16178,7 +16235,7 @@ obj2ast_excepthandler(struct ast_state *state, PyObject* obj, excepthandler_ty* if (_Py_EnterRecursiveCall(" while traversing 'excepthandler' node")) { goto failed; } - res = obj2ast_int(state, tmp, &col_offset, arena); + res = obj2ast_int(state, tmp, &col_offset, "col_offset", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16195,7 +16252,7 @@ obj2ast_excepthandler(struct ast_state *state, PyObject* obj, excepthandler_ty* if (_Py_EnterRecursiveCall(" while traversing 'excepthandler' node")) { goto failed; } - res = obj2ast_int(state, tmp, &end_lineno, arena); + res = obj2ast_int(state, tmp, &end_lineno, "end_lineno", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16212,7 +16269,7 @@ obj2ast_excepthandler(struct ast_state *state, PyObject* obj, excepthandler_ty* if (_Py_EnterRecursiveCall(" while traversing 'excepthandler' node")) { goto failed; } - res = obj2ast_int(state, tmp, &end_col_offset, arena); + res = obj2ast_int(state, tmp, &end_col_offset, "end_col_offset", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16239,7 +16296,7 @@ obj2ast_excepthandler(struct ast_state *state, PyObject* obj, excepthandler_ty* if (_Py_EnterRecursiveCall(" while traversing 'ExceptHandler' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &type, arena); + res = obj2ast_expr(state, tmp, &type, "type", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16256,7 +16313,7 @@ obj2ast_excepthandler(struct ast_state *state, PyObject* obj, excepthandler_ty* if (_Py_EnterRecursiveCall(" while traversing 'ExceptHandler' node")) { goto failed; } - res = obj2ast_identifier(state, tmp, &name, arena); + res = obj2ast_identifier(state, tmp, &name, "name", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16287,7 +16344,7 @@ obj2ast_excepthandler(struct ast_state *state, PyObject* obj, excepthandler_ty* if (_Py_EnterRecursiveCall(" while traversing 'ExceptHandler' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "body", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -16313,7 +16370,7 @@ obj2ast_excepthandler(struct ast_state *state, PyObject* obj, excepthandler_ty* int obj2ast_arguments(struct ast_state *state, PyObject* obj, arguments_ty* out, - PyArena* arena) + const char* field, PyArena* arena) { PyObject* tmp = NULL; asdl_arg_seq* posonlyargs; @@ -16350,7 +16407,7 @@ obj2ast_arguments(struct ast_state *state, PyObject* obj, arguments_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'arguments' node")) { goto failed; } - res = obj2ast_arg(state, tmp2, &val, arena); + res = obj2ast_arg(state, tmp2, &val, "posonlyargs", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -16388,7 +16445,7 @@ obj2ast_arguments(struct ast_state *state, PyObject* obj, arguments_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'arguments' node")) { goto failed; } - res = obj2ast_arg(state, tmp2, &val, arena); + res = obj2ast_arg(state, tmp2, &val, "args", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -16412,7 +16469,7 @@ obj2ast_arguments(struct ast_state *state, PyObject* obj, arguments_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'arguments' node")) { goto failed; } - res = obj2ast_arg(state, tmp, &vararg, arena); + res = obj2ast_arg(state, tmp, &vararg, "vararg", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16443,7 +16500,7 @@ obj2ast_arguments(struct ast_state *state, PyObject* obj, arguments_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'arguments' node")) { goto failed; } - res = obj2ast_arg(state, tmp2, &val, arena); + res = obj2ast_arg(state, tmp2, &val, "kwonlyargs", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -16481,7 +16538,7 @@ obj2ast_arguments(struct ast_state *state, PyObject* obj, arguments_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'arguments' node")) { goto failed; } - res = obj2ast_expr(state, tmp2, &val, arena); + res = obj2ast_expr(state, tmp2, &val, "kw_defaults", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -16505,7 +16562,7 @@ obj2ast_arguments(struct ast_state *state, PyObject* obj, arguments_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'arguments' node")) { goto failed; } - res = obj2ast_arg(state, tmp, &kwarg, arena); + res = obj2ast_arg(state, tmp, &kwarg, "kwarg", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16536,7 +16593,7 @@ obj2ast_arguments(struct ast_state *state, PyObject* obj, arguments_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'arguments' node")) { goto failed; } - res = obj2ast_expr(state, tmp2, &val, arena); + res = obj2ast_expr(state, tmp2, &val, "defaults", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -16558,7 +16615,8 @@ obj2ast_arguments(struct ast_state *state, PyObject* obj, arguments_ty* out, } int -obj2ast_arg(struct ast_state *state, PyObject* obj, arg_ty* out, PyArena* arena) +obj2ast_arg(struct ast_state *state, PyObject* obj, arg_ty* out, const char* + field, PyArena* arena) { PyObject* tmp = NULL; identifier arg; @@ -16581,7 +16639,7 @@ obj2ast_arg(struct ast_state *state, PyObject* obj, arg_ty* out, PyArena* arena) if (_Py_EnterRecursiveCall(" while traversing 'arg' node")) { goto failed; } - res = obj2ast_identifier(state, tmp, &arg, arena); + res = obj2ast_identifier(state, tmp, &arg, "arg", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16598,7 +16656,7 @@ obj2ast_arg(struct ast_state *state, PyObject* obj, arg_ty* out, PyArena* arena) if (_Py_EnterRecursiveCall(" while traversing 'arg' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &annotation, arena); + res = obj2ast_expr(state, tmp, &annotation, "annotation", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16615,7 +16673,7 @@ obj2ast_arg(struct ast_state *state, PyObject* obj, arg_ty* out, PyArena* arena) if (_Py_EnterRecursiveCall(" while traversing 'arg' node")) { goto failed; } - res = obj2ast_string(state, tmp, &type_comment, arena); + res = obj2ast_string(state, tmp, &type_comment, "type_comment", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16632,7 +16690,7 @@ obj2ast_arg(struct ast_state *state, PyObject* obj, arg_ty* out, PyArena* arena) if (_Py_EnterRecursiveCall(" while traversing 'arg' node")) { goto failed; } - res = obj2ast_int(state, tmp, &lineno, arena); + res = obj2ast_int(state, tmp, &lineno, "lineno", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16649,7 +16707,7 @@ obj2ast_arg(struct ast_state *state, PyObject* obj, arg_ty* out, PyArena* arena) if (_Py_EnterRecursiveCall(" while traversing 'arg' node")) { goto failed; } - res = obj2ast_int(state, tmp, &col_offset, arena); + res = obj2ast_int(state, tmp, &col_offset, "col_offset", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16666,7 +16724,7 @@ obj2ast_arg(struct ast_state *state, PyObject* obj, arg_ty* out, PyArena* arena) if (_Py_EnterRecursiveCall(" while traversing 'arg' node")) { goto failed; } - res = obj2ast_int(state, tmp, &end_lineno, arena); + res = obj2ast_int(state, tmp, &end_lineno, "end_lineno", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16683,7 +16741,7 @@ obj2ast_arg(struct ast_state *state, PyObject* obj, arg_ty* out, PyArena* arena) if (_Py_EnterRecursiveCall(" while traversing 'arg' node")) { goto failed; } - res = obj2ast_int(state, tmp, &end_col_offset, arena); + res = obj2ast_int(state, tmp, &end_col_offset, "end_col_offset", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16698,8 +16756,8 @@ obj2ast_arg(struct ast_state *state, PyObject* obj, arg_ty* out, PyArena* arena) } int -obj2ast_keyword(struct ast_state *state, PyObject* obj, keyword_ty* out, - PyArena* arena) +obj2ast_keyword(struct ast_state *state, PyObject* obj, keyword_ty* out, const + char* field, PyArena* arena) { PyObject* tmp = NULL; identifier arg; @@ -16721,7 +16779,7 @@ obj2ast_keyword(struct ast_state *state, PyObject* obj, keyword_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'keyword' node")) { goto failed; } - res = obj2ast_identifier(state, tmp, &arg, arena); + res = obj2ast_identifier(state, tmp, &arg, "arg", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16738,7 +16796,7 @@ obj2ast_keyword(struct ast_state *state, PyObject* obj, keyword_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'keyword' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &value, arena); + res = obj2ast_expr(state, tmp, &value, "value", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16755,7 +16813,7 @@ obj2ast_keyword(struct ast_state *state, PyObject* obj, keyword_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'keyword' node")) { goto failed; } - res = obj2ast_int(state, tmp, &lineno, arena); + res = obj2ast_int(state, tmp, &lineno, "lineno", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16772,7 +16830,7 @@ obj2ast_keyword(struct ast_state *state, PyObject* obj, keyword_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'keyword' node")) { goto failed; } - res = obj2ast_int(state, tmp, &col_offset, arena); + res = obj2ast_int(state, tmp, &col_offset, "col_offset", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16789,7 +16847,7 @@ obj2ast_keyword(struct ast_state *state, PyObject* obj, keyword_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'keyword' node")) { goto failed; } - res = obj2ast_int(state, tmp, &end_lineno, arena); + res = obj2ast_int(state, tmp, &end_lineno, "end_lineno", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16806,7 +16864,7 @@ obj2ast_keyword(struct ast_state *state, PyObject* obj, keyword_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'keyword' node")) { goto failed; } - res = obj2ast_int(state, tmp, &end_col_offset, arena); + res = obj2ast_int(state, tmp, &end_col_offset, "end_col_offset", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16821,8 +16879,8 @@ obj2ast_keyword(struct ast_state *state, PyObject* obj, keyword_ty* out, } int -obj2ast_alias(struct ast_state *state, PyObject* obj, alias_ty* out, PyArena* - arena) +obj2ast_alias(struct ast_state *state, PyObject* obj, alias_ty* out, const + char* field, PyArena* arena) { PyObject* tmp = NULL; identifier name; @@ -16844,7 +16902,7 @@ obj2ast_alias(struct ast_state *state, PyObject* obj, alias_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'alias' node")) { goto failed; } - res = obj2ast_identifier(state, tmp, &name, arena); + res = obj2ast_identifier(state, tmp, &name, "name", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16861,7 +16919,7 @@ obj2ast_alias(struct ast_state *state, PyObject* obj, alias_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'alias' node")) { goto failed; } - res = obj2ast_identifier(state, tmp, &asname, arena); + res = obj2ast_identifier(state, tmp, &asname, "asname", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16878,7 +16936,7 @@ obj2ast_alias(struct ast_state *state, PyObject* obj, alias_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'alias' node")) { goto failed; } - res = obj2ast_int(state, tmp, &lineno, arena); + res = obj2ast_int(state, tmp, &lineno, "lineno", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16895,7 +16953,7 @@ obj2ast_alias(struct ast_state *state, PyObject* obj, alias_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'alias' node")) { goto failed; } - res = obj2ast_int(state, tmp, &col_offset, arena); + res = obj2ast_int(state, tmp, &col_offset, "col_offset", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16912,7 +16970,7 @@ obj2ast_alias(struct ast_state *state, PyObject* obj, alias_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'alias' node")) { goto failed; } - res = obj2ast_int(state, tmp, &end_lineno, arena); + res = obj2ast_int(state, tmp, &end_lineno, "end_lineno", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16929,7 +16987,7 @@ obj2ast_alias(struct ast_state *state, PyObject* obj, alias_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'alias' node")) { goto failed; } - res = obj2ast_int(state, tmp, &end_col_offset, arena); + res = obj2ast_int(state, tmp, &end_col_offset, "end_col_offset", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16945,7 +17003,7 @@ obj2ast_alias(struct ast_state *state, PyObject* obj, alias_ty* out, PyArena* int obj2ast_withitem(struct ast_state *state, PyObject* obj, withitem_ty* out, - PyArena* arena) + const char* field, PyArena* arena) { PyObject* tmp = NULL; expr_ty context_expr; @@ -16963,7 +17021,7 @@ obj2ast_withitem(struct ast_state *state, PyObject* obj, withitem_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'withitem' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &context_expr, arena); + res = obj2ast_expr(state, tmp, &context_expr, "context_expr", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16980,7 +17038,7 @@ obj2ast_withitem(struct ast_state *state, PyObject* obj, withitem_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'withitem' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &optional_vars, arena); + res = obj2ast_expr(state, tmp, &optional_vars, "optional_vars", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16995,7 +17053,7 @@ obj2ast_withitem(struct ast_state *state, PyObject* obj, withitem_ty* out, int obj2ast_match_case(struct ast_state *state, PyObject* obj, match_case_ty* out, - PyArena* arena) + const char* field, PyArena* arena) { PyObject* tmp = NULL; pattern_ty pattern; @@ -17014,7 +17072,7 @@ obj2ast_match_case(struct ast_state *state, PyObject* obj, match_case_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'match_case' node")) { goto failed; } - res = obj2ast_pattern(state, tmp, &pattern, arena); + res = obj2ast_pattern(state, tmp, &pattern, "pattern", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17031,7 +17089,7 @@ obj2ast_match_case(struct ast_state *state, PyObject* obj, match_case_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'match_case' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &guard, arena); + res = obj2ast_expr(state, tmp, &guard, "guard", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17062,7 +17120,7 @@ obj2ast_match_case(struct ast_state *state, PyObject* obj, match_case_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'match_case' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "body", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -17083,8 +17141,8 @@ obj2ast_match_case(struct ast_state *state, PyObject* obj, match_case_ty* out, } int -obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, - PyArena* arena) +obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, const + char* field, PyArena* arena) { int isinstance; @@ -17099,6 +17157,15 @@ obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, *out = NULL; return 0; } + tp = state->pattern_type; + isinstance = PyObject_IsInstance(obj, tp); + if (isinstance == -1) { + return 1; + } + if (!isinstance && field != NULL) { + PyErr_Format(PyExc_TypeError, "field '%s' was expecting node of type 'pattern', got '%s'", field, _PyType_Name(Py_TYPE(obj))); + return 1; + } if (PyObject_GetOptionalAttr(obj, state->lineno, &tmp) < 0) { return -1; } @@ -17111,7 +17178,7 @@ obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'pattern' node")) { goto failed; } - res = obj2ast_int(state, tmp, &lineno, arena); + res = obj2ast_int(state, tmp, &lineno, "lineno", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17128,7 +17195,7 @@ obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'pattern' node")) { goto failed; } - res = obj2ast_int(state, tmp, &col_offset, arena); + res = obj2ast_int(state, tmp, &col_offset, "col_offset", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17145,7 +17212,7 @@ obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'pattern' node")) { goto failed; } - res = obj2ast_int(state, tmp, &end_lineno, arena); + res = obj2ast_int(state, tmp, &end_lineno, "end_lineno", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17162,7 +17229,7 @@ obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'pattern' node")) { goto failed; } - res = obj2ast_int(state, tmp, &end_col_offset, arena); + res = obj2ast_int(state, tmp, &end_col_offset, "end_col_offset", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17187,7 +17254,7 @@ obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'MatchValue' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &value, arena); + res = obj2ast_expr(state, tmp, &value, "value", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17217,7 +17284,7 @@ obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'MatchSingleton' node")) { goto failed; } - res = obj2ast_constant(state, tmp, &value, arena); + res = obj2ast_constant(state, tmp, &value, "value", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17261,7 +17328,7 @@ obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'MatchSequence' node")) { goto failed; } - res = obj2ast_pattern(state, tmp2, &val, arena); + res = obj2ast_pattern(state, tmp2, &val, "patterns", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -17314,7 +17381,7 @@ obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'MatchMapping' node")) { goto failed; } - res = obj2ast_expr(state, tmp2, &val, arena); + res = obj2ast_expr(state, tmp2, &val, "keys", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -17352,7 +17419,7 @@ obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'MatchMapping' node")) { goto failed; } - res = obj2ast_pattern(state, tmp2, &val, arena); + res = obj2ast_pattern(state, tmp2, &val, "patterns", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -17376,7 +17443,7 @@ obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'MatchMapping' node")) { goto failed; } - res = obj2ast_identifier(state, tmp, &rest, arena); + res = obj2ast_identifier(state, tmp, &rest, "rest", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17409,7 +17476,7 @@ obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'MatchClass' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &cls, arena); + res = obj2ast_expr(state, tmp, &cls, "cls", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17440,7 +17507,7 @@ obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'MatchClass' node")) { goto failed; } - res = obj2ast_pattern(state, tmp2, &val, arena); + res = obj2ast_pattern(state, tmp2, &val, "patterns", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -17478,7 +17545,7 @@ obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'MatchClass' node")) { goto failed; } - res = obj2ast_identifier(state, tmp2, &val, arena); + res = obj2ast_identifier(state, tmp2, &val, "kwd_attrs", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -17516,7 +17583,7 @@ obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'MatchClass' node")) { goto failed; } - res = obj2ast_pattern(state, tmp2, &val, arena); + res = obj2ast_pattern(state, tmp2, &val, "kwd_patterns", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -17554,7 +17621,7 @@ obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'MatchStar' node")) { goto failed; } - res = obj2ast_identifier(state, tmp, &name, arena); + res = obj2ast_identifier(state, tmp, &name, "name", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17585,7 +17652,7 @@ obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'MatchAs' node")) { goto failed; } - res = obj2ast_pattern(state, tmp, &pattern, arena); + res = obj2ast_pattern(state, tmp, &pattern, "pattern", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17602,7 +17669,7 @@ obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'MatchAs' node")) { goto failed; } - res = obj2ast_identifier(state, tmp, &name, arena); + res = obj2ast_identifier(state, tmp, &name, "name", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17646,7 +17713,7 @@ obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'MatchOr' node")) { goto failed; } - res = obj2ast_pattern(state, tmp2, &val, arena); + res = obj2ast_pattern(state, tmp2, &val, "patterns", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -17672,7 +17739,7 @@ obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, int obj2ast_type_ignore(struct ast_state *state, PyObject* obj, type_ignore_ty* - out, PyArena* arena) + out, const char* field, PyArena* arena) { int isinstance; @@ -17683,6 +17750,15 @@ obj2ast_type_ignore(struct ast_state *state, PyObject* obj, type_ignore_ty* *out = NULL; return 0; } + tp = state->type_ignore_type; + isinstance = PyObject_IsInstance(obj, tp); + if (isinstance == -1) { + return 1; + } + if (!isinstance && field != NULL) { + PyErr_Format(PyExc_TypeError, "field '%s' was expecting node of type 'type_ignore', got '%s'", field, _PyType_Name(Py_TYPE(obj))); + return 1; + } tp = state->TypeIgnore_type; isinstance = PyObject_IsInstance(obj, tp); if (isinstance == -1) { @@ -17704,7 +17780,7 @@ obj2ast_type_ignore(struct ast_state *state, PyObject* obj, type_ignore_ty* if (_Py_EnterRecursiveCall(" while traversing 'TypeIgnore' node")) { goto failed; } - res = obj2ast_int(state, tmp, &lineno, arena); + res = obj2ast_int(state, tmp, &lineno, "lineno", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17721,7 +17797,7 @@ obj2ast_type_ignore(struct ast_state *state, PyObject* obj, type_ignore_ty* if (_Py_EnterRecursiveCall(" while traversing 'TypeIgnore' node")) { goto failed; } - res = obj2ast_string(state, tmp, &tag, arena); + res = obj2ast_string(state, tmp, &tag, "tag", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17739,7 +17815,7 @@ obj2ast_type_ignore(struct ast_state *state, PyObject* obj, type_ignore_ty* int obj2ast_type_param(struct ast_state *state, PyObject* obj, type_param_ty* out, - PyArena* arena) + const char* field, PyArena* arena) { int isinstance; @@ -17754,6 +17830,15 @@ obj2ast_type_param(struct ast_state *state, PyObject* obj, type_param_ty* out, *out = NULL; return 0; } + tp = state->type_param_type; + isinstance = PyObject_IsInstance(obj, tp); + if (isinstance == -1) { + return 1; + } + if (!isinstance && field != NULL) { + PyErr_Format(PyExc_TypeError, "field '%s' was expecting node of type 'type_param', got '%s'", field, _PyType_Name(Py_TYPE(obj))); + return 1; + } if (PyObject_GetOptionalAttr(obj, state->lineno, &tmp) < 0) { return -1; } @@ -17766,7 +17851,7 @@ obj2ast_type_param(struct ast_state *state, PyObject* obj, type_param_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'type_param' node")) { goto failed; } - res = obj2ast_int(state, tmp, &lineno, arena); + res = obj2ast_int(state, tmp, &lineno, "lineno", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17783,7 +17868,7 @@ obj2ast_type_param(struct ast_state *state, PyObject* obj, type_param_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'type_param' node")) { goto failed; } - res = obj2ast_int(state, tmp, &col_offset, arena); + res = obj2ast_int(state, tmp, &col_offset, "col_offset", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17800,7 +17885,7 @@ obj2ast_type_param(struct ast_state *state, PyObject* obj, type_param_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'type_param' node")) { goto failed; } - res = obj2ast_int(state, tmp, &end_lineno, arena); + res = obj2ast_int(state, tmp, &end_lineno, "end_lineno", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17817,7 +17902,7 @@ obj2ast_type_param(struct ast_state *state, PyObject* obj, type_param_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'type_param' node")) { goto failed; } - res = obj2ast_int(state, tmp, &end_col_offset, arena); + res = obj2ast_int(state, tmp, &end_col_offset, "end_col_offset", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17844,7 +17929,7 @@ obj2ast_type_param(struct ast_state *state, PyObject* obj, type_param_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'TypeVar' node")) { goto failed; } - res = obj2ast_identifier(state, tmp, &name, arena); + res = obj2ast_identifier(state, tmp, &name, "name", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17861,7 +17946,7 @@ obj2ast_type_param(struct ast_state *state, PyObject* obj, type_param_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'TypeVar' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &bound, arena); + res = obj2ast_expr(state, tmp, &bound, "bound", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17878,7 +17963,8 @@ obj2ast_type_param(struct ast_state *state, PyObject* obj, type_param_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'TypeVar' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &default_value, arena); + res = obj2ast_expr(state, tmp, &default_value, "default_value", + arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17909,7 +17995,7 @@ obj2ast_type_param(struct ast_state *state, PyObject* obj, type_param_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'ParamSpec' node")) { goto failed; } - res = obj2ast_identifier(state, tmp, &name, arena); + res = obj2ast_identifier(state, tmp, &name, "name", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17926,7 +18012,8 @@ obj2ast_type_param(struct ast_state *state, PyObject* obj, type_param_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'ParamSpec' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &default_value, arena); + res = obj2ast_expr(state, tmp, &default_value, "default_value", + arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17957,7 +18044,7 @@ obj2ast_type_param(struct ast_state *state, PyObject* obj, type_param_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'TypeVarTuple' node")) { goto failed; } - res = obj2ast_identifier(state, tmp, &name, arena); + res = obj2ast_identifier(state, tmp, &name, "name", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17974,7 +18061,8 @@ obj2ast_type_param(struct ast_state *state, PyObject* obj, type_param_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'TypeVarTuple' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &default_value, arena); + res = obj2ast_expr(state, tmp, &default_value, "default_value", + arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -18506,7 +18594,7 @@ mod_ty PyAST_obj2mod(PyObject* ast, PyArena* arena, int mode) } mod_ty res = NULL; - if (obj2ast_mod(state, ast, &res, arena) != 0) + if (obj2ast_mod(state, ast, &res, NULL, arena) != 0) return NULL; else return res; From 3b92926176edc51b279f043e86009de1dab5a3ec Mon Sep 17 00:00:00 2001 From: Adorilson Bezerra Date: Thu, 4 Jun 2026 12:39:14 +0100 Subject: [PATCH 017/185] gh-106318: Add doctest role and a 'See also' to the `str.split()` docs (#144367) --- Doc/library/stdtypes.rst | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index ba896212925d89b..f770809dfb4006c 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -2616,7 +2616,9 @@ expression support in the :mod:`re` module). :func:`re.split`). Splitting an empty string with a specified separator returns ``['']``. - For example:: + For example: + + .. doctest:: >>> '1,2,3'.split(',') ['1', '2', '3'] @@ -2634,7 +2636,9 @@ expression support in the :mod:`re` module). string or a string consisting of just whitespace with a ``None`` separator returns ``[]``. - For example:: + For example: + + .. doctest:: >>> '1 2 3'.split() ['1', '2', '3'] @@ -2646,7 +2650,9 @@ expression support in the :mod:`re` module). If *sep* is not specified or is ``None`` and *maxsplit* is ``0``, only leading runs of consecutive whitespace are considered. - For example:: + For example: + + .. doctest:: >>> "".split(None, 0) [] @@ -2655,7 +2661,7 @@ expression support in the :mod:`re` module). >>> " foo ".split(maxsplit=0) ['foo '] - See also :meth:`join`. + See also :meth:`join` and :meth:`rsplit`. .. index:: From 70fd181de048de33ead88b65ed5c356ba97852a2 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Thu, 4 Jun 2026 16:31:31 +0300 Subject: [PATCH 018/185] gh-150750: Fix a race condition in `deque.index` with free-threading (#150779) --- Lib/test/test_deque.py | 16 +++++++++++++ .../test_free_threading/test_collections.py | 24 +++++++++++++++++++ ...-06-02-14-21-46.gh-issue-150750.SVS2o0.rst | 1 + Modules/_collectionsmodule.c | 15 ++++++------ Modules/clinic/_collectionsmodule.c.h | 4 ++-- Modules/posixmodule.c | 4 ++-- 6 files changed, 53 insertions(+), 11 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-06-02-14-21-46.gh-issue-150750.SVS2o0.rst diff --git a/Lib/test/test_deque.py b/Lib/test/test_deque.py index 4e1a489205a6855..3c45032cda91387 100644 --- a/Lib/test/test_deque.py +++ b/Lib/test/test_deque.py @@ -287,6 +287,22 @@ def test_index(self): else: self.assertEqual(d.index(element, start, stop), target) + # Test stop argument + for elem in d: + index = d.index(elem) + self.assertEqual( + index, + d.index(elem, 0), + ) + self.assertEqual( + index, + d.index(elem, 0, len(d)), + ) + self.assertEqual( + index, + d.index(elem, 0, len(d) + 100), + ) + # Test large start argument d = deque(range(0, 10000, 10)) for step in range(100): diff --git a/Lib/test/test_free_threading/test_collections.py b/Lib/test/test_free_threading/test_collections.py index 3a413ccf396d4ba..849b0480e232fc2 100644 --- a/Lib/test/test_free_threading/test_collections.py +++ b/Lib/test/test_free_threading/test_collections.py @@ -24,6 +24,30 @@ def copy_loop(): threading_helper.run_concurrently([mutate, copy_loop]) + def test_index_race_in_ac(self): + # gh-150750: There was a c_default specified as `Py_SIZE(self)`, + # it was used without a critical section. + + d = deque(range(100)) + + def index(): + for _ in range(10000): + try: + d.index(50) + except ValueError: + pass + + def mutate(): + for _ in range(10000): + d.append(0) + d.clear() + d.extend(range(100)) + d.appendleft(-1) + + threading_helper.run_concurrently( + [index, *[mutate for _ in range(3)]], + ) + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Library/2026-06-02-14-21-46.gh-issue-150750.SVS2o0.rst b/Misc/NEWS.d/next/Library/2026-06-02-14-21-46.gh-issue-150750.SVS2o0.rst new file mode 100644 index 000000000000000..bda500383e7cda3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-02-14-21-46.gh-issue-150750.SVS2o0.rst @@ -0,0 +1 @@ +Fix a race condition in :meth:`collections.deque.index` with free-threading. diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c index c5d4879312bc8a8..5ca6362406a78b9 100644 --- a/Modules/_collectionsmodule.c +++ b/Modules/_collectionsmodule.c @@ -1251,7 +1251,7 @@ _collections.deque.index as deque_index deque: dequeobject value as v: object start: object(converter='_PyEval_SliceIndexNotNone', type='Py_ssize_t', c_default='0') = NULL - stop: object(converter='_PyEval_SliceIndexNotNone', type='Py_ssize_t', c_default='Py_SIZE(deque)') = NULL + stop: object(converter='_PyEval_SliceIndexNotNone', type='Py_ssize_t', c_default='PY_SSIZE_T_MAX') = NULL / Return first index of value. @@ -1262,7 +1262,7 @@ Raises ValueError if the value is not present. static PyObject * deque_index_impl(dequeobject *deque, PyObject *v, Py_ssize_t start, Py_ssize_t stop) -/*[clinic end generated code: output=df45132753175ef9 input=90f48833a91e1743]*/ +/*[clinic end generated code: output=df45132753175ef9 input=1c3b19632cf3484f]*/ { Py_ssize_t i, n; PyObject *item; @@ -1270,22 +1270,23 @@ deque_index_impl(dequeobject *deque, PyObject *v, Py_ssize_t start, Py_ssize_t index = deque->leftindex; size_t start_state = deque->state; int cmp; + Py_ssize_t size = Py_SIZE(deque); if (start < 0) { - start += Py_SIZE(deque); + start += size; if (start < 0) start = 0; } if (stop < 0) { - stop += Py_SIZE(deque); + stop += size; if (stop < 0) stop = 0; } - if (stop > Py_SIZE(deque)) - stop = Py_SIZE(deque); + if (stop > size) + stop = size; if (start > stop) start = stop; - assert(0 <= start && start <= stop && stop <= Py_SIZE(deque)); + assert(0 <= start && start <= stop && stop <= size); for (i=0 ; i < start - BLOCKLEN ; i += BLOCKLEN) { b = b->rightlink; diff --git a/Modules/clinic/_collectionsmodule.c.h b/Modules/clinic/_collectionsmodule.c.h index b5c315c680e7821..6c60678a6fbd51a 100644 --- a/Modules/clinic/_collectionsmodule.c.h +++ b/Modules/clinic/_collectionsmodule.c.h @@ -340,7 +340,7 @@ deque_index(PyObject *deque, PyObject *const *args, Py_ssize_t nargs) PyObject *return_value = NULL; PyObject *v; Py_ssize_t start = 0; - Py_ssize_t stop = Py_SIZE(deque); + Py_ssize_t stop = PY_SSIZE_T_MAX; if (!_PyArg_CheckPositional("index", nargs, 1, 3)) { goto exit; @@ -632,4 +632,4 @@ tuplegetter_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=b9d4d647c221cb9f input=a9049054013a1b77]*/ +/*[clinic end generated code: output=f5a388add99d3d15 input=a9049054013a1b77]*/ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index a06479fa60cdba1..f9f53ca5cb5e49b 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -3123,7 +3123,7 @@ class path_t_converter(CConverter): impl_by_reference = True parse_by_reference = True default_type = () - c_init_default = "" # overridden in pre_render(() + c_init_default = "" # overridden in pre_render() converter = 'path_converter' @@ -3266,7 +3266,7 @@ class confname_converter(CConverter): """, argname=argname, converter=self.converter, table=self.table) [python start generated code]*/ -/*[python end generated code: output=da39a3ee5e6b4b0d input=d58f18bdf3bd3565]*/ +/*[python end generated code: output=da39a3ee5e6b4b0d input=ddbf3ac90a981122]*/ /*[clinic input] From d7a242e3e3e3cbc775e5c3f2c081a37b8654e17a Mon Sep 17 00:00:00 2001 From: Jiseok CHOI Date: Thu, 4 Jun 2026 22:41:47 +0900 Subject: [PATCH 019/185] gh-150913: Fix sqlite3.Blob validation for empty slice assignment (GH-150915) ass_subscript_slice() returned early when the computed slice length was zero, bypassing validation performed for non-empty slices. --- Lib/test/test_sqlite3/test_dbapi.py | 12 ++++++++++++ ...26-06-04-21-49-18.gh-issue-150913.EmptyBl.rst | 3 +++ Modules/_sqlite/blob.c | 16 ++++++++++------ 3 files changed, 25 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-06-04-21-49-18.gh-issue-150913.EmptyBl.rst diff --git a/Lib/test/test_sqlite3/test_dbapi.py b/Lib/test/test_sqlite3/test_dbapi.py index 73b40e82a96811f..5f6cb527955ca17 100644 --- a/Lib/test/test_sqlite3/test_dbapi.py +++ b/Lib/test/test_sqlite3/test_dbapi.py @@ -1400,6 +1400,18 @@ def test_blob_set_empty_slice(self): self.blob[0:0] = b"" self.assertEqual(self.blob[:], self.data) + def test_blob_set_empty_slice_wrong_type(self): + with self.assertRaises(TypeError): + self.blob[5:5] = None + + def test_blob_set_empty_slice_wrong_size(self): + with self.assertRaisesRegex(IndexError, "wrong size"): + self.blob[5:5] = b"123" + + def test_blob_set_empty_slice_correct(self): + self.blob[5:5] = b"" + self.assertEqual(self.blob[:], self.data) + def test_blob_set_slice_with_skip(self): self.blob[0:10:2] = b"12345" actual = self.cx.execute("select b from test").fetchone()[0] diff --git a/Misc/NEWS.d/next/Library/2026-06-04-21-49-18.gh-issue-150913.EmptyBl.rst b/Misc/NEWS.d/next/Library/2026-06-04-21-49-18.gh-issue-150913.EmptyBl.rst new file mode 100644 index 000000000000000..f95a6ee6ee15bf7 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-04-21-49-18.gh-issue-150913.EmptyBl.rst @@ -0,0 +1,3 @@ +Fix :class:`sqlite3.Blob` slice assignment to raise +:exc:`TypeError` and :exc:`IndexError` for type and size mismatches +respectively, even when the target slice is empty. diff --git a/Modules/_sqlite/blob.c b/Modules/_sqlite/blob.c index 2cc62751054278f..d81784409e5d91a 100644 --- a/Modules/_sqlite/blob.c +++ b/Modules/_sqlite/blob.c @@ -531,21 +531,25 @@ ass_subscript_slice(pysqlite_Blob *self, PyObject *item, PyObject *value) return -1; } - if (len == 0) { - return 0; - } - Py_buffer vbuf; if (PyObject_GetBuffer(value, &vbuf, PyBUF_SIMPLE) < 0) { return -1; } - int rc = -1; if (vbuf.len != len) { PyErr_SetString(PyExc_IndexError, "Blob slice assignment is wrong size"); + PyBuffer_Release(&vbuf); + return -1; } - else if (step == 1) { + + if (len == 0) { + PyBuffer_Release(&vbuf); + return 0; + } + + int rc = -1; + if (step == 1) { rc = inner_write(self, vbuf.buf, len, start); } else { From 8b847ff1d4c8e1d6d6c06fdfe57a60586b10853b Mon Sep 17 00:00:00 2001 From: Ivan Marton Date: Thu, 4 Jun 2026 15:50:33 +0200 Subject: [PATCH 020/185] gh-84649: Make TimedRotatingFileHandler use CTIME instead of MTIME (GH-24660) The TimedRotatingFileHandler previously only used st_mtime attribute of the log file to detect whether it has to be rotate yet or not. In cases when the file is changed within the rotatation period the st_mtime is also updated to the current time and the rotation never happens. It's more appropriate to check the file creation time (st_ctime) instead. Whenever available, the more appropriate st_birthtime will be in use. (This feature is available on FreeBSD, MacOS and Windows at the moment.) If the st_mtime would be newer than st_ctime (e.g.: because the inode related to the file has been changed without any file content modification), then the earliest attribute will be used. --- Lib/logging/handlers.py | 11 +++- Lib/test/support/__init__.py | 5 ++ Lib/test/test_logging.py | 50 +++++++++++++++++++ Misc/ACKS | 1 + .../2021-02-26-13-17-57.bpo-40469.yJHeQg.rst | 6 +++ 5 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2021-02-26-13-17-57.bpo-40469.yJHeQg.rst diff --git a/Lib/logging/handlers.py b/Lib/logging/handlers.py index 575f2babbc47853..73782f53041008c 100644 --- a/Lib/logging/handlers.py +++ b/Lib/logging/handlers.py @@ -282,7 +282,16 @@ def __init__(self, filename, when='h', interval=1, backupCount=0, # path object (see Issue #27493), but self.baseFilename will be a string filename = self.baseFilename if os.path.exists(filename): - t = int(os.stat(filename).st_mtime) + # Use the minimum of file creation and modification time as + # the base of the rollover calculation + stat_result = os.stat(filename) + # Use st_birthtime whenever it is available or use st_ctime + # instead otherwise + try: + creation_time = stat_result.st_birthtime + except AttributeError: + creation_time = stat_result.st_ctime + t = int(min(creation_time, stat_result.st_mtime)) else: t = int(time.time()) self.rolloverAt = self.computeRollover(t) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index cd85ef60a80f4bf..84f735c1537efa7 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -40,6 +40,7 @@ "has_fork_support", "requires_fork", "has_subprocess_support", "requires_subprocess", "has_socket_support", "requires_working_socket", + "has_st_birthtime", "has_remote_subprocess_debugging", "requires_remote_subprocess_debugging", "anticipate_failure", "load_package_tests", "detect_api_mismatch", "check__all__", "skip_if_buggy_ucrt_strfptime", @@ -620,6 +621,10 @@ def skip_wasi_stack_overflow(): or is_android ) +# At the moment, st_birthtime attribute is only supported on Windows, +# MacOS and FreeBSD. +has_st_birthtime = sys.platform.startswith(("win", "freebsd", "darwin")) + def requires_fork(): return unittest.skipUnless(has_fork_support, "requires working os.fork()") diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 08678119200d427..31c052bfb56cd7a 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -6615,6 +6615,56 @@ def test_rollover(self): print(tf.read()) self.assertTrue(found, msg=msg) + @unittest.skipUnless(support.has_st_birthtime, + "st_birthtime not available or supported by Python on this OS") + def test_rollover_based_on_st_birthtime_only(self): + def add_record(message: str) -> None: + fh = logging.handlers.TimedRotatingFileHandler( + self.fn, when='S', interval=4, encoding="utf-8", backupCount=1) + fmt = logging.Formatter('%(asctime)s %(message)s') + fh.setFormatter(fmt) + record = logging.makeLogRecord({'msg': message}) + fh.emit(record) + fh.close() + + add_record('testing - initial') + self.assertLogFile(self.fn) + # Sleep a little over the half of rollover time - and this value + # must be over 2 seconds, since this is the mtime resolution on + # FAT32 filesystems. + time.sleep(2.1) + add_record('testing - update before rollover to renew the st_mtime') + time.sleep(2.1) # a little over the half of rollover time + add_record('testing - new record supposedly in the new file after rollover') + + # At this point, the log file should be rotated if the rotation + # is based on creation time but should be not if it's based on + # creation time. + found = False + now = datetime.datetime.now() + GO_BACK = 5 # seconds + for secs in range(GO_BACK): + prev = now - datetime.timedelta(seconds=secs) + fn = self.fn + prev.strftime(".%Y-%m-%d_%H-%M-%S") + found = os.path.exists(fn) + if found: + self.rmfiles.append(fn) + break + msg = 'No rotated files found, went back %d seconds' % GO_BACK + if not found: + # print additional diagnostics + dn, fn = os.path.split(self.fn) + files = [f for f in os.listdir(dn) if f.startswith(fn)] + print('Test time: %s' % now.strftime("%Y-%m-%d %H-%M-%S"), file=sys.stderr) + print('The only matching files are: %s' % files, file=sys.stderr) + for f in files: + print('Contents of %s:' % f) + path = os.path.join(dn, f) + print(os.stat(path)) + with open(path, 'r') as tf: + print(tf.read()) + self.assertTrue(found, msg=msg) + def test_rollover_at_midnight(self, weekly=False): os_helper.unlink(self.fn) now = datetime.datetime.now() diff --git a/Misc/ACKS b/Misc/ACKS index 14f0db7549534be..71466e0804ae3c1 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1226,6 +1226,7 @@ Owen Martin Sidney San Martín Westley Martínez Sébastien Martini +Iván Márton Roger Masse Nick Mathewson Simon Mathieu diff --git a/Misc/NEWS.d/next/Library/2021-02-26-13-17-57.bpo-40469.yJHeQg.rst b/Misc/NEWS.d/next/Library/2021-02-26-13-17-57.bpo-40469.yJHeQg.rst new file mode 100644 index 000000000000000..eab474dfd2ea82a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2021-02-26-13-17-57.bpo-40469.yJHeQg.rst @@ -0,0 +1,6 @@ +A bug has been fixed that made the ``TimedRotatingFileHandler`` use the +MTIME attribute of the configured log file to to detect whether it has to be +rotated yet or not. In cases when the file was changed within the rotation +period the value of the MTIME was also updated to the current time and as a +result the rotation never happened. The file creation time (CTIME) is used +instead that makes the rotation file modification independent. From 0b6eb236fc4352325f9bb7c97d139cef1a88685d Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Thu, 4 Jun 2026 15:11:33 +0100 Subject: [PATCH 021/185] GH-148960: Ensure that asserts are ignored if `NDEBUG` is set (#150916) --- Tools/jit/template.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Tools/jit/template.c b/Tools/jit/template.c index 72379165f3b610e..f1e2e25aaecdada 100644 --- a/Tools/jit/template.c +++ b/Tools/jit/template.c @@ -1,8 +1,10 @@ #include "Python.h" +#ifndef NDEBUG #undef assert #define assert(TEST) ((TEST) ? 0 : _Py_jit_assertion_failure(__LINE__)) +#endif #include "pycore_backoff.h" #include "pycore_call.h" @@ -40,8 +42,10 @@ #include "jit.h" +#ifndef NDEBUG #undef assert #define assert(TEST) ((TEST) ? 0 : _Py_jit_assertion_failure(__LINE__)) +#endif #undef CURRENT_OPERAND0_64 #define CURRENT_OPERAND0_64() (_operand0_64) From 2f8edef13512cde6b54201888b500642dd35a0fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bern=C3=A1t=20G=C3=A1bor?= Date: Thu, 4 Jun 2026 09:15:12 -0700 Subject: [PATCH 022/185] gh-150818: Speed up logging.getLogger() for existing loggers (GH-150825) --- Lib/logging/__init__.py | 9 ++++++++- .../2026-06-02-15-44-58.gh-issue-150818.Orcefu.rst | 3 +++ 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2026-06-02-15-44-58.gh-issue-150818.Orcefu.rst diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py index 6eef90ae5cd9143..9febc50b1264ef4 100644 --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -1373,9 +1373,16 @@ def getLogger(self, name): logger and fix up the parent/child references which pointed to the placeholder to now point to the logger. """ - rv = None if not isinstance(name, str): raise TypeError('A logger name must be a string') + # Fast path: an already-registered, non-placeholder logger can be + # returned without taking the lock. dict.get() is atomic under both + # the GIL and free threading, and a Logger is fully initialised before + # being inserted into loggerDict under the lock, so this never sees a + # partially-constructed object. + rv = self.loggerDict.get(name) + if rv is not None and not isinstance(rv, PlaceHolder): + return rv with _lock: if name in self.loggerDict: rv = self.loggerDict[name] diff --git a/Misc/NEWS.d/next/Library/2026-06-02-15-44-58.gh-issue-150818.Orcefu.rst b/Misc/NEWS.d/next/Library/2026-06-02-15-44-58.gh-issue-150818.Orcefu.rst new file mode 100644 index 000000000000000..3bb16001cc0bb41 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-02-15-44-58.gh-issue-150818.Orcefu.rst @@ -0,0 +1,3 @@ +Speed up :func:`logging.getLogger` with a lock-free fast path that returns an +already-registered logger without acquiring the logging lock. Patch by Bernát +Gábor. From ea0dbd15ad423b50deae2f5f1c9d849537766db8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bern=C3=A1t=20G=C3=A1bor?= Date: Thu, 4 Jun 2026 12:58:20 -0700 Subject: [PATCH 023/185] gh-150817: Speed up Flag bitwise operations (GH-150824) Flag.__or__, __and__ and __xor__ walked both operands on every call to reject None values. Run that scan only when one of the operand values is actually None, so valid combinations skip it. The TypeError and its message are unchanged for the invalid cases. --- Lib/enum.py | 24 ++++++++++++------- ...-06-02-15-44-57.gh-issue-150817.lpFCh0.rst | 2 ++ 2 files changed, 17 insertions(+), 9 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-06-02-15-44-57.gh-issue-150817.lpFCh0.rst diff --git a/Lib/enum.py b/Lib/enum.py index f97a5193492e602..7aff36c94ce1dcf 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -1620,10 +1620,14 @@ def __or__(self, other): if other_value is NotImplemented: return NotImplemented - for flag in self, other: - if self._get_value(flag) is None: - raise TypeError(f"'{flag}' cannot be combined with other flags with |") value = self._value_ + # _get_value(self) is self._value_ and _get_value(other) is + # other_value, so only walk the operands (to raise on the offending + # flag) when one of those values is actually None. + if value is None or other_value is None: + for flag in self, other: + if self._get_value(flag) is None: + raise TypeError(f"'{flag}' cannot be combined with other flags with |") return self.__class__(value | other_value) def __and__(self, other): @@ -1631,10 +1635,11 @@ def __and__(self, other): if other_value is NotImplemented: return NotImplemented - for flag in self, other: - if self._get_value(flag) is None: - raise TypeError(f"'{flag}' cannot be combined with other flags with &") value = self._value_ + if value is None or other_value is None: + for flag in self, other: + if self._get_value(flag) is None: + raise TypeError(f"'{flag}' cannot be combined with other flags with &") return self.__class__(value & other_value) def __xor__(self, other): @@ -1642,10 +1647,11 @@ def __xor__(self, other): if other_value is NotImplemented: return NotImplemented - for flag in self, other: - if self._get_value(flag) is None: - raise TypeError(f"'{flag}' cannot be combined with other flags with ^") value = self._value_ + if value is None or other_value is None: + for flag in self, other: + if self._get_value(flag) is None: + raise TypeError(f"'{flag}' cannot be combined with other flags with ^") return self.__class__(value ^ other_value) def __invert__(self): diff --git a/Misc/NEWS.d/next/Library/2026-06-02-15-44-57.gh-issue-150817.lpFCh0.rst b/Misc/NEWS.d/next/Library/2026-06-02-15-44-57.gh-issue-150817.lpFCh0.rst new file mode 100644 index 000000000000000..42532bb0222cf4e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-02-15-44-57.gh-issue-150817.lpFCh0.rst @@ -0,0 +1,2 @@ +Speed up the ``|``, ``&`` and ``^`` operations on :class:`enum.Flag` members. +Patch by Bernát Gábor. From f4846849b588c2000c0305fd4f6a381141a99e5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bern=C3=A1t=20G=C3=A1bor?= Date: Thu, 4 Jun 2026 13:08:58 -0700 Subject: [PATCH 024/185] gh-150886: Remove unused importlib._bootstrap._object_name (#150884) Introduced in GH-23469 (bpo-26131, "Deprecate usage of load_module()") to render an object's qualified name inside the load_module() deprecation warnings. Orphaned by gh-142205 (GH-97850, "Remove all uses and definitions of load_module() from importlib"), which deleted the warning-building call sites f"{_object_name(spec.loader)}.exec_module() not found; ..." and left the helper with no caller. A word-boundary search across Lib, Modules, Python, Objects and Include finds zero references outside its own definition, and a GitHub code search finds no downstream importers. The frozen importlib was regenerated; importlib._bootstrap._object_name no longer exists at runtime and the full test_importlib suite passes. --- Lib/importlib/_bootstrap.py | 6 ------ .../Library/2026-06-03-21-59-11.gh-issue-150886.r2c25g.rst | 4 ++++ 2 files changed, 4 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-06-03-21-59-11.gh-issue-150886.r2c25g.rst diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py index 06dc45d71d7ca4a..eb1686a5c8217c6 100644 --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -20,12 +20,6 @@ # reference any injected objects! This includes not only global code but also # anything specified at the class level. -def _object_name(obj): - try: - return obj.__qualname__ - except AttributeError: - return type(obj).__qualname__ - # Bootstrap-related code ###################################################### # Modules injected manually by _setup() diff --git a/Misc/NEWS.d/next/Library/2026-06-03-21-59-11.gh-issue-150886.r2c25g.rst b/Misc/NEWS.d/next/Library/2026-06-03-21-59-11.gh-issue-150886.r2c25g.rst new file mode 100644 index 000000000000000..b220b63cfdb4ab5 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-03-21-59-11.gh-issue-150886.r2c25g.rst @@ -0,0 +1,4 @@ +Remove the private, undocumented function +``importlib._bootstrap._object_name()``. It had no caller after +``load_module()`` and its deprecation warnings were removed from +:mod:`importlib`. From 81f47ce07f0d8aa2a2997a5fe9a6a18fde9fd745 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Thu, 4 Jun 2026 21:37:18 +0100 Subject: [PATCH 025/185] Add shebang documentation for PyManager 26.3b1 (GH-150931) --- Doc/using/windows.rst | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/Doc/using/windows.rst b/Doc/using/windows.rst index eea1e2f64a468d0..5b715d4614dad8f 100644 --- a/Doc/using/windows.rst +++ b/Doc/using/windows.rst @@ -455,6 +455,12 @@ customization. Python runtimes, or false to prevent it. By default, true. + * - ``shebang_templates`` + - (none) + - Mapping from shebang line template to alternative command, such as + ``py -V:`` or a substitute string. + See :ref:`pymanager-shebang` for more details. + * - ``log_level`` - ``PYMANAGER_VERBOSE``, ``PYMANAGER_DEBUG`` - Set the default level of output (0-50). @@ -568,6 +574,30 @@ which the path to the script and any additional arguments will be appended. This functionality may be disabled by the ``shebang_can_run_anything`` configuration option. +Since version 26.3 of the Python install manager, custom shebang templates may +be added to your configuration file. Add the ``shebang_templates`` object with +one member for each template (the string to match) and the command to use when +the template is matched. Most commands should be ``py -V:`` (or ``pyw``) to +launch one of your installed runtimes. The ``py -3.`` form is also +allowed, as is a plain ``py`` to launch the default. No other arguments are +supported. + +.. code:: json5 + + { + "shebang_templates": { + "/usr/bin/python": "py", + "/usr/bin/my_custom_python": "py -V:MyCustomPython/3" + } + } + +If the substitute command is not ``py`` or ``pyw``, it will be written back into +the shebang and regular handling continues. If launching arbitrary executables +is permitted, then providing a full path will allow you to redirect from Python +to any executable. The template should match either the entire line (ignoring +leading and trailing whitespace), or up to the first space in the shebang line. + + .. note:: The behaviour of shebangs in the Python install manager is subtly different From 0e63794235ede597fd6712d87f669cd8d27250be Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Thu, 4 Jun 2026 22:59:43 -0700 Subject: [PATCH 026/185] gh-145177: Bump emscripten version to 4.0.19 (#150926) Bumpts the emscripten version to 4.0.19. --- Lib/test/test_platform.py | 2 +- Platforms/emscripten/config.toml | 2 +- Platforms/emscripten/streams.mjs | 6 +++++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py index 9ee97b922ad48e1..63c130813ec4972 100644 --- a/Lib/test/test_platform.py +++ b/Lib/test/test_platform.py @@ -534,7 +534,7 @@ def test_ios_ver(self): def test_libc_ver(self): if support.is_emscripten: - assert platform.libc_ver() == ("emscripten", "4.0.12") + assert platform.libc_ver() == ("emscripten", "4.0.19") return # check that libc_ver(executable) doesn't raise an exception if os.path.isdir(sys.executable) and \ diff --git a/Platforms/emscripten/config.toml b/Platforms/emscripten/config.toml index ba2dc8f4a482bfa..401e9396ddbb009 100644 --- a/Platforms/emscripten/config.toml +++ b/Platforms/emscripten/config.toml @@ -1,7 +1,7 @@ # Any data that can vary between Python versions is to be kept in this file. # This allows for blanket copying of the Emscripten build code between supported # Python versions. -emscripten-version = "4.0.12" +emscripten-version = "4.0.19" node-version = "24" test-args = [ "-m", "test", diff --git a/Platforms/emscripten/streams.mjs b/Platforms/emscripten/streams.mjs index 76ad79f9247f4cf..1b121d48d4e76c6 100644 --- a/Platforms/emscripten/streams.mjs +++ b/Platforms/emscripten/streams.mjs @@ -112,7 +112,7 @@ const prepareBuffer = (buffer, offset, length) => const TTY_OPS = { ioctl_tiocgwinsz(tty) { - return tty.devops.ioctl_tiocgwinsz?.(); + return tty.devops.ioctl_tiocgwinsz?.() ?? [24, 80]; }, }; @@ -188,6 +188,10 @@ class NodeReader { fsync() { nodeFsync(this.nodeStream.fd); } + + ioctl_tiocgwinsz() { + return [this.nodeStream.rows ?? 24, this.nodeStream.columns ?? 80]; + } } class NodeWriter { From b85a74b56d77f3ef2f89324c92b5ca9490b56c44 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 5 Jun 2026 10:58:20 +0300 Subject: [PATCH 027/185] gh-62825: Fix encoding aliases "KS_C_5601-1987", "KS X 1001", etc (GH-150933) They are now aliases of CP949 instead of EUC-KR. --- Lib/encodings/aliases.py | 12 ++++++------ .../2026-06-04-23-10-31.gh-issue-62825.BtG_yQ.rst | 2 ++ 2 files changed, 8 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-06-04-23-10-31.gh-issue-62825.BtG_yQ.rst diff --git a/Lib/encodings/aliases.py b/Lib/encodings/aliases.py index e5e50630f33d14d..df4c230fbf9c4e4 100644 --- a/Lib/encodings/aliases.py +++ b/Lib/encodings/aliases.py @@ -226,6 +226,12 @@ # cp949 codec '949' : 'cp949', + 'korean' : 'cp949', + 'ksc5601' : 'cp949', + 'ks_c_5601' : 'cp949', + 'ks_c_5601_1987' : 'cp949', + 'ksx1001' : 'cp949', + 'ks_x_1001' : 'cp949', 'ms949' : 'cp949', 'uhc' : 'cp949', @@ -248,12 +254,6 @@ # euc_kr codec 'euckr' : 'euc_kr', - 'korean' : 'euc_kr', - 'ksc5601' : 'euc_kr', - 'ks_c_5601' : 'euc_kr', - 'ks_c_5601_1987' : 'euc_kr', - 'ksx1001' : 'euc_kr', - 'ks_x_1001' : 'euc_kr', 'cseuckr' : 'euc_kr', # gb18030 codec diff --git a/Misc/NEWS.d/next/Library/2026-06-04-23-10-31.gh-issue-62825.BtG_yQ.rst b/Misc/NEWS.d/next/Library/2026-06-04-23-10-31.gh-issue-62825.BtG_yQ.rst new file mode 100644 index 000000000000000..95a4fb1c61d4c30 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-04-23-10-31.gh-issue-62825.BtG_yQ.rst @@ -0,0 +1,2 @@ +Encodings "KS_C_5601-1987", "KS X 1001", etc are now aliases of "CP949" +instead of "EUC-KR". From 4096eceaf84a54d9505a46e0cec8e8021d9037ac Mon Sep 17 00:00:00 2001 From: sobolevn Date: Fri, 5 Jun 2026 11:03:57 +0300 Subject: [PATCH 028/185] gh-150899: Do not reset custom `-Xlazy_imports` mode in `test_lazy_imports` (#150900) --- Lib/test/test_lazy_import/__init__.py | 230 ++++---------------------- 1 file changed, 32 insertions(+), 198 deletions(-) diff --git a/Lib/test/test_lazy_import/__init__.py b/Lib/test/test_lazy_import/__init__.py index 321733a4fdf1705..94576a206b42caa 100644 --- a/Lib/test/test_lazy_import/__init__.py +++ b/Lib/test/test_lazy_import/__init__.py @@ -21,8 +21,10 @@ _testcapi = None -class LazyImportTests(unittest.TestCase): - """Tests for basic lazy import functionality.""" +class LazyImportTestCase(unittest.TestCase): + def setUp(self): + self.lazy_imports_filter = sys.get_lazy_imports_filter() + self.lazy_imports = sys.get_lazy_imports() def tearDown(self): """Clean up any test modules from sys.modules.""" @@ -30,10 +32,14 @@ def tearDown(self): if key.startswith('test.test_lazy_import.data'): del sys.modules[key] - sys.set_lazy_imports_filter(None) - sys.set_lazy_imports("normal") + sys.set_lazy_imports_filter(self.lazy_imports_filter) + sys.set_lazy_imports(self.lazy_imports) sys.lazy_modules.clear() + +class LazyImportTests(LazyImportTestCase): + """Tests for basic lazy import functionality.""" + def test_basic_unused(self): """Lazy imported module should not be loaded if never accessed.""" import test.test_lazy_import.data.basic_unused @@ -162,17 +168,9 @@ def test_from_import_with_imported_module_getattr(self): assert_python_ok("-c", code) -class GlobalLazyImportModeTests(unittest.TestCase): +class GlobalLazyImportModeTests(LazyImportTestCase): """Tests for sys.set_lazy_imports() global mode control.""" - def tearDown(self): - for key in list(sys.modules.keys()): - if key.startswith('test.test_lazy_import.data'): - del sys.modules[key] - - sys.set_lazy_imports_filter(None) - sys.set_lazy_imports("normal") - def test_global_off_rejected(self): """Mode 'none' is not supported.""" with self.assertRaises(ValueError): @@ -204,17 +202,9 @@ def test_global_filter_from_true(self): self.assertNotIn("test.test_lazy_import.data.basic2", sys.modules) -class CompatibilityModeTests(unittest.TestCase): +class CompatibilityModeTests(LazyImportTestCase): """Tests for __lazy_modules__ compatibility mode.""" - def tearDown(self): - for key in list(sys.modules.keys()): - if key.startswith('test.test_lazy_import.data'): - del sys.modules[key] - - sys.set_lazy_imports_filter(None) - sys.set_lazy_imports("normal") - def test_compatibility_mode(self): """__lazy_modules__ should enable lazy imports for listed modules.""" import test.test_lazy_import.data.basic_compatibility_mode @@ -241,17 +231,9 @@ def test_compatibility_mode_relative(self): self.assertNotIn("test.test_lazy_import.data.basic2", sys.modules) -class ModuleIntrospectionTests(unittest.TestCase): +class ModuleIntrospectionTests(LazyImportTestCase): """Tests for module dict and getattr behavior with lazy imports.""" - def tearDown(self): - for key in list(sys.modules.keys()): - if key.startswith('test.test_lazy_import.data'): - del sys.modules[key] - - sys.set_lazy_imports_filter(None) - sys.set_lazy_imports("normal") - def test_modules_dict(self): """Accessing module.__dict__ should not trigger reification.""" import test.test_lazy_import.data.modules_dict @@ -268,17 +250,9 @@ def test_modules_getattr_other(self): self.assertNotIn("test.test_lazy_import.data.basic2", sys.modules) -class LazyImportTypeTests(unittest.TestCase): +class LazyImportTypeTests(LazyImportTestCase): """Tests for the LazyImportType and its resolve() method.""" - def tearDown(self): - for key in list(sys.modules.keys()): - if key.startswith('test.test_lazy_import.data'): - del sys.modules[key] - - sys.set_lazy_imports_filter(None) - sys.set_lazy_imports("normal") - def test_lazy_value_resolve(self): """resolve() method should force the lazy import to load.""" import test.test_lazy_import.data.lazy_get_value @@ -304,17 +278,9 @@ def test_lazy_import_type_attributes_accessible(self): self.assertIn(b" bool """ - def tearDown(self): - for key in list(sys.modules.keys()): - if key.startswith('test.test_lazy_import.data'): - del sys.modules[key] - - sys.set_lazy_imports_filter(None) - sys.set_lazy_imports("normal") - def _run_subprocess_with_modules(self, code, files): with tempfile.TemporaryDirectory() as tmpdir: for relpath, contents in files.items(): @@ -1611,17 +1497,9 @@ def my_filter(importer, name, fromlist): self.assertIn("OK", result.stdout) -class AdditionalSyntaxRestrictionTests(unittest.TestCase): +class AdditionalSyntaxRestrictionTests(LazyImportTestCase): """Additional syntax restriction tests per PEP 810.""" - def tearDown(self): - for key in list(sys.modules.keys()): - if key.startswith('test.test_lazy_import.data'): - del sys.modules[key] - - sys.set_lazy_imports_filter(None) - sys.set_lazy_imports("normal") - def test_lazy_import_inside_class_raises_syntax_error(self): """lazy import inside class body should raise SyntaxError.""" # PEP 810: "The soft keyword is only allowed at the global (module) level, @@ -1631,7 +1509,7 @@ def test_lazy_import_inside_class_raises_syntax_error(self): @support.requires_subprocess() -class MixedLazyEagerImportTests(unittest.TestCase): +class MixedLazyEagerImportTests(LazyImportTestCase): """Tests for mixing lazy and eager imports of the same module. PEP 810: "If module foo is imported both lazily and eagerly in the same @@ -1639,14 +1517,6 @@ class MixedLazyEagerImportTests(unittest.TestCase): the same module object." """ - def tearDown(self): - for key in list(sys.modules.keys()): - if key.startswith('test.test_lazy_import.data'): - del sys.modules[key] - - sys.set_lazy_imports_filter(None) - sys.set_lazy_imports("normal") - def test_eager_import_before_lazy_resolves_to_same_module(self): """Eager import before lazy should make lazy resolve to same module.""" code = textwrap.dedent(""" @@ -1692,17 +1562,9 @@ def test_lazy_import_before_eager_resolves_to_same_module(self): self.assertIn("OK", result.stdout) -class RelativeImportTests(unittest.TestCase): +class RelativeImportTests(LazyImportTestCase): """Tests for relative imports with lazy keyword.""" - def tearDown(self): - for key in list(sys.modules.keys()): - if key.startswith('test.test_lazy_import.data'): - del sys.modules[key] - - sys.set_lazy_imports_filter(None) - sys.set_lazy_imports("normal") - def test_relative_lazy_import(self): """lazy from . import submodule should work.""" from test.test_lazy_import.data import relative_lazy @@ -1727,21 +1589,13 @@ def test_relative_lazy_from_import(self): self.assertIn("test.test_lazy_import.data.basic2", sys.modules) -class LazyModulesCompatibilityFromImportTests(unittest.TestCase): +class LazyModulesCompatibilityFromImportTests(LazyImportTestCase): """Tests for __lazy_modules__ with from imports. PEP 810: "When a module is made lazy this way, from-imports using that module are also lazy" """ - def tearDown(self): - for key in list(sys.modules.keys()): - if key.startswith('test.test_lazy_import.data'): - del sys.modules[key] - - sys.set_lazy_imports_filter(None) - sys.set_lazy_imports("normal") - def test_lazy_modules_makes_from_imports_lazy(self): """__lazy_modules__ should make from imports of listed modules lazy.""" from test.test_lazy_import.data import lazy_compat_from @@ -1756,7 +1610,7 @@ def test_lazy_modules_makes_from_imports_lazy(self): @support.requires_subprocess() -class ImportStateAtReificationTests(unittest.TestCase): +class ImportStateAtReificationTests(LazyImportTestCase): """Tests for import system state at reification time. PEP 810: "Reification still calls __import__ to resolve the import, which uses @@ -1765,14 +1619,6 @@ class ImportStateAtReificationTests(unittest.TestCase): statement was evaluated." """ - def tearDown(self): - for key in list(sys.modules.keys()): - if key.startswith('test.test_lazy_import.data'): - del sys.modules[key] - - sys.set_lazy_imports_filter(None) - sys.set_lazy_imports("normal") - def test_sys_path_at_reification_time_is_used(self): """sys.path changes after lazy import should affect reification.""" code = textwrap.dedent(""" @@ -1815,17 +1661,9 @@ def test_sys_path_at_reification_time_is_used(self): @support.requires_subprocess() -class ThreadSafetyTests(unittest.TestCase): +class ThreadSafetyTests(LazyImportTestCase): """Tests for thread-safety of lazy imports.""" - def tearDown(self): - for key in list(sys.modules.keys()): - if key.startswith('test.test_lazy_import.data'): - del sys.modules[key] - - sys.set_lazy_imports_filter(None) - sys.set_lazy_imports("normal") - def test_concurrent_lazy_import_reification(self): """Multiple threads racing to reify the same lazy import should succeed.""" from test.test_lazy_import.data import basic_unused @@ -2087,11 +1925,7 @@ def test_normal_import_dis(self): @unittest.skipIf(_testcapi is None, 'need the _testcapi module') -class LazyCApiTests(unittest.TestCase): - def tearDown(self): - sys.set_lazy_imports("normal") - sys.set_lazy_imports_filter(None) - +class LazyCApiTests(LazyImportTestCase): def test_set_matches_sys(self): self.assertEqual(_testcapi.PyImport_GetLazyImportsMode(), sys.get_lazy_imports()) for mode in ("normal", "all"): From d0538893efb4b7464025fdef0225e8675207e371 Mon Sep 17 00:00:00 2001 From: Cody Maloney Date: Fri, 5 Jun 2026 05:00:59 -0700 Subject: [PATCH 029/185] gh-129011: Update docs for Raw I/O read, readinto, and write (#135328) Update `RawIOBase` and `FileIO` documentation to match implementation behavior around `.read`, `.readinto`, `.readall` and `.write`. In particular: - They may make more than one system call (PEP-475) - Add warnings if `.write()` requires a wrapping retry loop (see: gh-126606) - "Raw I/O" `.write`` may not write all bytes - `buffering=0` example results in a "Raw I/O" Co-authored-by: Serhiy Storchaka Co-authored-by: Victor Stinner --- Doc/library/io.rst | 50 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/Doc/library/io.rst b/Doc/library/io.rst index 8c0eed592dd49ed..d47b74efe22de9d 100644 --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -38,6 +38,7 @@ will raise a :exc:`TypeError`. So will giving a :class:`bytes` object to the Operations that used to raise :exc:`IOError` now raise :exc:`OSError`, since :exc:`IOError` is now an alias of :exc:`OSError`. +.. _text-io: Text I/O ^^^^^^^^ @@ -65,6 +66,7 @@ In-memory text streams are also available as :class:`StringIO` objects:: The text stream API is described in detail in the documentation of :class:`TextIOBase`. +.. _binary-io: Binary I/O ^^^^^^^^^^ @@ -103,6 +105,13 @@ stream by opening a file in binary mode with buffering disabled:: The raw stream API is described in detail in the docs of :class:`RawIOBase`. +.. warning:: + Raw I/O is a low-level interface and methods generally must have their return + values checked and be explicitly retried to ensure an operation completes. + For instance :meth:`~RawIOBase.write` returns the number of bytes written + which may be less than the number of bytes provided (a partial write). + High-level I/O objects like :ref:`binary-io` and :ref:`text-io` implement + retry behavior. .. _io-text-encoding: @@ -478,8 +487,11 @@ I/O Base Classes Read up to *size* bytes from the object and return them. As a convenience, if *size* is unspecified or -1, all bytes until EOF are returned. - Otherwise, only one system call is ever made. Fewer than *size* bytes may - be returned if the operating system call returns fewer than *size* bytes. + + Attempts to make only one system call but will retry if interrupted and + the signal handler does not raise an exception (see :pep:`475` for the + rationale). This means fewer than *size* bytes may be returned if the + operating system call returns fewer than *size* bytes. If 0 bytes are returned, and *size* was not 0, this indicates end of file. If the object is in non-blocking mode and no bytes are available, @@ -493,13 +505,19 @@ I/O Base Classes Read and return all the bytes from the stream until EOF, using multiple calls to the stream if necessary. + If ``0`` bytes are returned this indicates end of file. If the object is in + non-blocking mode and the underlying :meth:`read` returns ``None`` + indicating no bytes are available, ``None`` is returned. + .. method:: readinto(b, /) Read bytes into a pre-allocated, writable :term:`bytes-like object` *b*, and return the number of bytes read. For example, *b* might be a :class:`bytearray`. - If the object is in non-blocking mode and no bytes - are available, ``None`` is returned. + + If ``0`` is returned and ``len(b)`` is not ``0``, this indicates end of file. If + the object is in non-blocking mode and no bytes are available, ``None`` is + returned. .. method:: write(b, /) @@ -513,6 +531,13 @@ I/O Base Classes this method returns, so the implementation should only access *b* during the method call. + .. warning:: + + This function does not ensure all bytes are written or an exception is + thrown. Callers may implement that behavior by checking the return + value and, if it is less than the length of *b*, looping with additional + write calls until all unwritten bytes are written. High-level I/O + objects like :ref:`binary-io` and :ref:`text-io` implement retry behavior. .. class:: BufferedIOBase @@ -641,7 +666,11 @@ Raw File I/O .. class:: FileIO(name, mode='r', closefd=True, opener=None) A raw binary stream representing an OS-level file containing bytes data. It - inherits from :class:`RawIOBase`. + inherits from :class:`RawIOBase` and implements its low-level access design. + This means :meth:`~RawIOBase.write` does not guarantee all bytes are written + and :meth:`~RawIOBase.read` may read less bytes than requested even when more + bytes may be present in the underlying file. To get "write all" and + "read at least" behavior, use :ref:`binary-io`. The *name* can be one of two things: @@ -661,10 +690,6 @@ Raw File I/O implies writing, so this mode behaves in a similar way to ``'w'``. Add a ``'+'`` to the mode to allow simultaneous reading and writing. - The :meth:`~RawIOBase.read` (when called with a positive argument), - :meth:`~RawIOBase.readinto` and :meth:`~RawIOBase.write` methods on this - class will only make one system call. - A custom opener can be used by passing a callable as *opener*. The underlying file descriptor for the file object is then obtained by calling *opener* with (*name*, *flags*). *opener* must return an open file descriptor (passing @@ -676,6 +701,13 @@ Raw File I/O See the :func:`open` built-in function for examples on using the *opener* parameter. + .. warning:: + :class:`FileIO` is a low-level I/O object and members, such as + :meth:`~RawIOBase.read` and :meth:`~RawIOBase.write`, need to have their + return values checked explicitly in a retry loop to implement "write all" + and "read at least" behavior. High-level I/O objects :ref:`binary-io` and + :ref:`text-io` implement retry behavior. + .. versionchanged:: 3.3 The *opener* parameter was added. The ``'x'`` mode was added. From 264450829b2e4b73b4964fa9e5aa72869bd7d786 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 5 Jun 2026 15:08:04 +0300 Subject: [PATCH 030/185] gh-149891: Add more encoding aliases (GH-149892) Support all aliases officially registered in IANA, except Extended_UNIX_Code_Packed_Format_for_Japanese. New names: KSC_5601, KS_C_5601-1989, iso-ir-149, GB_2312-80, windows-936, mac, CCSID00858, CCSID01140, and a number of "cs"-prefixed names. Fix csHPRoman8, which was not normalized. --- Lib/encodings/aliases.py | 57 +++++++++++++++++-- ...-05-15-19-52-41.gh-issue-149891.BJUIGB.rst | 1 + 2 files changed, 53 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-05-15-19-52-41.gh-issue-149891.BJUIGB.rst diff --git a/Lib/encodings/aliases.py b/Lib/encodings/aliases.py index df4c230fbf9c4e4..ef51168d755ba98 100644 --- a/Lib/encodings/aliases.py +++ b/Lib/encodings/aliases.py @@ -43,6 +43,7 @@ # big5hkscs codec 'big5_hkscs' : 'big5hkscs', + 'csbig5hkscs' : 'big5hkscs', 'hkscs' : 'big5hkscs', # bz2_codec codec @@ -71,6 +72,7 @@ # cp1140 codec '1140' : 'cp1140', + 'ccsid01140' : 'cp1140', 'cp01140' : 'cp1140', 'csibm01140' : 'cp1140', 'ebcdic_us_37_euro' : 'cp1140', @@ -79,38 +81,47 @@ # cp1250 codec '1250' : 'cp1250', + 'cswindows1250' : 'cp1250', 'windows_1250' : 'cp1250', # cp1251 codec '1251' : 'cp1251', + 'cswindows1251' : 'cp1251', 'windows_1251' : 'cp1251', # cp1252 codec '1252' : 'cp1252', + 'cswindows1252' : 'cp1252', 'windows_1252' : 'cp1252', # cp1253 codec '1253' : 'cp1253', + 'cswindows1253' : 'cp1253', 'windows_1253' : 'cp1253', # cp1254 codec '1254' : 'cp1254', + 'cswindows1254' : 'cp1254', 'windows_1254' : 'cp1254', # cp1255 codec '1255' : 'cp1255', + 'cswindows1255' : 'cp1255', 'windows_1255' : 'cp1255', # cp1256 codec '1256' : 'cp1256', + 'cswindows1256' : 'cp1256', 'windows_1256' : 'cp1256', # cp1257 codec '1257' : 'cp1257', + 'cswindows1257' : 'cp1257', 'windows_1257' : 'cp1257', # cp1258 codec '1258' : 'cp1258', + 'cswindows1258' : 'cp1258', 'windows_1258' : 'cp1258', # cp273 codec @@ -163,6 +174,7 @@ # cp858 codec '858' : 'cp858', + 'ccsid00858' : 'cp858', 'cp00858' : 'cp858', 'csibm00858' : 'cp858', 'csibm858' : 'cp858', @@ -214,11 +226,13 @@ # cp874 codec '874' : 'cp874', + 'cswindows874' : 'cp874', 'ms874' : 'cp874', 'windows_874' : 'cp874', # cp932 codec '932' : 'cp932', + 'cswindows31j' : 'cp932', 'ms932' : 'cp932', 'mskanji' : 'cp932', 'ms_kanji' : 'cp932', @@ -226,10 +240,14 @@ # cp949 codec '949' : 'cp949', + 'csksc56011987' : 'cp949', + 'iso_ir_149' : 'cp949', 'korean' : 'cp949', + 'ks_c_5601_1987' : 'cp949', + 'ks_c_5601_1989' : 'cp949', 'ksc5601' : 'cp949', 'ks_c_5601' : 'cp949', - 'ks_c_5601_1987' : 'cp949', + 'ksc_5601' : 'cp949', 'ksx1001' : 'cp949', 'ks_x_1001' : 'cp949', 'ms949' : 'cp949', @@ -248,41 +266,47 @@ 'eucjisx0213' : 'euc_jisx0213', # euc_jp codec + 'cseucpkdfmtjapanese' : 'euc_jp', 'eucjp' : 'euc_jp', 'ujis' : 'euc_jp', 'u_jis' : 'euc_jp', # euc_kr codec - 'euckr' : 'euc_kr', 'cseuckr' : 'euc_kr', + 'euckr' : 'euc_kr', # gb18030 codec + 'csgb18030' : 'gb18030', 'gb18030_2000' : 'gb18030', # gb2312 codec 'chinese' : 'gb2312', + 'csgb2312' : 'gb2312', 'csiso58gb231280' : 'gb2312', 'euc_cn' : 'gb2312', 'euccn' : 'gb2312', 'eucgb2312_cn' : 'gb2312', 'gb2312_1980' : 'gb2312', 'gb2312_80' : 'gb2312', + 'gb_2312_80' : 'gb2312', 'iso_ir_58' : 'gb2312', # gbk codec '936' : 'gbk', 'cp936' : 'gbk', + 'csgbk' : 'gbk', 'ms936' : 'gbk', + 'windows_936' : 'gbk', # hex_codec codec 'hex' : 'hex_codec', # hp_roman8 codec - 'roman8' : 'hp_roman8', - 'r8' : 'hp_roman8', - 'csHPRoman8' : 'hp_roman8', 'cp1051' : 'hp_roman8', + 'cshproman8' : 'hp_roman8', 'ibm1051' : 'hp_roman8', + 'r8' : 'hp_roman8', + 'roman8' : 'hp_roman8', # hz codec 'hzgb' : 'hz', @@ -299,6 +323,7 @@ 'iso_2022_jp_1' : 'iso2022_jp_1', # iso2022_jp_2 codec + 'csiso2022jp2' : 'iso2022_jp_2', 'iso2022jp_2' : 'iso2022_jp_2', 'iso_2022_jp_2' : 'iso2022_jp_2', @@ -334,12 +359,14 @@ 'iso_8859_11_2001' : 'iso8859_11', # iso8859_13 codec + 'csiso885913' : 'iso8859_13', 'iso_8859_13' : 'iso8859_13', 'l7' : 'iso8859_13', 'latin7' : 'iso8859_13', 'latin_7' : 'iso8859_13', # iso8859_14 codec + 'csiso885914' : 'iso8859_14', 'iso_8859_14' : 'iso8859_14', 'iso_8859_14_1998' : 'iso8859_14', 'iso_celtic' : 'iso8859_14', @@ -349,12 +376,14 @@ 'latin_8' : 'iso8859_14', # iso8859_15 codec + 'csiso885915' : 'iso8859_15', 'iso_8859_15' : 'iso8859_15', 'l9' : 'iso8859_15', 'latin9' : 'iso8859_15', 'latin_9' : 'iso8859_15', # iso8859_16 codec + 'csiso885916' : 'iso8859_16', 'iso_8859_16' : 'iso8859_16', 'iso_8859_16_2001' : 'iso8859_16', 'iso_ir_226' : 'iso8859_16', @@ -416,6 +445,8 @@ 'iso_ir_126' : 'iso8859_7', # iso8859_8 codec + 'csiso88598e' : 'iso8859_8', + 'csiso88598i' : 'iso8859_8', 'csisolatinhebrew' : 'iso8859_8', 'hebrew' : 'iso8859_8', 'iso_8859_8' : 'iso8859_8', @@ -440,7 +471,11 @@ # koi8_r codec 'cskoi8r' : 'koi8_r', + # koi8_u codec + 'cskoi8u' : 'koi8_u', + # kz1048 codec + 'cskz1048' : 'kz1048', 'kz_1048' : 'kz1048', 'rk1048' : 'kz1048', 'strk1048_2002' : 'kz1048', @@ -480,7 +515,9 @@ 'maclatin2' : 'mac_latin2', # mac_roman codec + 'csmacintosh' : 'mac_roman', 'macintosh' : 'mac_roman', + 'mac' : 'mac_roman', 'macroman' : 'mac_roman', # mac_turkish codec @@ -521,6 +558,7 @@ 's_jisx0213' : 'shift_jisx0213', # tis_620 codec + 'cstis620' : 'tis_620', 'tis620' : 'tis_620', 'tis_620_0' : 'tis_620', 'tis_620_2529_0' : 'tis_620', @@ -528,33 +566,42 @@ 'iso_ir_166' : 'tis_620', # utf_16 codec + 'csutf16' : 'utf_16', 'u16' : 'utf_16', 'utf16' : 'utf_16', # utf_16_be codec + 'csutf16be' : 'utf_16_be', 'unicodebigunmarked' : 'utf_16_be', 'utf_16be' : 'utf_16_be', # utf_16_le codec + 'csutf16le' : 'utf_16_le', 'unicodelittleunmarked' : 'utf_16_le', 'utf_16le' : 'utf_16_le', # utf_32 codec + 'csutf32' : 'utf_32', 'u32' : 'utf_32', 'utf32' : 'utf_32', # utf_32_be codec + 'csutf32be' : 'utf_32_be', 'utf_32be' : 'utf_32_be', # utf_32_le codec + 'csutf32le' : 'utf_32_le', 'utf_32le' : 'utf_32_le', # utf_7 codec + 'csunicode11utf7' : 'utf_7', + 'csutf7' : 'utf_7', 'u7' : 'utf_7', 'utf7' : 'utf_7', 'unicode_1_1_utf_7' : 'utf_7', # utf_8 codec + 'csutf8' : 'utf_8', 'u8' : 'utf_8', 'utf' : 'utf_8', 'utf8' : 'utf_8', diff --git a/Misc/NEWS.d/next/Library/2026-05-15-19-52-41.gh-issue-149891.BJUIGB.rst b/Misc/NEWS.d/next/Library/2026-05-15-19-52-41.gh-issue-149891.BJUIGB.rst new file mode 100644 index 000000000000000..f8bc28659533af8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-15-19-52-41.gh-issue-149891.BJUIGB.rst @@ -0,0 +1 @@ +Add support for more encoding aliases `officially registered in IANA `__. From 34b227a844dd43faa4abb8fc9ca37130208a0ca1 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 5 Jun 2026 14:11:46 +0200 Subject: [PATCH 031/185] gh-150907: Fix dynamic_annotations.h when built with C++ and Valgrind (#150914) Add extern "C++" scope for the C++ template. Fix test_cppext when Python is built with --with-valgrind. --- Include/dynamic_annotations.h | 3 +++ .../next/C_API/2026-06-04-14-26-17.gh-issue-150907.CA91_B.rst | 2 ++ 2 files changed, 5 insertions(+) create mode 100644 Misc/NEWS.d/next/C_API/2026-06-04-14-26-17.gh-issue-150907.CA91_B.rst diff --git a/Include/dynamic_annotations.h b/Include/dynamic_annotations.h index 4d4def9bf8983e2..5290319dd762c55 100644 --- a/Include/dynamic_annotations.h +++ b/Include/dynamic_annotations.h @@ -461,6 +461,7 @@ int RunningOnValgrind(void); #if DYNAMIC_ANNOTATIONS_ENABLED != 0 && defined(__cplusplus) +extern "C++" { /* _Py_ANNOTATE_UNPROTECTED_READ is the preferred way to annotate racey reads. Instead of doing @@ -476,6 +477,8 @@ int RunningOnValgrind(void); _Py_ANNOTATE_IGNORE_READS_END(); return res; } +} + /* Apply _Py_ANNOTATE_BENIGN_RACE_SIZED to a static variable. */ #define _Py_ANNOTATE_BENIGN_RACE_STATIC(static_var, description) \ namespace { \ diff --git a/Misc/NEWS.d/next/C_API/2026-06-04-14-26-17.gh-issue-150907.CA91_B.rst b/Misc/NEWS.d/next/C_API/2026-06-04-14-26-17.gh-issue-150907.CA91_B.rst new file mode 100644 index 000000000000000..f58b248f3a0b986 --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2026-06-04-14-26-17.gh-issue-150907.CA91_B.rst @@ -0,0 +1,2 @@ +Fix ``dynamic_annotations.h`` header file when built with C++ and Valgrind: +add ``extern "C++" scope`` for the C++ template. Patch by Victor Stinner. From 3bfa420e82eb278f42fb0f943cf413188e9d2db2 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 5 Jun 2026 15:41:51 +0300 Subject: [PATCH 032/185] gh-84649: Fix unstable test_rollover_based_on_st_birthtime_only (GH-150954) --- Lib/test/test_logging.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 31c052bfb56cd7a..d4fa78acfd11dee 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -6617,6 +6617,7 @@ def test_rollover(self): @unittest.skipUnless(support.has_st_birthtime, "st_birthtime not available or supported by Python on this OS") + @support.requires_resource('walltime') def test_rollover_based_on_st_birthtime_only(self): def add_record(message: str) -> None: fh = logging.handlers.TimedRotatingFileHandler( @@ -6639,11 +6640,11 @@ def add_record(message: str) -> None: # At this point, the log file should be rotated if the rotation # is based on creation time but should be not if it's based on - # creation time. + # modification time. found = False now = datetime.datetime.now() GO_BACK = 5 # seconds - for secs in range(GO_BACK): + for secs in range(GO_BACK + 1): prev = now - datetime.timedelta(seconds=secs) fn = self.fn + prev.strftime(".%Y-%m-%d_%H-%M-%S") found = os.path.exists(fn) From b721ebc1a6952fb575e8bec8de4897642ee14ef0 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 5 Jun 2026 15:53:59 +0300 Subject: [PATCH 033/185] gh-53144: Improve charset support in the email package (GH-149942) Defer to the codecs module for all aliases. Use MIME/IANA names for all IANA registered charsets. Fix email.contentmanager.set_text_content(). --- Lib/email/charset.py | 104 ++++++++++---- Lib/email/contentmanager.py | 6 +- Lib/test/test_email/test_asian_codecs.py | 6 +- Lib/test/test_email/test_contentmanager.py | 13 ++ Lib/test/test_email/test_email.py | 127 ++++++++++++++++++ ...6-05-17-12-37-59.gh-issue-53144.c5tr1p.rst | 2 + 6 files changed, 223 insertions(+), 35 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-05-17-12-37-59.gh-issue-53144.c5tr1p.rst diff --git a/Lib/email/charset.py b/Lib/email/charset.py index c4b246455f86c64..5981791820e740c 100644 --- a/Lib/email/charset.py +++ b/Lib/email/charset.py @@ -9,6 +9,7 @@ 'add_codec', ] +import codecs from functools import partial import email.base64mime @@ -58,37 +59,71 @@ 'shift_jis': (BASE64, None, 'iso-2022-jp'), 'iso-2022-jp': (BASE64, None, None), 'koi8-r': (BASE64, BASE64, None), - 'utf-8': (SHORTEST, BASE64, 'utf-8'), } -# Aliases for other commonly-used names for character sets. Map -# them to the real ones used in email. +# Map Python codec names to their corresponding MIME/IANA names. ALIASES = { - 'latin_1': 'iso-8859-1', - 'latin-1': 'iso-8859-1', - 'latin_2': 'iso-8859-2', - 'latin-2': 'iso-8859-2', - 'latin_3': 'iso-8859-3', - 'latin-3': 'iso-8859-3', - 'latin_4': 'iso-8859-4', - 'latin-4': 'iso-8859-4', - 'latin_5': 'iso-8859-9', - 'latin-5': 'iso-8859-9', - 'latin_6': 'iso-8859-10', - 'latin-6': 'iso-8859-10', - 'latin_7': 'iso-8859-13', - 'latin-7': 'iso-8859-13', - 'latin_8': 'iso-8859-14', - 'latin-8': 'iso-8859-14', - 'latin_9': 'iso-8859-15', - 'latin-9': 'iso-8859-15', - 'latin_10':'iso-8859-16', - 'latin-10':'iso-8859-16', - 'cp949': 'ks_c_5601-1987', - 'euc_jp': 'euc-jp', - 'euc_kr': 'euc-kr', - 'ascii': 'us-ascii', - } + 'ascii': 'us-ascii', + 'big5hkscs': 'big5-hkscs', + 'cp037': 'ibm037', + 'cp1026': 'ibm1026', + 'cp1140': 'ibm01140', + 'cp1250': 'windows-1250', + 'cp1251': 'windows-1251', + 'cp1252': 'windows-1252', + 'cp1253': 'windows-1253', + 'cp1254': 'windows-1254', + 'cp1255': 'windows-1255', + 'cp1256': 'windows-1256', + 'cp1257': 'windows-1257', + 'cp1258': 'windows-1258', + 'cp273': 'ibm273', + 'cp424': 'ibm424', + 'cp437': 'ibm437', + 'cp500': 'ibm500', + 'cp775': 'ibm775', + 'cp850': 'ibm850', + 'cp852': 'ibm852', + 'cp855': 'ibm855', + 'cp857': 'ibm857', + 'cp858': 'ibm00858', + 'cp860': 'ibm860', + 'cp861': 'ibm861', + 'cp862': 'ibm862', + 'cp863': 'ibm863', + 'cp864': 'ibm864', + 'cp865': 'ibm865', + 'cp866': 'ibm866', + 'cp869': 'ibm869', + 'cp874': 'windows-874', + 'euc_jp': 'euc-jp', + 'euc_kr': 'euc-kr', + 'hz': 'hz-gb-2312', + 'iso2022_jp': 'iso-2022-jp', + 'iso2022_jp_2': 'iso-2022-jp-2', + 'iso2022_kr': 'iso-2022-kr', + 'iso8859-1': 'iso-8859-1', + 'iso8859-10': 'iso-8859-10', + 'iso8859-11': 'iso-8859-11', + 'iso8859-13': 'iso-8859-13', + 'iso8859-14': 'iso-8859-14', + 'iso8859-15': 'iso-8859-15', + 'iso8859-16': 'iso-8859-16', + 'iso8859-2': 'iso-8859-2', + 'iso8859-3': 'iso-8859-3', + 'iso8859-4': 'iso-8859-4', + 'iso8859-5': 'iso-8859-5', + 'iso8859-6': 'iso-8859-6', + 'iso8859-7': 'iso-8859-7', + 'iso8859-8': 'iso-8859-8-i', + 'iso8859-9': 'iso-8859-9', + 'kz1048': 'kz-1048', + 'mac-roman': 'macintosh', + + # CP949 is not registered in IANA. KS_C_5601-1987 is not the same, + # but the closest registered option. + 'cp949': 'ks_c_5601-1987', +} # Map charsets to their Unicode codec strings. @@ -215,7 +250,18 @@ def __init__(self, input_charset=DEFAULT_CHARSET): raise errors.CharsetError(input_charset) input_charset = input_charset.lower() # Set the input charset after filtering through the aliases - self.input_charset = ALIASES.get(input_charset, input_charset) + # For backward compatibility, try ALIASES first to let the user + # override it. + if input_charset in ALIASES: + input_charset = ALIASES[input_charset] + else: + try: + input_codec = codecs.lookup(input_charset).name + except LookupError: + pass + else: + input_charset = ALIASES.get(input_codec, input_codec) + self.input_charset = input_charset # We can try to guess which encoding and conversion to use by the # charset_map dictionary. Try that first, but let the user override # it. diff --git a/Lib/email/contentmanager.py b/Lib/email/contentmanager.py index 13fcb9787f1f320..faf2626bccce651 100644 --- a/Lib/email/contentmanager.py +++ b/Lib/email/contentmanager.py @@ -173,11 +173,11 @@ def set_text_content(msg, string, subtype="plain", charset='utf-8', cte=None, disposition=None, filename=None, cid=None, params=None, headers=None): _prepare_set(msg, 'text', subtype, headers) + + charset = email.charset.Charset(charset).input_charset cte, payload = _encode_text(string, charset, cte, msg.policy) msg.set_payload(payload) - msg.set_param('charset', - email.charset.ALIASES.get(charset, charset), - replace=True) + msg.set_param('charset', charset, replace=True) msg['Content-Transfer-Encoding'] = cte _finalize_set(msg, disposition, filename, cid, params) raw_data_manager.add_set_handler(str, set_text_content) diff --git a/Lib/test/test_email/test_asian_codecs.py b/Lib/test/test_email/test_asian_codecs.py index 85979ffd8169a75..59013f087199e3a 100644 --- a/Lib/test/test_email/test_asian_codecs.py +++ b/Lib/test/test_email/test_asian_codecs.py @@ -83,15 +83,15 @@ def test_chinese_codecs(self): h.append(s, Charset('big5hkscs')) eq(h.encode(), """\ Chinese =?gb2312?b?1tDOxA==?= =?gbk?b?1tDOxA==?= =?gb18030?b?1tDOxA==?= - =?hz?b?fntWUE5Efn0=?= =?big5?b?pKSk5Q==?= =?big5hkscs?b?pKSk5Q==?=""") + =?hz-gb-2312?b?fntWUE5Efn0=?= =?big5?b?pKSk5Q==?= =?big5-hkscs?b?pKSk5Q==?=""") eq(decode_header(h.encode()), [(b'Chinese ', None), (b'\xd6\xd0\xce\xc4', 'gb2312'), (b'\xd6\xd0\xce\xc4', 'gbk'), (b'\xd6\xd0\xce\xc4', 'gb18030'), - (b'~{VPND~}', 'hz'), + (b'~{VPND~}', 'hz-gb-2312'), (b'\xa4\xa4\xa4\xe5', 'big5'), - (b'\xa4\xa4\xa4\xe5', 'big5hkscs'), + (b'\xa4\xa4\xa4\xe5', 'big5-hkscs'), ]) def test_korean_codecs(self): diff --git a/Lib/test/test_email/test_contentmanager.py b/Lib/test/test_email/test_contentmanager.py index dceb54f15e48f4e..bc0e5d356181591 100644 --- a/Lib/test/test_email/test_contentmanager.py +++ b/Lib/test/test_email/test_contentmanager.py @@ -342,6 +342,19 @@ def test_set_text_charset_latin_1(self): self.assertEqual(m.get_payload(decode=True).decode('utf-8'), content) self.assertEqual(m.get_content(), content) + def test_set_text_charset_cp949(self): + m = self._make_message() + content = "\ud55c\uad6d\uc5b4\n\uac02\n" + raw_data_manager.set_content(m, content, charset='cp949') + self.assertEqual(str(m), textwrap.dedent("""\ + Content-Type: text/plain; charset="ks_c_5601-1987" + Content-Transfer-Encoding: base64 + + x9Gxub7uCoFBCg== + """)) + self.assertEqual(m.get_payload(decode=True).decode('ks_c_5601-1987'), content) + self.assertEqual(m.get_content(), content) + def test_set_text_plain_long_line_heuristics(self): m = self._make_message() content = ("Simple but long message that is over 78 characters" diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py index d2c2261edbe04e1..19555d87085e176 100644 --- a/Lib/test/test_email/test_email.py +++ b/Lib/test/test_email/test_email.py @@ -4970,6 +4970,128 @@ def tearDown(self): except KeyError: pass + def test_attributes(self): + from email import charset + c = Charset() + self.assertEqual(c.input_charset, 'us-ascii') + self.assertEqual(c.header_encoding, None) + self.assertEqual(c.body_encoding, None) + self.assertEqual(c.output_charset, 'us-ascii') + self.assertEqual(c.input_codec, None) + self.assertEqual(c.output_codec, None) + + c = Charset('us-ascii') + self.assertEqual(c.input_charset, 'us-ascii') + self.assertEqual(c.header_encoding, None) + self.assertEqual(c.body_encoding, None) + self.assertEqual(c.output_charset, 'us-ascii') + self.assertEqual(c.input_codec, None) + self.assertEqual(c.output_codec, None) + + c = Charset('utf8') + self.assertEqual(c.input_charset, 'utf-8') + self.assertEqual(c.header_encoding, charset.SHORTEST) + self.assertEqual(c.body_encoding, charset.BASE64) + self.assertEqual(c.output_charset, 'utf-8') + self.assertEqual(c.input_codec, 'utf-8') + self.assertEqual(c.output_codec, 'utf-8') + + c = Charset('latin1') + self.assertEqual(c.input_charset, 'iso-8859-1') + self.assertEqual(c.header_encoding, charset.QP) + self.assertEqual(c.body_encoding, charset.QP) + self.assertEqual(c.output_charset, 'iso-8859-1') + self.assertEqual(c.input_codec, 'iso-8859-1') + self.assertEqual(c.output_codec, 'iso-8859-1') + + c = Charset('latin9') + self.assertEqual(c.input_charset, 'iso-8859-15') + self.assertEqual(c.header_encoding, charset.QP) + self.assertEqual(c.body_encoding, charset.QP) + self.assertEqual(c.output_charset, 'iso-8859-15') + self.assertEqual(c.input_codec, 'iso-8859-15') + self.assertEqual(c.output_codec, 'iso-8859-15') + + c = Charset('cyrillic') + self.assertEqual(c.input_charset, 'iso-8859-5') + self.assertEqual(c.header_encoding, charset.SHORTEST) + self.assertEqual(c.body_encoding, charset.BASE64) + self.assertEqual(c.output_charset, 'iso-8859-5') + self.assertEqual(c.input_codec, 'iso-8859-5') + self.assertEqual(c.output_codec, 'iso-8859-5') + + c = Charset('cp1251') + self.assertEqual(c.input_charset, 'windows-1251') + self.assertEqual(c.header_encoding, charset.SHORTEST) + self.assertEqual(c.body_encoding, charset.BASE64) + self.assertEqual(c.output_charset, 'windows-1251') + self.assertEqual(c.input_codec, 'windows-1251') + self.assertEqual(c.output_codec, 'windows-1251') + + c = Charset('cp1252') + self.assertEqual(c.input_charset, 'windows-1252') + self.assertEqual(c.header_encoding, charset.QP) + self.assertEqual(c.body_encoding, charset.QP) + self.assertEqual(c.output_charset, 'windows-1252') + self.assertEqual(c.input_codec, 'windows-1252') + self.assertEqual(c.output_codec, 'windows-1252') + + c = Charset('eucjp') + self.assertEqual(c.input_charset, 'euc-jp') + self.assertEqual(c.header_encoding, charset.BASE64) + self.assertEqual(c.body_encoding, None) + self.assertEqual(c.output_charset, 'iso-2022-jp') + self.assertEqual(c.input_codec, 'euc-jp') + self.assertEqual(c.output_codec, 'iso-2022-jp') + + c = Charset('cp949') + self.assertEqual(c.input_charset, 'ks_c_5601-1987') + self.assertEqual(c.header_encoding, charset.SHORTEST) + self.assertEqual(c.body_encoding, charset.BASE64) + self.assertEqual(c.output_charset, 'ks_c_5601-1987') + self.assertEqual(c.input_codec, 'ks_c_5601-1987') + self.assertEqual(c.output_codec, 'ks_c_5601-1987') + + c = Charset('gb2312') + self.assertEqual(c.input_charset, 'gb2312') + self.assertEqual(c.header_encoding, charset.BASE64) + self.assertEqual(c.body_encoding, charset.BASE64) + self.assertEqual(c.output_charset, 'gb2312') + self.assertEqual(c.input_codec, 'gb2312') + self.assertEqual(c.output_codec, 'gb2312') + + c = Charset('big5') + self.assertEqual(c.input_charset, 'big5') + self.assertEqual(c.header_encoding, charset.BASE64) + self.assertEqual(c.body_encoding, charset.BASE64) + self.assertEqual(c.output_charset, 'big5') + self.assertEqual(c.input_codec, 'big5') + self.assertEqual(c.output_codec, 'big5') + + def test_user_charsets(self): + from email import charset + c = Charset('fake0') + self.assertEqual(c.input_charset, 'fake0') + self.assertEqual(c.header_encoding, charset.SHORTEST) + self.assertEqual(c.body_encoding, charset.BASE64) + self.assertEqual(c.output_charset, 'fake0') + self.assertEqual(c.input_codec, 'fake0') + self.assertEqual(c.output_codec, 'fake0') + + charset.add_alias('fake1', 'mime-fake') + charset.add_alias('output-mime-fake', 'output-mime-fake-alias') + charset.add_codec('mime-fake', 'fakecodec') + charset.add_codec('output-mime-fake-alias', 'outputfakecodec') + charset.add_charset('mime-fake', charset.QP, None, 'output-mime-fake') + + c = Charset('fake1') + self.assertEqual(c.input_charset, 'mime-fake') + self.assertEqual(c.header_encoding, charset.QP) + self.assertEqual(c.body_encoding, None) + self.assertEqual(c.output_charset, 'output-mime-fake-alias') + self.assertEqual(c.input_codec, 'fakecodec') + self.assertEqual(c.output_codec, 'outputfakecodec') + def test_codec_encodeable(self): eq = self.assertEqual # Make sure us-ascii = no Unicode conversion @@ -5010,6 +5132,11 @@ def test_unicode_charset_name(self): self.assertEqual(str(charset), 'us-ascii') self.assertRaises(errors.CharsetError, Charset, 'asc\xffii') + def test_bytes_charset_name(self): + charset = Charset(b'us-ascii') + self.assertEqual(str(charset), 'us-ascii') + self.assertRaises(errors.CharsetError, Charset, b'asc\xffii') + # Test multilingual MIME headers. diff --git a/Misc/NEWS.d/next/Library/2026-05-17-12-37-59.gh-issue-53144.c5tr1p.rst b/Misc/NEWS.d/next/Library/2026-05-17-12-37-59.gh-issue-53144.c5tr1p.rst new file mode 100644 index 000000000000000..283a5ba44d1f19f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-17-12-37-59.gh-issue-53144.c5tr1p.rst @@ -0,0 +1,2 @@ +The :mod:`email` package now supports all aliases of Python codecs and uses +MIME/IANA names for all IANA registered charsets. From 1765da40a9776797d49664ba61074636bd776d54 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 5 Jun 2026 15:43:05 +0200 Subject: [PATCH 034/185] gh-146527: Fix memory leak in _PyGC_Fini() (#150969) Free generation_stats allocated by _PyGC_Init(). Fix Python/gc.c: Python/gc_free_threading.c was already fixed. --- Python/gc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Python/gc.c b/Python/gc.c index 54ac1b089e503d0..201c621bcc3cb9b 100644 --- a/Python/gc.c +++ b/Python/gc.c @@ -1876,6 +1876,8 @@ _PyGC_Fini(PyInterpreterState *interp) GCState *gcstate = &interp->gc; Py_CLEAR(gcstate->garbage); Py_CLEAR(gcstate->callbacks); + PyMem_RawFree(gcstate->generation_stats); + gcstate->generation_stats = NULL; /* Prevent a subtle bug that affects sub-interpreters that use basic * single-phase init extensions (m_size == -1). Those extensions cause objects From f1d6076e0d084cd0d3cfb3478ff0f43c50ecf176 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bern=C3=A1t=20G=C3=A1bor?= Date: Fri, 5 Jun 2026 08:00:56 -0700 Subject: [PATCH 035/185] gh-150818: Wire logger parent before publishing it in getLogger() (GH-150941) --- Lib/logging/__init__.py | 15 ++++++++++----- Lib/test/test_logging.py | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py index 9febc50b1264ef4..b4a5f5cc2f598f5 100644 --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -1377,9 +1377,10 @@ def getLogger(self, name): raise TypeError('A logger name must be a string') # Fast path: an already-registered, non-placeholder logger can be # returned without taking the lock. dict.get() is atomic under both - # the GIL and free threading, and a Logger is fully initialised before - # being inserted into loggerDict under the lock, so this never sees a - # partially-constructed object. + # the GIL and free threading. A Logger is inserted into loggerDict only + # after it is fully wired up (parent/child references fixed) under the + # lock, so the fast path never observes a logger whose parent is not yet + # set. rv = self.loggerDict.get(name) if rv is not None and not isinstance(rv, PlaceHolder): return rv @@ -1390,14 +1391,18 @@ def getLogger(self, name): ph = rv rv = (self.loggerClass or _loggerClass)(name) rv.manager = self - self.loggerDict[name] = rv self._fixupChildren(ph, rv) self._fixupParents(rv) + # Publish only after rv is fully wired: the fast path reads + # loggerDict without the lock. + self.loggerDict[name] = rv else: rv = (self.loggerClass or _loggerClass)(name) rv.manager = self - self.loggerDict[name] = rv self._fixupParents(rv) + # Publish only after rv is fully wired: the fast path reads + # loggerDict without the lock. + self.loggerDict[name] = rv return rv def setLoggerClass(self, klass): diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index d4fa78acfd11dee..9f29fe8a5b3c9bc 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -4269,6 +4269,43 @@ def test_set_log_record_factory(self): man.setLogRecordFactory(expected) self.assertEqual(man.logRecordFactory, expected) + @threading_helper.requires_working_threading() + def test_getLogger_fast_path_never_returns_unwired_logger(self): + # getLogger()'s lock-free fast path returns a logger straight out of + # loggerDict, so a logger must be published there only after + # _fixupParents() has set its parent; otherwise a concurrent caller + # observes it detached from the hierarchy (gh-150818 follow-up). + manager = logging.Manager(logging.RootLogger(logging.WARNING)) + name = 'a.b.c' + + paused = threading.Event() + seen = [] + real_fixup = manager._fixupParents + + # Pause the creating thread between publishing rv and wiring its + # parent, then read loggerDict the way the fast path does and snapshot + # the parent at that instant (rv is wired in place soon after). + def fixup(alogger): + paused.set() + reader.join() + real_fixup(alogger) + + def read(): + paused.wait() + rv = manager.loggerDict.get(name) + if rv is not None and not isinstance(rv, logging.PlaceHolder): + seen.append(rv.parent) + + reader = threading.Thread(target=read) + manager._fixupParents = fixup + try: + reader.start() + manager.getLogger(name) + finally: + manager._fixupParents = real_fixup + + self.assertNotIn(None, seen) + class ChildLoggerTest(BaseTest): def test_child_loggers(self): r = logging.getLogger() From 5c817575bf533b9b155d7f2c67f52c48dbdc35df Mon Sep 17 00:00:00 2001 From: sobolevn Date: Fri, 5 Jun 2026 18:21:10 +0300 Subject: [PATCH 036/185] gh-149977: Fix extra output of `-m test test_lazy_import`, again (#150965) --- Lib/test/test_lazy_import/__init__.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_lazy_import/__init__.py b/Lib/test/test_lazy_import/__init__.py index 94576a206b42caa..1724beb8ce69517 100644 --- a/Lib/test/test_lazy_import/__init__.py +++ b/Lib/test/test_lazy_import/__init__.py @@ -447,11 +447,15 @@ def test_lazy_import_pkg(self): def test_lazy_submodule_stored_in_parent_dict(self): """Accessing a lazy submodule should store it in the parent's __dict__.""" - import test.test_lazy_import.data.lazy_import_pkg + out = io.StringIO() + + with contextlib.redirect_stdout(out): + import test.test_lazy_import.data.lazy_import_pkg pkg = sys.modules["test.test_lazy_import.data.pkg"] self.assertIn("bar", pkg.__dict__) self.assertIs(pkg.__dict__["bar"], sys.modules["test.test_lazy_import.data.pkg.bar"]) + self.assertIn("BAR_MODULE_LOADED", out.getvalue()) def test_lazy_import_pkg_cross_import(self): """Cross-imports within package should preserve lazy imports.""" From 9adf3eda94cc775ca63865674353dfeda5704704 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 5 Jun 2026 17:24:46 +0200 Subject: [PATCH 037/185] gh-150436: Check returncode in Win32ProcessTestCase (#150972) --- Lib/test/test_subprocess.py | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index f944084aaa6d6aa..d41cb1294a3dafe 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -3719,8 +3719,9 @@ def test_startupinfo(self): # Since Python is a console process, it won't be affected # by wShowWindow, but the argument should be silently # ignored - subprocess.call(ZERO_RETURN_CMD, - startupinfo=startupinfo) + rc = subprocess.call(ZERO_RETURN_CMD, + startupinfo=startupinfo) + self.assertEqual(rc, 0) def test_startupinfo_keywords(self): # startupinfo argument @@ -3735,8 +3736,9 @@ def test_startupinfo_keywords(self): # Since Python is a console process, it won't be affected # by wShowWindow, but the argument should be silently # ignored - subprocess.call(ZERO_RETURN_CMD, - startupinfo=startupinfo) + rc = subprocess.call(ZERO_RETURN_CMD, + startupinfo=startupinfo) + self.assertEqual(rc, 0) def test_startupinfo_copy(self): # bpo-34044: Popen must not modify input STARTUPINFO structure @@ -3853,14 +3855,16 @@ def test_close_fds_with_stdio(self): def test_empty_attribute_list(self): startupinfo = subprocess.STARTUPINFO() startupinfo.lpAttributeList = {} - subprocess.call(ZERO_RETURN_CMD, - startupinfo=startupinfo) + rc = subprocess.call(ZERO_RETURN_CMD, + startupinfo=startupinfo) + self.assertEqual(rc, 0) def test_empty_handle_list(self): startupinfo = subprocess.STARTUPINFO() startupinfo.lpAttributeList = {"handle_list": []} - subprocess.call(ZERO_RETURN_CMD, - startupinfo=startupinfo) + rc = subprocess.call(ZERO_RETURN_CMD, + startupinfo=startupinfo) + self.assertEqual(rc, 0) def test_shell_sequence(self): # Run command through the shell (sequence) @@ -3871,6 +3875,8 @@ def test_shell_sequence(self): env=newenv) with p: self.assertIn(b"physalis", p.stdout.read()) + p.communicate() + self.assertEqual(p.returncode, 0) def test_shell_string(self): # Run command through the shell (string) @@ -3881,6 +3887,8 @@ def test_shell_string(self): env=newenv) with p: self.assertIn(b"physalis", p.stdout.read()) + p.communicate() + self.assertEqual(p.returncode, 0) def test_shell_encodings(self): # Run command through the shell (string) @@ -3893,6 +3901,8 @@ def test_shell_encodings(self): encoding=enc) with p: self.assertIn("physalis", p.stdout.read(), enc) + p.communicate() + self.assertEqual(p.returncode, 0) def test_call_string(self): # call() function with string argument on Windows From 0e4676475b6d07d30f427b217a3adea8ecc09f1c Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Sat, 6 Jun 2026 01:02:10 +0800 Subject: [PATCH 038/185] gh-110631: Fix wrong blocks indentation in difflib.rst (GH-110885) --- Doc/library/difflib.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/difflib.rst b/Doc/library/difflib.rst index 8b812c173b59536..25edb40e35a630a 100644 --- a/Doc/library/difflib.rst +++ b/Doc/library/difflib.rst @@ -724,7 +724,7 @@ Finally, we compare the two: >>> result = list(d.compare(text1, text2)) -``result`` is a list of strings, so let's pretty-print it: +``result`` is a list of strings, so let's pretty-print it:: >>> from pprint import pprint >>> pprint(result) @@ -739,7 +739,7 @@ Finally, we compare the two: '? ++++ ^ ^\n', '+ 5. Flat is better than nested.\n'] -As a single multi-line string it looks like this: +As a single multi-line string it looks like this:: >>> import sys >>> sys.stdout.writelines(result) From 8e5ab6ad3f8a1d32bf9c326a9ac482b36ac13d37 Mon Sep 17 00:00:00 2001 From: Ilya Nikolaev <65247719+ilya-nikolaev@users.noreply.github.com> Date: Sat, 6 Jun 2026 01:42:59 +0300 Subject: [PATCH 039/185] Use `time.monotonic` in OrderedDict LRU cache example (#150986) --- Doc/library/collections.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/library/collections.rst b/Doc/library/collections.rst index bd04063232e527e..d09a6c92bbd37dc 100644 --- a/Doc/library/collections.rst +++ b/Doc/library/collections.rst @@ -1233,7 +1233,7 @@ variants of :deco:`functools.lru_cache`: .. testcode:: from collections import OrderedDict - from time import time + from time import monotonic class TimeBoundedLRU: "LRU Cache that invalidates and refreshes old entries." @@ -1248,10 +1248,10 @@ variants of :deco:`functools.lru_cache`: if args in self.cache: self.cache.move_to_end(args) timestamp, result = self.cache[args] - if time() - timestamp <= self.maxage: + if monotonic() - timestamp <= self.maxage: return result result = self.func(*args) - self.cache[args] = time(), result + self.cache[args] = monotonic(), result if len(self.cache) > self.maxsize: self.cache.popitem(last=False) return result From 9974822d0bc40fd90ccc9c358ef6528121be5b04 Mon Sep 17 00:00:00 2001 From: Grant Herman Date: Fri, 5 Jun 2026 21:58:35 -0400 Subject: [PATCH 040/185] gh-150207: Raise MemoryError on tokenizer allocation failure instead of crashing (#150275) --- .../2026-05-22-21-52-38.gh-issue-150207.l2BUtI.rst | 1 + Parser/lexer/state.c | 5 ++++- Parser/tokenizer/file_tokenizer.c | 1 + Parser/tokenizer/helpers.c | 2 ++ Parser/tokenizer/readline_tokenizer.c | 1 + 5 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-05-22-21-52-38.gh-issue-150207.l2BUtI.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-05-22-21-52-38.gh-issue-150207.l2BUtI.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-22-21-52-38.gh-issue-150207.l2BUtI.rst new file mode 100644 index 000000000000000..12fbffcd170684c --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-22-21-52-38.gh-issue-150207.l2BUtI.rst @@ -0,0 +1 @@ +Fix a crash when a memory allocation fails during tokenizer initialization. A proper :exc:`MemoryError` is now raised instead. diff --git a/Parser/lexer/state.c b/Parser/lexer/state.c index 3663dc3eb7f9f69..5cf9b4d768c3ebb 100644 --- a/Parser/lexer/state.c +++ b/Parser/lexer/state.c @@ -15,8 +15,11 @@ _PyTokenizer_tok_new(void) struct tok_state *tok = (struct tok_state *)PyMem_Calloc( 1, sizeof(struct tok_state)); - if (tok == NULL) + if (tok == NULL) { + PyErr_NoMemory(); return NULL; + } + tok->buf = tok->cur = tok->inp = NULL; tok->fp_interactive = 0; tok->interactive_src_start = NULL; diff --git a/Parser/tokenizer/file_tokenizer.c b/Parser/tokenizer/file_tokenizer.c index 8c836a3f7258296..a11702557a07af3 100644 --- a/Parser/tokenizer/file_tokenizer.c +++ b/Parser/tokenizer/file_tokenizer.c @@ -378,6 +378,7 @@ _PyTokenizer_FromFile(FILE *fp, const char* enc, return NULL; if ((tok->buf = (char *)PyMem_Malloc(BUFSIZ)) == NULL) { _PyTokenizer_Free(tok); + PyErr_NoMemory(); return NULL; } tok->cur = tok->inp = tok->buf; diff --git a/Parser/tokenizer/helpers.c b/Parser/tokenizer/helpers.c index 9542969ad3127b9..c69e66d0ab9b7a8 100644 --- a/Parser/tokenizer/helpers.c +++ b/Parser/tokenizer/helpers.c @@ -193,6 +193,7 @@ _PyTokenizer_new_string(const char *s, Py_ssize_t len, struct tok_state *tok) char* result = (char *)PyMem_Malloc(len + 1); if (!result) { tok->done = E_NOMEM; + PyErr_NoMemory(); return NULL; } memcpy(result, s, len); @@ -221,6 +222,7 @@ _PyTokenizer_translate_newlines(const char *s, int exec_input, int preserve_crlf buf = PyMem_Malloc(needed_length); if (buf == NULL) { tok->done = E_NOMEM; + PyErr_NoMemory(); return NULL; } for (current = buf; *s; s++, current++) { diff --git a/Parser/tokenizer/readline_tokenizer.c b/Parser/tokenizer/readline_tokenizer.c index 0f7769aeb8fd570..917f7b40cfbbfed 100644 --- a/Parser/tokenizer/readline_tokenizer.c +++ b/Parser/tokenizer/readline_tokenizer.c @@ -114,6 +114,7 @@ _PyTokenizer_FromReadline(PyObject* readline, const char* enc, return NULL; if ((tok->buf = (char *)PyMem_Malloc(BUFSIZ)) == NULL) { _PyTokenizer_Free(tok); + PyErr_NoMemory(); return NULL; } tok->cur = tok->inp = tok->buf; From 16fd11183592e4797652ad1801d37dfff27bcc9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maurycy=20Paw=C5=82owski-Wiero=C5=84ski?= Date: Sat, 6 Jun 2026 04:27:41 +0200 Subject: [PATCH 041/185] gh-150662: Stop unbounded memory growth in Tachyon `--gecko` collector (#150845) --- Lib/profiling/sampling/gecko_collector.py | 379 +++++++++++++----- .../test_sampling_profiler/test_collectors.py | 203 ++++++++-- ...-06-03-13-51-29.gh-issue-150662.ELT8Vg.rst | 4 + 3 files changed, 454 insertions(+), 132 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-06-03-13-51-29.gh-issue-150662.ELT8Vg.rst diff --git a/Lib/profiling/sampling/gecko_collector.py b/Lib/profiling/sampling/gecko_collector.py index 54392af95000082..361f6037f216fdc 100644 --- a/Lib/profiling/sampling/gecko_collector.py +++ b/Lib/profiling/sampling/gecko_collector.py @@ -1,8 +1,10 @@ import itertools +import io import json import os import platform import sys +import tempfile import threading import time @@ -61,6 +63,77 @@ PROCESS_TYPE_MAIN = 0 STACKWALK_DISABLED = 0 +# In-memory buffer before spilling to disk +DEFAULT_SPILL_BUFFER_BYTES = 128 * 1024 +_JSON_SEPARATORS = (",", ":") +_JSON_ENCODER = json.JSONEncoder( + separators=_JSON_SEPARATORS, allow_nan=False +) + + +class SpillColumn: + def __init__(self, directory, basename, *, + buffer_bytes=None): + self.path = os.path.join(directory, basename) + self.buffer = bytearray() + self._buffer_bytes = ( + DEFAULT_SPILL_BUFFER_BYTES if buffer_bytes is None + else buffer_bytes + ) + + def append(self, value): + self.buffer += (_JSON_ENCODER.encode(value) + "\n").encode("utf-8") + if len(self.buffer) >= self._buffer_bytes: + self.flush() + + def flush(self): + with open(self.path, "ab") as file: + file.write(self.buffer) + self.buffer.clear() + + def iter_tokens(self): + with open(self.path, encoding="utf-8") as file: + for line in file: + yield line.rstrip("\n") + + +class GeckoThreadSpill: + _COLUMNS = ( + ("samples_stack", "samples-stack.json"), + ("samples_time", "samples-time.json"), + ("markers_name", "markers-name.json"), + ("markers_start_time", "markers-start-time.json"), + ("markers_end_time", "markers-end-time.json"), + ("markers_phase", "markers-phase.json"), + ("markers_category", "markers-category.json"), + ("markers_data", "markers-data.json"), + ) + + def __init__(self, directory, tid): + prefix = f"thread-{tid}-" + for attr, basename in self._COLUMNS: + setattr(self, attr, SpillColumn(directory, prefix + basename)) + self.sample_count = 0 + self.marker_count = 0 + + def append_sample(self, stack_index, time_ms): + self.samples_stack.append(stack_index) + self.samples_time.append(time_ms) + self.sample_count += 1 + + def append_marker(self, name_idx, start_time, end_time, phase, category, data): + self.markers_name.append(name_idx) + self.markers_start_time.append(start_time) + self.markers_end_time.append(end_time) + self.markers_phase.append(phase) + self.markers_category.append(category) + self.markers_data.append(data) + self.marker_count += 1 + + def prepare_read(self): + for attr, _basename in self._COLUMNS: + getattr(self, attr).flush() + class GeckoCollector(Collector): aggregating = True @@ -77,6 +150,8 @@ def __init__(self, sample_interval_usec, *, skip_idle=False, opcodes=False): # Per-thread data structures self.threads = {} # tid -> thread data + self.spill_dir = None + self.exported = False # Global tables self.libs = [] @@ -151,6 +226,9 @@ def collect(self, stack_frames, timestamps_us=None): stack_frames: List of interpreter/thread frame info timestamps_us: List of timestamps in microseconds (None for live sampling) """ + if self.exported: + raise RuntimeError("cannot append to GeckoCollector after export") + # Handle live sampling (no timestamps provided) if timestamps_us is None: current_time = (time.monotonic() * 1000) - self.start_time @@ -259,15 +337,9 @@ def collect(self, stack_frames, timestamps_us=None): stack_index = self._process_stack(thread_data, frames) # Add samples with timestamps - samples = thread_data["samples"] - samples_stack = samples["stack"] - samples_time = samples["time"] - samples_delay = samples["eventDelay"] - + thread_spill = thread_data["_spill"] for t in times: - samples_stack.append(stack_index) - samples_time.append(t) - samples_delay.append(None) + thread_spill.append_sample(stack_index, t) # Handle opcodes if self.opcodes_enabled and frames: @@ -294,6 +366,8 @@ def collect(self, stack_frames, timestamps_us=None): def _create_thread(self, tid, is_main_thread): """Create a new thread structure with processed profile format.""" + if self.spill_dir is None: + self.spill_dir = tempfile.TemporaryDirectory() thread = { "name": f"Thread-{tid}", @@ -307,15 +381,6 @@ def _create_thread(self, tid, is_main_thread): "tid": tid, "processType": "default", "processName": "Python Process", - # Sample data - processed format with direct arrays - "samples": { - "stack": [], - "time": [], - "eventDelay": [], - "weight": None, - "weightType": "samples", - "length": 0, # Will be updated on export - }, # Stack table - processed format "stackTable": { "frame": [], @@ -366,21 +431,12 @@ def _create_thread(self, tid, is_main_thread): "functionSize": [], "length": 0, }, - # Markers - processed format (arrays) - "markers": { - "data": [], - "name": [], - "startTime": [], - "endTime": [], - "phase": [], - "category": [], - "length": 0, - }, # Caches for deduplication "_stackCache": {}, "_frameCache": {}, "_funcCache": {}, "_resourceCache": {}, + "_spill": GeckoThreadSpill(self.spill_dir.name, tid), } return thread @@ -405,51 +461,42 @@ def _add_marker(self, tid, name, start_time, end_time, category): if tid not in self.threads: return - thread_data = self.threads[tid] duration = end_time - start_time name_idx = self._intern_string(name) - markers = thread_data["markers"] - markers["name"].append(name_idx) - markers["startTime"].append(start_time) - markers["endTime"].append(end_time) - markers["phase"].append(1) # 1 = interval marker - markers["category"].append(category) - markers["data"].append({ - "type": name.replace(" ", ""), - "duration": duration, - "tid": tid - }) - - def _add_opcode_interval_marker(self, tid, opcode, lineno, col_offset, funcname, start_time, end_time): + self.threads[tid]["_spill"].append_marker( + name_idx, start_time, end_time, 1, category, { + "type": name.replace(" ", ""), + "duration": duration, + "tid": tid, + } + ) + + def _add_opcode_interval_marker(self, tid, opcode, lineno, col_offset, + funcname, start_time, end_time): """Add an interval marker for opcode execution span.""" if tid not in self.threads or opcode is None: return - thread_data = self.threads[tid] opcode_info = get_opcode_info(opcode) # Use formatted opcode name (with base opcode for specialized ones) formatted_opname = format_opcode(opcode) name_idx = self._intern_string(formatted_opname) - markers = thread_data["markers"] - markers["name"].append(name_idx) - markers["startTime"].append(start_time) - markers["endTime"].append(end_time) - markers["phase"].append(1) # 1 = interval marker - markers["category"].append(CATEGORY_OPCODES) - markers["data"].append({ - "type": "Opcode", - "opcode": opcode, - "opname": formatted_opname, - "base_opname": opcode_info["base_opname"], - "is_specialized": opcode_info["is_specialized"], - "line": lineno, - "column": col_offset if col_offset >= 0 else None, - "function": funcname, - "duration": end_time - start_time, - }) + self.threads[tid]["_spill"].append_marker( + name_idx, start_time, end_time, 1, CATEGORY_OPCODES, { + "type": "Opcode", + "opcode": opcode, + "opname": formatted_opname, + "base_opname": opcode_info["base_opname"], + "is_specialized": opcode_info["is_specialized"], + "line": lineno, + "column": col_offset if col_offset >= 0 else None, + "function": funcname, + "duration": end_time - start_time, + } + ) def _process_stack(self, thread_data, frames): """Process a stack and return the stack index.""" @@ -660,7 +707,6 @@ def _finalize_markers(self): def export(self, filename): """Export the profile to a Gecko JSON file.""" - if self.sample_count > 0 and self.last_sample_time > 0: self.interval = self.last_sample_time / self.sample_count @@ -681,19 +727,30 @@ def spin(): spinner_thread = threading.Thread(target=spin, daemon=True) spinner_thread.start() + temp_path = None + replaced = False try: - # Finalize any open markers before building profile - self._finalize_markers() - - profile = self._build_profile() - - with open(filename, "w") as f: - json.dump(profile, f, separators=(",", ":")) + self._prepare_for_serialization() + output_dir = os.path.dirname(os.path.abspath(filename)) or "." + with tempfile.NamedTemporaryFile( + "w", dir=output_dir, delete=False + ) as file: + temp_path = file.name + self._stream_profile(file) + os.replace(temp_path, filename) + replaced = True finally: + self.exported = True stop_spinner.set() spinner_thread.join(timeout=1.0) # Small delay to ensure the clear happens time.sleep(0.01) + if temp_path is not None and not replaced: + try: + os.unlink(temp_path) + except FileNotFoundError: + pass + self._cleanup_spills() print(f"Gecko profile written to {filename}") print( @@ -727,34 +784,17 @@ def _build_marker_schema(self): def _build_profile(self): """Build the complete profile structure in processed format.""" - # Convert thread data to final format - threads = [] - - for tid, thread_data in self.threads.items(): - # Update lengths - samples = thread_data["samples"] - stack_table = thread_data["stackTable"] - frame_table = thread_data["frameTable"] - func_table = thread_data["funcTable"] - resource_table = thread_data["resourceTable"] - - samples["length"] = len(samples["stack"]) - stack_table["length"] = len(stack_table["frame"]) - frame_table["length"] = len(frame_table["func"]) - func_table["length"] = len(func_table["name"]) - resource_table["length"] = len(resource_table["name"]) - thread_data["markers"]["length"] = len(thread_data["markers"]["name"]) - - # Clean up internal caches - del thread_data["_stackCache"] - del thread_data["_frameCache"] - del thread_data["_funcCache"] - del thread_data["_resourceCache"] - - threads.append(thread_data) - - # Main profile structure in processed format - profile = { + try: + self._prepare_for_serialization() + file = io.StringIO() + self._stream_profile(file) + return json.loads(file.getvalue()) + finally: + self.exported = True + self._cleanup_spills() + + def _profile_head(self): + return { "meta": { "interval": self.interval, "startTime": self.start_time, @@ -784,7 +824,10 @@ def _build_profile(self): }, }, "libs": self.libs, - "threads": threads, + } + + def _profile_tail(self): + return { "pages": [], "shared": { "stringArray": self.global_strings, @@ -792,4 +835,146 @@ def _build_profile(self): }, } - return profile + def _prepare_for_serialization(self): + if self.exported: + raise RuntimeError("GeckoCollector has already been exported") + self._finalize_markers() + for thread_data in self.threads.values(): + thread_data["_spill"].prepare_read() + thread_data["stackTable"]["length"] = len(thread_data["stackTable"]["frame"]) + thread_data["frameTable"]["length"] = len(thread_data["frameTable"]["func"]) + thread_data["funcTable"]["length"] = len(thread_data["funcTable"]["name"]) + thread_data["resourceTable"]["length"] = len(thread_data["resourceTable"]["name"]) + + def _cleanup_spills(self): + if self.spill_dir is not None: + self.spill_dir.cleanup() + self.spill_dir = None + + def _stream_profile(self, file): + file.write("{") + first = True + for key, value in self._profile_head().items(): + first = _write_json_member(file, key, value, first) + + first = _write_member_name(file, "threads", first) + file.write("[") + for index, (tid, thread_data) in enumerate(self.threads.items()): + if index: + file.write(",") + self._stream_thread(file, tid, thread_data) + file.write("]") + + for key, value in self._profile_tail().items(): + first = _write_json_member(file, key, value, first) + file.write("}") + + def _stream_thread(self, file, tid, thread_data): + spill = thread_data["_spill"] + metadata = { + "name": thread_data["name"], + "isMainThread": thread_data["isMainThread"], + "processStartupTime": thread_data["processStartupTime"], + "processShutdownTime": thread_data["processShutdownTime"], + "registerTime": thread_data["registerTime"], + "unregisterTime": thread_data["unregisterTime"], + "pausedRanges": thread_data["pausedRanges"], + "pid": thread_data["pid"], + "tid": thread_data["tid"], + "processType": thread_data["processType"], + "processName": thread_data["processName"], + } + file.write("{") + first = True + for key, value in metadata.items(): + first = _write_json_member(file, key, value, first) + + first = _write_member_name(file, "samples", first) + self._stream_samples(file, spill) + for key in ( + "stackTable", + "frameTable", + "funcTable", + "resourceTable", + "nativeSymbols", + ): + first = _write_json_member(file, key, thread_data[key], first) + first = _write_member_name(file, "markers", first) + self._stream_markers(file, spill) + file.write("}") + + def _stream_samples(self, file, spill): + _stream_column_table( + file, + ( + ("stack", spill.samples_stack.iter_tokens()), + ("time", spill.samples_time.iter_tokens()), + ("eventDelay", ("null" for _ in range(spill.sample_count))), + ), + spill.sample_count, + ( + ("weight", None), + ("weightType", "samples"), + ("length", spill.sample_count), + ), + ) + + def _stream_markers(self, file, spill): + _stream_column_table( + file, + ( + ("data", spill.markers_data.iter_tokens()), + ("name", spill.markers_name.iter_tokens()), + ("startTime", spill.markers_start_time.iter_tokens()), + ("endTime", spill.markers_end_time.iter_tokens()), + ("phase", spill.markers_phase.iter_tokens()), + ("category", spill.markers_category.iter_tokens()), + ), + spill.marker_count, + (("length", spill.marker_count),), + ) + + +def _write_json(file, value): + for chunk in _JSON_ENCODER.iterencode(value): + file.write(chunk) + + +def _write_member_name(file, name, first): + if not first: + file.write(",") + _write_json(file, name) + file.write(":") + return False + + +def _write_json_member(file, name, value, first): + first = _write_member_name(file, name, first) + _write_json(file, value) + return first + + +def _stream_column_table(file, columns, expected_count, trailing_members=()): + file.write("{") + first = True + for name, token_iter in columns: + first = _write_member_name(file, name, first) + _stream_array(file, token_iter, expected_count, name) + for name, value in trailing_members: + first = _write_json_member(file, name, value, first) + file.write("}") + + +def _stream_array(file, token_iter, expected_count, label="array"): + file.write("[") + count = 0 + for token in token_iter: + if count: + file.write(",") + file.write(token) + count += 1 + if count != expected_count: + raise RuntimeError( + f"streamed {count} {label} items, expected {expected_count}" + ) + file.write("]") diff --git a/Lib/test/test_profiling/test_sampling_profiler/test_collectors.py b/Lib/test/test_profiling/test_sampling_profiler/test_collectors.py index 390a1479fdd2975..1ab31af67fec522 100644 --- a/Lib/test/test_profiling/test_sampling_profiler/test_collectors.py +++ b/Lib/test/test_profiling/test_sampling_profiler/test_collectors.py @@ -11,6 +11,7 @@ try: import _remote_debugging # noqa: F401 + from profiling.sampling import gecko_collector from profiling.sampling.pstats_collector import PstatsCollector from profiling.sampling.stack_collector import ( CollapsedStackCollector, @@ -59,6 +60,42 @@ def find_child_by_name(children, strings, substr): return None +def export_gecko_profile(testcase, collector): + gecko_out = tempfile.NamedTemporaryFile(suffix=".json", delete=False) + testcase.addCleanup(close_and_unlink, gecko_out) + # We cannot overwrite an open file on Windows. + gecko_out.close() + + with captured_stdout(), captured_stderr(): + collector.export(gecko_out.name) + + testcase.assertGreater(os.path.getsize(gecko_out.name), 0) + with open(gecko_out.name, encoding="utf-8") as file: + return json.load(file) + + +def assert_gecko_column_lengths(testcase, table, columns): + expected = table["length"] + for column in columns: + testcase.assertEqual( + len(table[column]), expected, + f"{column!r} has wrong length", + ) + + +def gecko_marker_names(profile, markers): + string_array = profile["shared"]["stringArray"] + return [string_array[idx] for idx in markers["name"]] + + +def gecko_opcode_marker_data(profile): + markers = profile["threads"][0]["markers"] + return [ + data for data in markers["data"] + if data.get("type") == "Opcode" + ] + + class TestSampleProfilerComponents(unittest.TestCase): """Unit tests for individual profiler components.""" @@ -583,9 +620,10 @@ def test_gecko_collector_basic(self): # Verify samples samples = thread_data["samples"] - self.assertEqual(len(samples["stack"]), 1) - self.assertEqual(len(samples["time"]), 1) self.assertEqual(samples["length"], 1) + assert_gecko_column_lengths( + self, samples, ("stack", "time", "eventDelay") + ) # Verify function table structure and content func_table = thread_data["funcTable"] @@ -622,9 +660,6 @@ def test_gecko_collector_basic(self): @unittest.skipIf(is_emscripten, "threads not available") def test_gecko_collector_export(self): """Test Gecko profile export functionality.""" - gecko_out = tempfile.NamedTemporaryFile(suffix=".json", delete=False) - self.addCleanup(close_and_unlink, gecko_out) - collector = GeckoCollector(1000) test_frames1 = [ @@ -657,17 +692,7 @@ def test_gecko_collector_export(self): collector.collect(test_frames2) collector.collect(test_frames3) - # Export gecko profile - with captured_stdout(), captured_stderr(): - collector.export(gecko_out.name) - - # Verify file was created and contains valid data - self.assertTrue(os.path.exists(gecko_out.name)) - self.assertGreater(os.path.getsize(gecko_out.name), 0) - - # Check file contains valid JSON - with open(gecko_out.name, "r") as f: - profile_data = json.load(f) + profile_data = export_gecko_profile(self, collector) # Should be valid Gecko profile format self.assertIn("meta", profile_data) @@ -688,6 +713,100 @@ def test_gecko_collector_export(self): self.assertIn("func2", string_array) self.assertIn("other_func", string_array) + thread_data = profile_data["threads"][0] + assert_gecko_column_lengths( + self, thread_data["samples"], ("stack", "time", "eventDelay") + ) + + @unittest.skipIf(is_emscripten, "threads not available") + def test_gecko_collector_export_after_spill_flush(self): + """Test Gecko profile export after spill buffers flush to disk.""" + old_buffer_bytes = gecko_collector.DEFAULT_SPILL_BUFFER_BYTES + gecko_collector.DEFAULT_SPILL_BUFFER_BYTES = 1 + self.addCleanup( + setattr, gecko_collector, "DEFAULT_SPILL_BUFFER_BYTES", + old_buffer_bytes + ) + + collector = GeckoCollector(1000) + test_frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [MockFrameInfo("file.py", 10, "func")], + status=THREAD_STATUS_HAS_GIL, + ) + ], + ) + ] + collector.collect(test_frames, timestamps_us=[1000, 2000, 3000]) + + profile_data = export_gecko_profile(self, collector) + samples = profile_data["threads"][0]["samples"] + self.assertEqual(samples["length"], 3) + assert_gecko_column_lengths( + self, samples, ("stack", "time", "eventDelay") + ) + + @unittest.skipIf(is_emscripten, "threads not available") + def test_gecko_collector_rejects_collect_after_export(self): + collector = GeckoCollector(1000) + test_frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [MockFrameInfo("file.py", 10, "func")], + status=THREAD_STATUS_HAS_GIL, + ) + ], + ) + ] + collector.collect(test_frames) + export_gecko_profile(self, collector) + + with self.assertRaisesRegex(RuntimeError, "after export"): + collector.collect(test_frames) + + @unittest.skipIf(is_emscripten, "threads not available") + def test_gecko_collector_export_failure_keeps_existing_file(self): + collector = GeckoCollector(1000) + test_frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [MockFrameInfo("file.py", 10, "func")], + status=THREAD_STATUS_HAS_GIL, + ) + ], + ) + ] + collector.collect(test_frames) + + with tempfile.TemporaryDirectory() as temp_dir: + filename = os.path.join(temp_dir, "profile.json") + with open(filename, "w", encoding="utf-8") as file: + file.write("existing") + + before = set(os.listdir(temp_dir)) + + def fail(file): + raise OSError("boom") + + collector._stream_profile = fail + with captured_stdout(), captured_stderr(): + with self.assertRaisesRegex(OSError, "boom"): + collector.export(filename) + + with open(filename, encoding="utf-8") as file: + self.assertEqual(file.read(), "existing") + self.assertEqual(set(os.listdir(temp_dir)), before) + def test_gecko_collector_markers(self): """Test Gecko profile markers for GIL and CPU state tracking.""" collector = GeckoCollector(1000) @@ -771,21 +890,16 @@ def test_gecko_collector_markers(self): self.assertIn("markers", thread_data) markers = thread_data["markers"] - # Should have marker arrays - self.assertIn("name", markers) - self.assertIn("startTime", markers) - self.assertIn("endTime", markers) - self.assertIn("category", markers) self.assertGreater( markers["length"], 0, "Should have generated markers" ) - - # Get marker names from string table - string_array = profile_data["shared"]["stringArray"] - marker_names = [string_array[idx] for idx in markers["name"]] + assert_gecko_column_lengths( + self, markers, + ("data", "name", "startTime", "endTime", "phase", "category"), + ) # Verify we have different marker types - marker_name_set = set(marker_names) + marker_name_set = set(gecko_marker_names(profile_data, markers)) # Should have "Has GIL" markers (when thread had GIL) self.assertIn( @@ -2659,6 +2773,7 @@ def test_gecko_collector_opcodes_enabled(self): def test_gecko_opcode_state_tracking(self): """Test that GeckoCollector tracks opcode state changes.""" collector = GeckoCollector(sample_interval_usec=1000, opcodes=True) + self.addCleanup(collector._cleanup_spills) # First sample with opcode 90 (RAISE_VARARGS) frame1 = MockFrameInfo("test.py", 10, "func", opcode=90) @@ -2702,10 +2817,28 @@ def test_gecko_opcode_state_change_emits_marker(self): collector.collect(frames2) # Should have emitted a marker for the first opcode - thread_data = collector.threads[1] - markers = thread_data["markers"] - # At least one marker should have been added - self.assertGreater(len(markers["name"]), 0) + profile = collector._build_profile() + markers = profile["threads"][0]["markers"] + assert_gecko_column_lengths( + self, markers, + ("data", "name", "startTime", "endTime", "phase", "category"), + ) + opcode_markers = gecko_opcode_marker_data(profile) + self.assertIn( + { + "opcode": 90, + "line": 10, + "function": "func", + }, + [ + { + "opcode": marker["opcode"], + "line": marker["line"], + "function": marker["function"], + } + for marker in opcode_markers + ], + ) def test_gecko_opcode_markers_not_emitted_when_disabled(self): """Test that no opcode markers when opcodes=False.""" @@ -2729,8 +2862,9 @@ def test_gecko_opcode_markers_not_emitted_when_disabled(self): ] collector.collect(frames2) - # opcode_state should not be tracked - self.assertEqual(len(collector.opcode_state), 0) + profile = collector._build_profile() + self.assertEqual(gecko_opcode_marker_data(profile), []) + self.assertEqual(profile["meta"]["markerSchema"], []) def test_gecko_opcode_with_none_opcode(self): """Test that None opcode doesn't cause issues.""" @@ -2746,9 +2880,8 @@ def test_gecko_opcode_with_none_opcode(self): ] collector.collect(frames) - # Should track the state but opcode is None - self.assertIn(1, collector.opcode_state) - self.assertIsNone(collector.opcode_state[1][0]) + profile = collector._build_profile() + self.assertEqual(gecko_opcode_marker_data(profile), []) class TestCollectorFrameFormat(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Library/2026-06-03-13-51-29.gh-issue-150662.ELT8Vg.rst b/Misc/NEWS.d/next/Library/2026-06-03-13-51-29.gh-issue-150662.ELT8Vg.rst new file mode 100644 index 000000000000000..42ed6ad7cd3c65f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-03-13-51-29.gh-issue-150662.ELT8Vg.rst @@ -0,0 +1,4 @@ +Fix the ``--gecko`` collector in :mod:`profiling.sampling` that kept every +sample in memory. It now writes sample and marker data to temporary files +and reads them back, ultimately building the output file at the end. Patch +by Pablo Galindo and Maurycy Pawłowski-Wieroński. From 988c367a386bc5283847c51703277a901b7c3f92 Mon Sep 17 00:00:00 2001 From: Shamil Date: Sat, 6 Jun 2026 10:19:49 +0300 Subject: [PATCH 042/185] gh-148832: Use `-icf=0` in BOLT_APPLY_FLAGS (gh-148833) --- .../next/Build/2026-04-21-17-27-52.gh-issue-148832.Kx9aQ1.rst | 4 ++++ configure | 2 +- configure.ac | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Build/2026-04-21-17-27-52.gh-issue-148832.Kx9aQ1.rst diff --git a/Misc/NEWS.d/next/Build/2026-04-21-17-27-52.gh-issue-148832.Kx9aQ1.rst b/Misc/NEWS.d/next/Build/2026-04-21-17-27-52.gh-issue-148832.Kx9aQ1.rst new file mode 100644 index 000000000000000..867cfcc5ab62ca2 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2026-04-21-17-27-52.gh-issue-148832.Kx9aQ1.rst @@ -0,0 +1,4 @@ +Fix :option:`--enable-bolt` build by switching the default BOLT flag from +``-icf=1`` to ``-icf=0``. ``-icf=1`` folds address-taken functions and +breaks type-slot dispatch, crashing on :mod:`list` and :mod:`tuple` +concatenation. Patched by Shamil Abdulaev. diff --git a/configure b/configure index eb53b200bf78bc5..67e5b3e46828024 100755 --- a/configure +++ b/configure @@ -9593,7 +9593,7 @@ printf "%s\n" "$BOLT_INSTRUMENT_FLAGS" >&6; } printf %s "checking BOLT_APPLY_FLAGS... " >&6; } if test -z "${BOLT_APPLY_FLAGS}" then - BOLT_APPLY_FLAGS=" ${BOLT_COMMON_FLAGS} -reorder-blocks=ext-tsp -reorder-functions=cdsort -split-functions -icf=1 -inline-all -split-eh -reorder-functions-use-hot-size -peepholes=none -jump-tables=aggressive -inline-ap -indirect-call-promotion=all -dyno-stats -use-gnu-stack -frame-opt=hot " + BOLT_APPLY_FLAGS=" ${BOLT_COMMON_FLAGS} -reorder-blocks=ext-tsp -reorder-functions=cdsort -split-functions -icf=0 -inline-all -split-eh -reorder-functions-use-hot-size -peepholes=none -jump-tables=aggressive -inline-ap -indirect-call-promotion=all -dyno-stats -use-gnu-stack -frame-opt=hot " fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $BOLT_APPLY_FLAGS" >&5 diff --git a/configure.ac b/configure.ac index b2f3f7210050693..cdff5ec78a5fb75 100644 --- a/configure.ac +++ b/configure.ac @@ -2255,7 +2255,7 @@ then -reorder-blocks=ext-tsp -reorder-functions=cdsort -split-functions - -icf=1 + -icf=0 -inline-all -split-eh -reorder-functions-use-hot-size From e3d14d2d726276203362a7b21c12276a74cb5f26 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Sat, 6 Jun 2026 10:34:33 +0200 Subject: [PATCH 043/185] gh-150889: Improve performance of unicodedata.normalize() (GH-150890) Scan the nfc_first/nfc_last reindex tables comparing only .start, range-check the candidate once, and terminate on a sentinel above every codepoint, so each entry costs a single comparison. ~2x faster on non-Latin and combining-heavy NFC/NFKC input; no new data tables. Co-Authored-By: Claude Opus 4.8 (1M context) --- ...-06-04-10-44-36.gh-issue-150889.UYNLR_.rst | 1 + Modules/unicodedata.c | 22 +++++++++++-------- Modules/unicodedata_db.h | 4 ++-- Tools/unicode/makeunicodedata.py | 10 +++++++-- 4 files changed, 24 insertions(+), 13 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-06-04-10-44-36.gh-issue-150889.UYNLR_.rst diff --git a/Misc/NEWS.d/next/Library/2026-06-04-10-44-36.gh-issue-150889.UYNLR_.rst b/Misc/NEWS.d/next/Library/2026-06-04-10-44-36.gh-issue-150889.UYNLR_.rst new file mode 100644 index 000000000000000..a5cc1da758637c5 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-04-10-44-36.gh-issue-150889.UYNLR_.rst @@ -0,0 +1 @@ +Speed up :func:`unicodedata.normalize` for the NFC and NFKC forms of non-ASCII text up to a factor 2. diff --git a/Modules/unicodedata.c b/Modules/unicodedata.c index 60df68216938134..bcdcc624e66f930 100644 --- a/Modules/unicodedata.c +++ b/Modules/unicodedata.c @@ -785,15 +785,19 @@ nfd_nfkd(PyObject *self, PyObject *input, int k) static int find_nfc_index(const struct reindex* nfc, Py_UCS4 code) { - unsigned int index; - for (index = 0; nfc[index].start; index++) { - unsigned int start = nfc[index].start; - if (code < start) - return -1; - if (code <= start + nfc[index].count) { - unsigned int delta = code - start; - return nfc[index].index + delta; - } + /* The table is sorted by .start ascending with disjoint [start, start+count] + ranges and ends with a sentinel whose .start exceeds every codepoint, so + a single .start <= code test per entry also stops at the sentinel. Find + the first entry past code, then range-check the candidate (entry i - 1). */ + unsigned int i; + for (i = 0; (Py_UCS4)nfc[i].start <= code; i++) { + } + if (i == 0) { + return -1; + } + unsigned int start = nfc[i - 1].start; + if (code <= start + nfc[i - 1].count) { + return nfc[i - 1].index + (code - start); } return -1; } diff --git a/Modules/unicodedata_db.h b/Modules/unicodedata_db.h index 9e88f5cca7115b0..1c961c5e329341c 100644 --- a/Modules/unicodedata_db.h +++ b/Modules/unicodedata_db.h @@ -629,7 +629,7 @@ static struct reindex nfc_first[] = { { 93539, 0, 388}, { 93543, 0, 389}, { 93545, 0, 390}, - {0,0,0} + {0x7fffffff, 0, 0} }; static struct reindex nfc_last[] = { @@ -680,7 +680,7 @@ static struct reindex nfc_last[] = { { 90398, 2, 67}, { 90409, 0, 70}, { 93543, 0, 71}, - {0,0,0} + {0x7fffffff, 0, 0} }; /* string literals */ diff --git a/Tools/unicode/makeunicodedata.py b/Tools/unicode/makeunicodedata.py index 5db850ca2d1f0c0..76283d6b794a0b0 100644 --- a/Tools/unicode/makeunicodedata.py +++ b/Tools/unicode/makeunicodedata.py @@ -342,15 +342,21 @@ def makeunicodedata(unicode, trace): fprint("#define TOTAL_FIRST",total_first) fprint("#define TOTAL_LAST",total_last) fprint("struct reindex{int start;short count,index;};") + # The reindex tables are read only by find_nfc_index(), which scans + # forward while .start <= code. The trailing sentinel's .start must + # exceed every codepoint (so the scan stops with a single comparison) + # and fit the signed int .start field. + nfc_sentinel = 0x7fffffff + assert sys.maxunicode < nfc_sentinel <= 0x7fffffff fprint("static struct reindex nfc_first[] = {") for start,end in comp_first_ranges: fprint(" { %d, %d, %d}," % (start,end-start,comp_first[start])) - fprint(" {0,0,0}") + fprint(" {0x%x, 0, 0}" % nfc_sentinel) fprint("};\n") fprint("static struct reindex nfc_last[] = {") for start,end in comp_last_ranges: fprint(" { %d, %d, %d}," % (start,end-start,comp_last[start])) - fprint(" {0,0,0}") + fprint(" {0x%x, 0, 0}" % nfc_sentinel) fprint("};\n") # FIXME: the following tables could be made static, and From 1bea55662004f072bb8550e9531b470ae1549eab Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 6 Jun 2026 11:49:07 +0300 Subject: [PATCH 044/185] =?UTF-8?q?gh-84649:=20Use=20statx()=20in=20TimedR?= =?UTF-8?q?otatingFileHandler=20if=20available=20(=D0=9F=D0=A0-150968)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows to support rotation based on the file birth time on Linux. --- Doc/library/logging.handlers.rst | 7 ++++- Doc/whatsnew/3.16.rst | 11 +++++++ Lib/logging/handlers.py | 30 ++++++++++++++----- Lib/test/support/__init__.py | 5 ---- Lib/test/test_logging.py | 4 +-- .../2021-02-26-13-17-57.bpo-40469.yJHeQg.rst | 11 ++++--- 6 files changed, 46 insertions(+), 22 deletions(-) diff --git a/Doc/library/logging.handlers.rst b/Doc/library/logging.handlers.rst index 714db5fa12af0aa..5152c7561fa1f26 100644 --- a/Doc/library/logging.handlers.rst +++ b/Doc/library/logging.handlers.rst @@ -402,7 +402,8 @@ timed intervals. rollover interval. When computing the next rollover time for the first time (when the handler - is created), the last modification time of an existing log file, or else + is created), the creation time (if supported by the OS and file system) + or the last modification of an existing log file, or else the current time, is used to compute when the next rotation will occur. If the *utc* argument is true, times in UTC will be used; otherwise @@ -449,6 +450,10 @@ timed intervals. .. versionchanged:: 3.9 The *errors* parameter was added. + .. versionchanged:: next + Use the creation time instead of the last modification time, if supported by the OS and file system. + + .. method:: doRollover() Does a rollover, as described above. diff --git a/Doc/whatsnew/3.16.rst b/Doc/whatsnew/3.16.rst index a055113dec0494c..5a7da26bb5b95a6 100644 --- a/Doc/whatsnew/3.16.rst +++ b/Doc/whatsnew/3.16.rst @@ -102,6 +102,17 @@ lzma requires ``lzma`` 5.4.0 or newer while RISC-V requires 5.6.0 or newer. (Contributed by Chien Wong in :gh:`115988`.) +logging +------- + +* :class:`~logging.handlers.TimedRotatingFileHandler` now uses the creation + time instead of the last modification time of an existing log file as + the basis for the first rotation after handler creation, if supported by + the OS and file system. + This allows it to be used in short-running programs that start and end + before the rotation interval expires. + (Contributed by Iván Márton and Serhiy Storchaka in :gh:`84649`.) + os -- diff --git a/Lib/logging/handlers.py b/Lib/logging/handlers.py index 73782f53041008c..a5394d2dbea6494 100644 --- a/Lib/logging/handlers.py +++ b/Lib/logging/handlers.py @@ -284,14 +284,28 @@ def __init__(self, filename, when='h', interval=1, backupCount=0, if os.path.exists(filename): # Use the minimum of file creation and modification time as # the base of the rollover calculation - stat_result = os.stat(filename) - # Use st_birthtime whenever it is available or use st_ctime - # instead otherwise - try: - creation_time = stat_result.st_birthtime - except AttributeError: - creation_time = stat_result.st_ctime - t = int(min(creation_time, stat_result.st_mtime)) + creation_time = modification_time = None + if hasattr(os, 'statx'): + statx_result = os.statx(filename, + os.STATX_BTIME|os.STATX_CTIME|os.STATX_MTIME) + # Use stx_btime whenever it is available or use stx_ctime + # instead otherwise + creation_time = statx_result.stx_btime + if creation_time is None: + creation_time = statx_result.stx_ctime + modification_time = statx_result.stx_mtime + if creation_time is None or modification_time is None: + stat_result = os.stat(filename) + # Use st_birthtime whenever it is available or use st_ctime + # instead otherwise + if creation_time is None: + try: + creation_time = stat_result.st_birthtime + except AttributeError: + creation_time = stat_result.st_ctime + if modification_time is None: + modification_time = stat_result.st_mtime + t = int(min(creation_time, modification_time)) else: t = int(time.time()) self.rolloverAt = self.computeRollover(t) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 84f735c1537efa7..cd85ef60a80f4bf 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -40,7 +40,6 @@ "has_fork_support", "requires_fork", "has_subprocess_support", "requires_subprocess", "has_socket_support", "requires_working_socket", - "has_st_birthtime", "has_remote_subprocess_debugging", "requires_remote_subprocess_debugging", "anticipate_failure", "load_package_tests", "detect_api_mismatch", "check__all__", "skip_if_buggy_ucrt_strfptime", @@ -621,10 +620,6 @@ def skip_wasi_stack_overflow(): or is_android ) -# At the moment, st_birthtime attribute is only supported on Windows, -# MacOS and FreeBSD. -has_st_birthtime = sys.platform.startswith(("win", "freebsd", "darwin")) - def requires_fork(): return unittest.skipUnless(has_fork_support, "requires working os.fork()") diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 9f29fe8a5b3c9bc..cc2e9b782a35022 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -6652,8 +6652,8 @@ def test_rollover(self): print(tf.read()) self.assertTrue(found, msg=msg) - @unittest.skipUnless(support.has_st_birthtime, - "st_birthtime not available or supported by Python on this OS") + @unittest.skipUnless(hasattr(os.stat_result, 'st_birthtime') or hasattr(os, 'statx'), + "st_birthtime and statx() not available or supported by Python on this OS") @support.requires_resource('walltime') def test_rollover_based_on_st_birthtime_only(self): def add_record(message: str) -> None: diff --git a/Misc/NEWS.d/next/Library/2021-02-26-13-17-57.bpo-40469.yJHeQg.rst b/Misc/NEWS.d/next/Library/2021-02-26-13-17-57.bpo-40469.yJHeQg.rst index eab474dfd2ea82a..239f856bb04c0ae 100644 --- a/Misc/NEWS.d/next/Library/2021-02-26-13-17-57.bpo-40469.yJHeQg.rst +++ b/Misc/NEWS.d/next/Library/2021-02-26-13-17-57.bpo-40469.yJHeQg.rst @@ -1,6 +1,5 @@ -A bug has been fixed that made the ``TimedRotatingFileHandler`` use the -MTIME attribute of the configured log file to to detect whether it has to be -rotated yet or not. In cases when the file was changed within the rotation -period the value of the MTIME was also updated to the current time and as a -result the rotation never happened. The file creation time (CTIME) is used -instead that makes the rotation file modification independent. +:class:`~logging.handlers.TimedRotatingFileHandler` now uses the creation time +instead of the last modification time of an existing log file as the basis +for the first rotation after handler creation, if supported by the OS and file system. +This allows it to be used in short-running programs that start and end before +the rotation interval expires. From 3b9c40fe8757601c8212dcd0666f0ece10c8dd47 Mon Sep 17 00:00:00 2001 From: Thomas Kowalski Date: Sat, 6 Jun 2026 12:04:23 +0200 Subject: [PATCH 045/185] gh-149835: Use realpath() instead of abspath() in shutil.move() (GH-149986) --- Lib/shutil.py | 4 ++-- Lib/test/test_shutil.py | 17 +++++++++++++++++ ...26-05-18-17-46-00.gh-issue-149835.EebFlk.rst | 3 +++ 3 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Security/2026-05-18-17-46-00.gh-issue-149835.EebFlk.rst diff --git a/Lib/shutil.py b/Lib/shutil.py index c8d02bbaeb80b4e..4d5a283662101c4 100644 --- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -944,8 +944,8 @@ def move(src, dst, copy_function=copy2): return real_dst def _destinsrc(src, dst): - src = os.path.abspath(src) - dst = os.path.abspath(dst) + src = os.path.realpath(src) + dst = os.path.realpath(dst) if not src.endswith(os.path.sep): src += os.path.sep if not dst.endswith(os.path.sep): diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py index 59cb319b0a95b5e..bb901220fb408c1 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -2914,6 +2914,23 @@ def test_destinsrc_false_positive(self): finally: os_helper.rmtree(TESTFN) + @os_helper.skip_unless_symlink + def test_destinsrc_symlink_bypass(self): + tmp = self.mkdtemp() + src = os.path.join(tmp, 'src') + os.makedirs(src) + # tmp/link -> tmp (one level up) + link = os.path.join(tmp, 'link') + os.symlink(tmp, link) + # raw path: tmp/link/src/sub - no src prefix in string space + # real path: tmp/src/sub - physically inside src + dst = os.path.join(link, 'src', 'sub') + self.assertTrue( + shutil._destinsrc(src, dst), + msg='_destinsrc failed to detect dst inside src via symlink ' + '(dst=%s, src=%s)' % (dst, src), + ) + @os_helper.skip_unless_symlink @mock_rename def test_move_file_symlink(self): diff --git a/Misc/NEWS.d/next/Security/2026-05-18-17-46-00.gh-issue-149835.EebFlk.rst b/Misc/NEWS.d/next/Security/2026-05-18-17-46-00.gh-issue-149835.EebFlk.rst new file mode 100644 index 000000000000000..20cab736552486d --- /dev/null +++ b/Misc/NEWS.d/next/Security/2026-05-18-17-46-00.gh-issue-149835.EebFlk.rst @@ -0,0 +1,3 @@ +:func:`shutil.move` now resolves symlinks via :func:`os.path.realpath` +when checking whether the destination is inside the source directory, +preventing a symlink-based bypass of that guard. From 61c8eb446ddcfa9b06bfcdb1544d9a8ba2f8df49 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Sat, 6 Jun 2026 03:19:45 -0700 Subject: [PATCH 046/185] gh-150534: Add C23 half-turn trigonometric *pi functions (GH-150555) Add the the following functions to the math module: acospi, asinpi, atanpi, atan2pi, cospi, sinpi, tanpi. --- Doc/library/math.rst | 78 +- Doc/whatsnew/3.16.rst | 11 + Lib/test/mathdata/math_testcases.txt | 1616 +++++++++++++++++ Lib/test/test_math.py | 16 +- ...-05-27-15-51-29.gh-issue-150534.3Nan6S.rst | 6 + Modules/mathmodule.c | 197 +- configure | 29 + configure.ac | 2 + pyconfig.h.in | 21 + 9 files changed, 1952 insertions(+), 24 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-05-27-15-51-29.gh-issue-150534.3Nan6S.rst diff --git a/Doc/library/math.rst b/Doc/library/math.rst index 41a9a0ab55d8fab..efe411e5a43f27d 100644 --- a/Doc/library/math.rst +++ b/Doc/library/math.rst @@ -83,13 +83,20 @@ noted otherwise, all return values are floats. **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:`sin(x) ` Sine of *x* -:func:`tan(x) ` Tangent of *x* +:func:`acos(x) ` Arc cosine of *x*, in radians +:func:`acospi(x) ` Arc cosine of *x*, in half-turns +:func:`asin(x) ` Arc sine of *x*, in radians +:func:`asinpi(x) ` Arc sine of *x*, in half-turns +:func:`atan(x) ` Arc tangent of *x*, in radians +:func:`atanpi(x) ` Arc tangent of *x*, in half-turns +:func:`atan2(y, x) ` ``atan(y / x)``, in radians +:func:`atan2pi(y, x) ` ``atan(y / x)``, in half-turns +:func:`cos(x) ` Cosine of *x* radians +:func:`cospi(x) ` Cosine of *x⋅π* radians +:func:`sin(x) ` Sine of *x* radians +:func:`sinpi(x) ` Sine of *x⋅π* radians +:func:`tan(x) ` Tangent of *x* radians +:func:`tanpi(x) ` Tangent of *x⋅π* radians **Hyperbolic functions** -------------------------------------------------------------------------------------------------- @@ -599,18 +606,42 @@ Trigonometric functions ``pi``. +.. function:: acospi(x) + + Return the arc cosine of *x*, in half-turns. The result is between ``0`` and + ``1``. + + .. versionadded:: next + + .. function:: asin(x) Return the arc sine of *x*, in radians. The result is between ``-pi/2`` and ``pi/2``. +.. function:: asinpi(x) + + Return the arc sine of *x*, in half-turns. The result is between ``-0.5`` and + ``0.5``. + + .. versionadded:: next + + .. function:: atan(x) Return the arc tangent of *x*, in radians. The result is between ``-pi/2`` and ``pi/2``. +.. function:: atanpi(x) + + Return the arc tangent of *x*, in half-turns. The result is between ``-0.5`` and + ``0.5``. + + .. versionadded:: next + + .. function:: atan2(y, x) Return ``atan(y / x)``, in radians. The result is between ``-pi`` and ``pi``. @@ -621,21 +652,54 @@ Trigonometric functions -1)`` is ``-3*pi/4``. +.. function:: atan2pi(y, x) + + Return ``atanpi(y / x)``, in half-turns. The result is between ``-1`` and ``1``. + The vector in the plane from the origin to point ``(x, y)`` makes this angle + with the positive X axis. The point of :func:`atan2pi` is that the signs of both + inputs are known to it, so it can compute the correct quadrant for the angle. + For example, ``atanpi(1)`` and ``atan2pi(1, 1)`` are both ``0.25``, but + ``atan2pi(-1, -1)`` is ``-0.75``. + + .. versionadded:: next + + .. function:: cos(x) Return the cosine of *x* radians. +.. function:: cospi(x) + + Return the cosine of *x* half-turns (*x⋅π* radians). + + .. versionadded:: next + + .. function:: sin(x) Return the sine of *x* radians. +.. function:: sinpi(x) + + Return the sine of *x* half-turns (*x⋅π* radians). + + .. versionadded:: next + + .. function:: tan(x) Return the tangent of *x* radians. +.. function:: tanpi(x) + + Return the tangent of *x* half-turns (*x⋅π* radians). + + .. versionadded:: next + + Hyperbolic functions -------------------- diff --git a/Doc/whatsnew/3.16.rst b/Doc/whatsnew/3.16.rst index 5a7da26bb5b95a6..6c35124ba7b4865 100644 --- a/Doc/whatsnew/3.16.rst +++ b/Doc/whatsnew/3.16.rst @@ -113,6 +113,17 @@ logging before the rotation interval expires. (Contributed by Iván Márton and Serhiy Storchaka in :gh:`84649`.) +math +---- + +* Added trigonometric functions that work in units of half turns, rather than + radians. The new functions :func:`math.acospi`, :func:`math.asinpi`, + :func:`math.atanpi`, and :func:`math.atan2pi` return half-turn angles. The + new functions :func:`math.cospi`, :func:`math.sinpi`, and :func:`math.tanpi` + take half-turn angle arguments. These functions are recommended by IEEE + 754-2019 and standardized in C23. + (Contributed by Jeff Epler in :gh:`150534`.) + os -- diff --git a/Lib/test/mathdata/math_testcases.txt b/Lib/test/mathdata/math_testcases.txt index 958518824376f82..4a38a3666bab208 100644 --- a/Lib/test/mathdata/math_testcases.txt +++ b/Lib/test/mathdata/math_testcases.txt @@ -631,3 +631,1619 @@ log20646 log2 2.535995592365391e+208 -> 692.30359597460460 log20647 log2 6.2011236566089916e+233 -> 776.64177576730913 log20648 log2 2.1843274820677632e+253 -> 841.57499717289647 log20649 log2 8.7493931063474791e+297 -> 989.74182713073981 + +------------ +-- acospi -- +------------ + +acospi10000 acospi 0.0 -> 0.5 +acospi10001 acospi -0.0 -> 0.5 +acospi10002 acospi 1.0 -> 0.0 +acospi10003 acospi -1.0 -> 1.0 +acospi10004 acospi 0.5 -> 0.3333333333333333 +acospi10005 acospi -0.5 -> 0.6666666666666666 +acospi10006 acospi 0.75 -> 0.23005345616261588 +acospi10007 acospi 2.0000001329864406e-17 -> 0.5 +acospi10008 acospi 1.999999967550318e-17 -> 0.5 +acospi10009 acospi 2e-17 -> 0.5 +acospi10010 acospi 1.9999999999999998e-17 -> 0.5 +acospi10011 acospi 0.0625 -> 0.48009265723071126 +acospi10012 acospi 0.9999999403953552 -> 0.00010990189460803536 +acospi10013 acospi -0.9999999403953552 -> 0.999890098105392 +acospi10014 acospi 1.0 -> 0.0 +acospi10015 acospi 0.9999999403953552 -> 0.00010990189460803536 +acospi10016 acospi 0.9999999998835847 -> 4.857023409833964e-06 +acospi10017 acospi -0.9999999403953552 -> 0.999890098105392 +acospi10018 acospi -1.0 -> 1.0 +acospi10019 acospi -0.9999999998835847 -> 0.9999951429765902 +acospi10020 acospi 1.0 -> 0.0 +acospi10021 acospi 0.9999999403953552 -> 0.00010990189460803536 +acospi10022 acospi 0.9999999999999964 -> 2.6831517105016303e-08 +acospi10023 acospi -0.9999999403953552 -> 0.999890098105392 +acospi10024 acospi -1.0 -> 1.0 +acospi10025 acospi -0.9999999999999964 -> 0.9999999731684829 +acospi10026 acospi 1.0 -> 0.0 +acospi10027 acospi 0.9999999403953552 -> 0.00010990189460803536 +acospi10028 acospi 0.9999999999999999 -> 4.743186923619966e-09 +acospi10029 acospi -0.9999999403953552 -> 0.999890098105392 +acospi10030 acospi -1.0 -> 1.0 +acospi10031 acospi -0.9999999999999999 -> 0.999999995256813 +acospi10032 acospi 1.0 -> 0.0 +acospi10033 acospi 0.9999999403953552 -> 0.00010990189460803536 +acospi10034 acospi 0.9999999999999999 -> 4.743186923619966e-09 +acospi10035 acospi -0.9999999403953552 -> 0.999890098105392 +acospi10036 acospi -1.0 -> 1.0 +acospi10037 acospi -0.9999999999999999 -> 0.999999995256813 +acospi10038 acospi 1.0 -> 0.0 +acospi10039 acospi 0.9999999403953552 -> 0.00010990189460803536 +acospi10040 acospi 0.9999999999999999 -> 4.743186923619966e-09 +acospi10041 acospi -0.9999999403953552 -> 0.999890098105392 +acospi10042 acospi -1.0 -> 1.0 +acospi10043 acospi -0.9999999999999999 -> 0.999999995256813 +acospi10044 acospi 1.0 -> 0.0 +acospi10045 acospi 0.9999999403953552 -> 0.00010990189460803536 +acospi10046 acospi 0.9999999999999999 -> 4.743186923619966e-09 +acospi10047 acospi -0.9999999403953552 -> 0.999890098105392 +acospi10048 acospi -1.0 -> 1.0 +acospi10049 acospi -0.9999999999999999 -> 0.999999995256813 +acospi10050 acospi 0.03125 -> 0.4900511963370614 +acospi10051 acospi 0.0009765625 -> 0.49968915045236545 +acospi10052 acospi 3.0517578125e-05 -> 0.4999902859531789 +acospi10053 acospi 9.5367431640625e-07 -> 0.49999969643603687 +acospi10054 acospi 2.9802322387695312e-08 -> 0.49999999051362615 +acospi10055 acospi 9.313225746154785e-10 -> 0.4999999997035508 +acospi10056 acospi 2.9103830456733704e-11 -> 0.49999999999073597 +acospi10057 acospi 9.094947017729282e-13 -> 0.4999999999997105 +acospi10058 acospi 2.842170943040401e-14 -> 0.49999999999999095 +acospi10059 acospi 8.881784197001252e-16 -> 0.4999999999999997 +acospi10060 acospi 2.7755575615628914e-17 -> 0.5 +acospi10061 acospi 8.673617379884035e-19 -> 0.5 +acospi10062 acospi 2.710505431213761e-20 -> 0.5 +acospi10063 acospi 8.470329472543003e-22 -> 0.5 +acospi10064 acospi 2.6469779601696886e-23 -> 0.5 +acospi10065 acospi 8.271806125530277e-25 -> 0.5 +acospi10066 acospi 2.5849394142282115e-26 -> 0.5 +acospi10067 acospi 8.077935669463161e-28 -> 0.5 +acospi10068 acospi 2.524354896707238e-29 -> 0.5 +acospi10069 acospi 7.888609052210118e-31 -> 0.5 +acospi10070 acospi 2.465190328815662e-32 -> 0.5 +acospi10071 acospi 7.703719777548943e-34 -> 0.5 +acospi10072 acospi 2.407412430484045e-35 -> 0.5 +acospi10073 acospi 7.52316384526264e-37 -> 0.5 +acospi10074 acospi -0.03125 -> 0.5099488036629386 +acospi10075 acospi -2.9802322387695312e-08 -> 0.5000000094863738 +acospi10076 acospi -2.842170943040401e-14 -> 0.500000000000009 +acospi10077 acospi -2.710505431213761e-20 -> 0.5 +acospi10078 acospi -2.5849394142282115e-26 -> 0.5 +acospi10079 acospi -2.465190328815662e-32 -> 0.5 +acospi10080 acospi -2.350988701644575e-38 -> 0.5 +acospi10081 acospi -0.1251312643289566 -> 0.5399352010629168 +acospi10082 acospi 0.9386433362960815 -> 0.1120835750337698 +acospi10083 acospi 0.3664908707141876 -> 0.3805590984950769 +acospi10084 acospi 0.36649084091186523 -> 0.38055910869085624 +acospi10085 acospi 0.366490841000206 -> 0.38055910866063364 +acospi10086 acospi -0.13816863298416138 -> 0.5441215931223088 +acospi10087 acospi -0.13816864788532257 -> 0.5441215979114296 +acospi10088 acospi -0.13816864007172486 -> 0.5441215954001982 +acospi10089 acospi 0.9126854538917542 -> 0.1340047775091756 +acospi10090 acospi 0.9126853942871094 -> 0.13400482393558352 +acospi10091 acospi 0.912685444573738 -> 0.13400478476703412 +acospi10092 acospi 0.9126854445737379 -> 0.1340047847670342 +acospi10093 acospi 0.880692720413208 -> 0.1570775606195134 +acospi10094 acospi 0.8806926608085632 -> 0.15707760067276577 +acospi10095 acospi 0.8806926814412734 -> 0.15707758680795558 +acospi10096 acospi 0.8806926814412733 -> 0.15707758680795567 +acospi10097 acospi 0.2435227781534195 -> 0.4216969371828132 +acospi10098 acospi 0.2435227632522583 -> 0.42169694207322506 +acospi10099 acospi 0.24352277005396675 -> 0.42169693984097245 +acospi10100 acospi 0.1279129981994629 -> 0.45917217189807447 +acospi10101 acospi -0.11572346091270447 -> 0.5369186384114879 +acospi10102 acospi 0.9999996423721313 -> 0.0002692035702405639 +acospi10103 acospi 0.9999995827674866 -> 0.000290773090413406 +acospi10104 acospi 0.9999996241289419 -> 0.0002759844303031754 +acospi10105 acospi 0.9995118975639343 -> 0.009945766966749 +acospi10106 acospi 0.9995118379592896 -> 0.009946374261451639 +acospi10107 acospi 0.9995118644629029 -> 0.009946104228281858 +acospi10108 acospi 0.9995118644629027 -> 0.009946104228282989 +acospi10109 acospi 1.1754943508222875e-38 -> 0.5 +acospi10110 acospi 2.2250738585072014e-308 -> 0.5 +acospi10111 acospi 2.004168360008973e-292 -> 0.5 +acospi10112 acospi -1.1754943508222875e-38 -> 0.5 +acospi10113 acospi -2.2250738585072014e-308 -> 0.5 +acospi10114 acospi -2.004168360008973e-292 -> 0.5 +acospi10115 acospi 1.401298464324817e-45 -> 0.5 +acospi10116 acospi 5e-324 -> 0.5 +acospi10117 acospi -1.401298464324817e-45 -> 0.5 +acospi10118 acospi -5e-324 -> 0.5 +acospi10119 acospi -0.5356916189193726 -> 0.6799490414927629 +acospi10120 acospi 0.9258930683135986 -> 0.12331439084862783 +acospi10121 acospi 0.9258930087089539 -> 0.12331444106954574 +acospi10122 acospi 0.9258930140454763 -> 0.12331443657316782 +acospi10123 acospi 0.9997028112411499 -> 0.007760542300314392 +acospi10124 acospi 0.9997027516365051 -> 0.007761320533135704 +acospi10125 acospi 0.9997027756274229 -> 0.00776100730317654 +acospi10126 acospi 0.9997027756274228 -> 0.007761007303177989 +acospi10127 acospi 0.7071993947029114 -> 0.24995830653935586 +acospi10128 acospi 0.7071993350982666 -> 0.249958333374387 +acospi10129 acospi 0.7071993911300235 -> 0.24995830814793119 +acospi10130 acospi 0.7071993911300234 -> 0.24995830814793124 + +acospi20001 acospi inf -> nan invalid +acospi20002 acospi -inf -> nan invalid +acospi20003 acospi nan -> nan + +------------ +-- asinpi -- +------------ + +asinpi10000 asinpi 0.0 -> 0.0 +asinpi10001 asinpi -0.0 -> -0.0 +asinpi10002 asinpi 0.5 -> 0.16666666666666666 +asinpi10003 asinpi -0.5 -> -0.16666666666666666 +asinpi10004 asinpi 1.0 -> 0.5 +asinpi10005 asinpi -1.0 -> -0.5 +asinpi10006 asinpi 0.75 -> 0.2699465438373841 +asinpi10007 asinpi 0.9999999403953552 -> 0.499890098105392 +asinpi10008 asinpi -0.9999999403953552 -> -0.499890098105392 +asinpi10009 asinpi 1.0 -> 0.5 +asinpi10010 asinpi 0.9999999403953552 -> 0.499890098105392 +asinpi10011 asinpi 0.9999999998835847 -> 0.49999514297659015 +asinpi10012 asinpi -0.9999999403953552 -> -0.499890098105392 +asinpi10013 asinpi -1.0 -> -0.5 +asinpi10014 asinpi -0.9999999998835847 -> -0.49999514297659015 +asinpi10015 asinpi 1.0 -> 0.5 +asinpi10016 asinpi 0.9999999403953552 -> 0.499890098105392 +asinpi10017 asinpi 0.9999999999999964 -> 0.4999999731684829 +asinpi10018 asinpi -0.9999999403953552 -> -0.499890098105392 +asinpi10019 asinpi -1.0 -> -0.5 +asinpi10020 asinpi -0.9999999999999964 -> -0.4999999731684829 +asinpi10021 asinpi 1.0 -> 0.5 +asinpi10022 asinpi 0.9999999403953552 -> 0.499890098105392 +asinpi10023 asinpi 0.9999999999999999 -> 0.4999999952568131 +asinpi10024 asinpi -0.9999999403953552 -> -0.499890098105392 +asinpi10025 asinpi -1.0 -> -0.5 +asinpi10026 asinpi -0.9999999999999999 -> -0.4999999952568131 +asinpi10027 asinpi 1.0 -> 0.5 +asinpi10028 asinpi 0.9999999403953552 -> 0.499890098105392 +asinpi10029 asinpi 0.9999999999999999 -> 0.4999999952568131 +asinpi10030 asinpi -0.9999999403953552 -> -0.499890098105392 +asinpi10031 asinpi -1.0 -> -0.5 +asinpi10032 asinpi -0.9999999999999999 -> -0.4999999952568131 +asinpi10033 asinpi 1.0 -> 0.5 +asinpi10034 asinpi 0.9999999403953552 -> 0.499890098105392 +asinpi10035 asinpi 0.9999999999999999 -> 0.4999999952568131 +asinpi10036 asinpi -0.9999999403953552 -> -0.499890098105392 +asinpi10037 asinpi -1.0 -> -0.5 +asinpi10038 asinpi -0.9999999999999999 -> -0.4999999952568131 +asinpi10039 asinpi 1.0 -> 0.5 +asinpi10040 asinpi 0.9999999403953552 -> 0.499890098105392 +asinpi10041 asinpi 0.9999999999999999 -> 0.4999999952568131 +asinpi10042 asinpi -0.9999999403953552 -> -0.499890098105392 +asinpi10043 asinpi -1.0 -> -0.5 +asinpi10044 asinpi -0.9999999999999999 -> -0.4999999952568131 +asinpi10045 asinpi 0.03125 -> 0.009948803662938608 +asinpi10046 asinpi 0.0009765625 -> 0.0003108495476345764 +asinpi10047 asinpi 3.0517578125e-05 -> 9.714046821081509e-06 +asinpi10048 asinpi 9.5367431640625e-07 -> 3.0356396311172383e-07 +asinpi10049 asinpi 2.9802322387695312e-08 -> 9.486373847239932e-09 +asinpi10050 asinpi 9.313225746154785e-10 -> 2.964491827262479e-10 +asinpi10051 asinpi 2.9103830456733704e-11 -> 9.264036960195246e-12 +asinpi10052 asinpi 9.094947017729282e-13 -> 2.8950115500610145e-13 +asinpi10053 asinpi 2.842170943040401e-14 -> 9.04691109394067e-15 +asinpi10054 asinpi 8.881784197001252e-16 -> 2.8271597168564594e-16 +asinpi10055 asinpi 2.7755575615628914e-17 -> 8.834874115176436e-18 +asinpi10056 asinpi 8.673617379884035e-19 -> 2.760898160992636e-19 +asinpi10057 asinpi 7.888609052210118e-31 -> 2.5110222495574236e-31 +asinpi10058 asinpi 1.401298464324817e-45 -> 4.460471546887532e-46 +asinpi10059 asinpi 0.0 -> 0.0 +asinpi10060 asinpi 2.409919865102884e-181 -> 7.671013179729553e-182 +asinpi10061 asinpi 1.401298464324817e-45 -> 4.460471546887532e-46 +asinpi10062 asinpi 0.0 -> 0.0 +asinpi10063 asinpi 5e-324 -> 0.0 +asinpi10064 asinpi -0.13099800050258636 -> -0.04181814856647883 +asinpi10065 asinpi -0.21591897308826447 -> -0.06927470561424247 +asinpi10066 asinpi -0.1944168210029602 -> -0.06228143288596484 +asinpi10067 asinpi -0.1944168359041214 -> -0.062281437721417 +asinpi10068 asinpi -0.19441682493723628 -> -0.06228143416264412 +asinpi10069 asinpi 0.9866708517074585 -> 0.4479704696000938 +asinpi10070 asinpi 0.9866707921028137 -> 0.4479703530089429 +asinpi10071 asinpi 0.9866708504122479 -> 0.4479704670665621 +asinpi10072 asinpi 0.9866708504122478 -> 0.44797046706656185 +asinpi10073 asinpi -0.6866101622581482 -> -0.24090205124758424 +asinpi10074 asinpi 0.8438399434089661 -> 0.3197103200989401 +asinpi10075 asinpi -0.13135038316249847 -> -0.04193129309542388 +asinpi10076 asinpi -0.5068531036376953 -> -0.16919134974732694 +asinpi10077 asinpi -0.5068531632423401 -> -0.1691913717566472 +asinpi10078 asinpi -0.5068531437412152 -> -0.16919136455575687 +asinpi10079 asinpi -0.7008922100067139 -> -0.2472146113103207 +asinpi10080 asinpi 0.12256252765655518 -> 0.03911120286288364 +asinpi10081 asinpi 0.12256252020597458 -> 0.03911120047327442 +asinpi10082 asinpi 0.12256252763528681 -> 0.03911120285606228 +asinpi10083 asinpi 1.1754943508222875e-38 -> 3.7417147301993126e-39 +asinpi10084 asinpi 2.2250738585072014e-308 -> 7.082630066519554e-309 +asinpi10085 asinpi 2.004168360008973e-292 -> 6.379466025676106e-293 +asinpi10086 asinpi -1.1754943508222875e-38 -> -3.7417147301993126e-39 +asinpi10087 asinpi -2.2250738585072014e-308 -> -7.082630066519554e-309 +asinpi10088 asinpi -2.004168360008973e-292 -> -6.379466025676106e-293 +asinpi10089 asinpi 1.401298464324817e-45 -> 4.460471546887532e-46 +asinpi10090 asinpi 5e-324 -> 0.0 +asinpi10091 asinpi -1.401298464324817e-45 -> -4.460471546887532e-46 +asinpi10092 asinpi -5e-324 -> -0.0 +asinpi10093 asinpi 0.972168505191803 -> 0.4247258559057922 +asinpi10094 asinpi -0.19141583144664764 -> -0.06130790022106249 +asinpi10095 asinpi -0.19141584634780884 -> -0.06130790505360787 +asinpi10096 asinpi -0.19141583181687138 -> -0.06130790034112851 +asinpi10097 asinpi 0.505088746547699 -> 0.16854024296386302 +asinpi10098 asinpi 0.5050886869430542 -> 0.16854022098093674 +asinpi10099 asinpi 0.5050886987499023 -> 0.1685402253354475 +asinpi10100 asinpi 0.5050886987499021 -> 0.16854022533544744 +asinpi10101 asinpi -0.6554324626922607 -> -0.2275136094679927 +asinpi10102 asinpi -0.6554325222969055 -> -0.2275136345890164 +asinpi10103 asinpi -0.6554324978014275 -> -0.22751362426513144 +asinpi10104 asinpi -0.6554324978014276 -> -0.2275136242651315 + +asinpi20001 asinpi inf -> nan invalid +asinpi20002 asinpi -inf -> nan invalid +asinpi20003 asinpi nan -> nan + +------------ +-- atanpi -- +------------ + +atanpi10000 atanpi 0.0 -> 0.0 +atanpi10001 atanpi -0.0 -> -0.0 +atanpi10002 atanpi 3.4028234663852886e+38 -> 0.5 +atanpi10003 atanpi 1.7976931348623157e+308 -> 0.5 +atanpi10004 atanpi -3.4028234663852886e+38 -> -0.5 +atanpi10005 atanpi -1.7976931348623157e+308 -> -0.5 +atanpi10006 atanpi 1.0 -> 0.25 +atanpi10007 atanpi -1.0 -> -0.25 +atanpi10008 atanpi 0.75 -> 0.20483276469913345 +atanpi10009 atanpi 0.03125 -> 0.009943947823589275 +atanpi10010 atanpi 0.0009765625 -> 0.0003108493994100204 +atanpi10011 atanpi 3.0517578125e-05 -> 9.714046816558053e-06 +atanpi10012 atanpi 9.5367431640625e-07 -> 3.0356396311158577e-07 +atanpi10013 atanpi 2.9802322387695312e-08 -> 9.486373847239929e-09 +atanpi10014 atanpi 9.313225746154785e-10 -> 2.964491827262479e-10 +atanpi10015 atanpi 2.9103830456733704e-11 -> 9.264036960195246e-12 +atanpi10016 atanpi 9.094947017729282e-13 -> 2.8950115500610145e-13 +atanpi10017 atanpi 2.842170943040401e-14 -> 9.04691109394067e-15 +atanpi10018 atanpi 8.881784197001252e-16 -> 2.8271597168564594e-16 +atanpi10019 atanpi 2.7755575615628914e-17 -> 8.834874115176436e-18 +atanpi10020 atanpi 8.673617379884035e-19 -> 2.760898160992636e-19 +atanpi10021 atanpi 2.5 -> 0.3788810584091566 +atanpi10022 atanpi 10.0 -> 0.4682744825694464 +atanpi10023 atanpi 1000000.0 -> 0.49999968169011383 +atanpi10024 atanpi 2147483648.0 -> 0.4999999998517754 +atanpi10025 atanpi 7.888609052210118e-31 -> 2.5110222495574236e-31 +atanpi10026 atanpi 1.401298464324817e-45 -> 4.460471546887532e-46 +atanpi10027 atanpi 0.0 -> 0.0 +atanpi10028 atanpi 2.409919865102884e-181 -> 7.671013179729553e-182 +atanpi10029 atanpi 1.401298464324817e-45 -> 4.460471546887532e-46 +atanpi10030 atanpi 0.0 -> 0.0 +atanpi10031 atanpi 5e-324 -> 0.0 +atanpi10032 atanpi -0.23051215708255768 -> -0.07211461946620386 +atanpi10033 atanpi -0.20305541157722473 -> -0.06376756760558985 +atanpi10034 atanpi -2.1964359283447266 -> -0.36400572992968316 +atanpi10035 atanpi -1.2119029760360718 -> -0.2804016904047878 +atanpi10036 atanpi -1.6769415140151978 -> -0.32884096476652747 +atanpi10037 atanpi -3.9201369285583496 -> -0.4204968782420327 +atanpi10038 atanpi 3.327171802520752 -> 0.40706420561165213 +atanpi10039 atanpi -1.3468046188354492 -> -0.2967010012978093 +atanpi10040 atanpi 0.692195475101471 -> 0.1927267050671652 +atanpi10041 atanpi 1.1754943508222875e-38 -> 3.7417147301993126e-39 +atanpi10042 atanpi 2.2250738585072014e-308 -> 7.082630066519554e-309 +atanpi10043 atanpi 2.004168360008973e-292 -> 6.379466025676106e-293 +atanpi10044 atanpi -1.1754943508222875e-38 -> -3.7417147301993126e-39 +atanpi10045 atanpi -2.2250738585072014e-308 -> -7.082630066519554e-309 +atanpi10046 atanpi -2.004168360008973e-292 -> -6.379466025676106e-293 +atanpi10047 atanpi 1.401298464324817e-45 -> 4.460471546887532e-46 +atanpi10048 atanpi 5e-324 -> 0.0 +atanpi10049 atanpi -1.401298464324817e-45 -> -4.460471546887532e-46 +atanpi10050 atanpi -5e-324 -> -0.0 +atanpi10051 atanpi 0.721666157245636 -> 0.19898171607590034 +atanpi10052 atanpi 0.19824542105197906 -> 0.06229575837467328 +atanpi10053 atanpi 0.19824540615081787 -> 0.06229575381085028 +atanpi10054 atanpi 0.198245408653517 -> 0.06229575457735939 +atanpi10055 atanpi 0.049094054847955704 -> 0.01561458616872582 +atanpi10056 atanpi 0.049094051122665405 -> 0.015614584985780253 +atanpi10057 atanpi 0.04909405149251647 -> 0.015614585103224438 +atanpi10058 atanpi 0.049094051492516465 -> 0.015614585103224436 +atanpi10059 atanpi 0.19859454035758972 -> 0.062402677059315244 +atanpi10060 atanpi 0.19859452545642853 -> 0.06240267249610054 +atanpi10061 atanpi 0.1985945401764544 -> 0.06240267700384578 +atanpi10062 atanpi 0.19859454017645436 -> 0.06240267700384577 + +atanpi20001 atanpi inf -> 0.5 +atanpi20002 atanpi -inf -> -0.5 +atanpi20003 atanpi nan -> nan + +------------- +-- atan2pi -- +------------- + +atan2pi10000 atan2pi 0.0 1.0 -> 0.0 +atan2pi10001 atan2pi -0.0 1.0 -> -0.0 +atan2pi10002 atan2pi 0.0 0.0 -> 0.0 +atan2pi10003 atan2pi -0.0 0.0 -> -0.0 +atan2pi10004 atan2pi 0.0 -1.0 -> 1.0 +atan2pi10005 atan2pi -0.0 -1.0 -> -1.0 +atan2pi10006 atan2pi 0.0 -0.0 -> 1.0 +atan2pi10007 atan2pi -0.0 -0.0 -> -1.0 +atan2pi10008 atan2pi 1.0 0.0 -> 0.5 +atan2pi10009 atan2pi 1.0 -0.0 -> 0.5 +atan2pi10010 atan2pi -1.0 0.0 -> -0.5 +atan2pi10011 atan2pi -1.0 -0.0 -> -0.5 +atan2pi10012 atan2pi 3.4028234663852886e+38 3.4028234663852886e+38 -> 0.25 +atan2pi10013 atan2pi 3.4028234663852886e+38 1.7976931348623157e+308 -> 6.025234948519683e-271 +atan2pi10014 atan2pi 1.7976931348623157e+308 3.4028234663852886e+38 -> 0.5 +atan2pi10015 atan2pi 1.7976931348623157e+308 1.7976931348623157e+308 -> 0.25 +atan2pi10016 atan2pi 3.4028234663852886e+38 -3.4028234663852886e+38 -> 0.75 +atan2pi10017 atan2pi 3.4028234663852886e+38 -1.7976931348623157e+308 -> 1.0 +atan2pi10018 atan2pi 1.7976931348623157e+308 -3.4028234663852886e+38 -> 0.5 +atan2pi10019 atan2pi 1.7976931348623157e+308 -1.7976931348623157e+308 -> 0.75 +atan2pi10020 atan2pi -3.4028234663852886e+38 3.4028234663852886e+38 -> -0.25 +atan2pi10021 atan2pi -3.4028234663852886e+38 1.7976931348623157e+308 -> -6.025234948519683e-271 +atan2pi10022 atan2pi -1.7976931348623157e+308 3.4028234663852886e+38 -> -0.5 +atan2pi10023 atan2pi -1.7976931348623157e+308 1.7976931348623157e+308 -> -0.25 +atan2pi10024 atan2pi -3.4028234663852886e+38 -3.4028234663852886e+38 -> -0.75 +atan2pi10025 atan2pi -3.4028234663852886e+38 -1.7976931348623157e+308 -> -1.0 +atan2pi10026 atan2pi -1.7976931348623157e+308 -3.4028234663852886e+38 -> -0.5 +atan2pi10027 atan2pi -1.7976931348623157e+308 -1.7976931348623157e+308 -> -0.75 +atan2pi10028 atan2pi 3.4028234663852886e+38 1.1754943508222875e-38 -> 0.5 +atan2pi10029 atan2pi 3.4028234663852886e+38 2.2250738585072014e-308 -> 0.5 +atan2pi10030 atan2pi 3.4028234663852886e+38 2.004168360008973e-292 -> 0.5 +atan2pi10031 atan2pi 1.7976931348623157e+308 1.1754943508222875e-38 -> 0.5 +atan2pi10032 atan2pi 1.7976931348623157e+308 2.2250738585072014e-308 -> 0.5 +atan2pi10033 atan2pi 1.7976931348623157e+308 2.004168360008973e-292 -> 0.5 +atan2pi10034 atan2pi -3.4028234663852886e+38 -1.1754943508222875e-38 -> -0.5 +atan2pi10035 atan2pi -3.4028234663852886e+38 -2.2250738585072014e-308 -> -0.5 +atan2pi10036 atan2pi -3.4028234663852886e+38 -2.004168360008973e-292 -> -0.5 +atan2pi10037 atan2pi -1.7976931348623157e+308 -1.1754943508222875e-38 -> -0.5 +atan2pi10038 atan2pi -1.7976931348623157e+308 -2.2250738585072014e-308 -> -0.5 +atan2pi10039 atan2pi -1.7976931348623157e+308 -2.004168360008973e-292 -> -0.5 +atan2pi10040 atan2pi -3.4028234663852886e+38 1.1754943508222875e-38 -> -0.5 +atan2pi10041 atan2pi -3.4028234663852886e+38 2.2250738585072014e-308 -> -0.5 +atan2pi10042 atan2pi -3.4028234663852886e+38 2.004168360008973e-292 -> -0.5 +atan2pi10043 atan2pi -1.7976931348623157e+308 1.1754943508222875e-38 -> -0.5 +atan2pi10044 atan2pi -1.7976931348623157e+308 2.2250738585072014e-308 -> -0.5 +atan2pi10045 atan2pi -1.7976931348623157e+308 2.004168360008973e-292 -> -0.5 +atan2pi10046 atan2pi 3.4028234663852886e+38 -1.1754943508222875e-38 -> 0.5 +atan2pi10047 atan2pi 3.4028234663852886e+38 -2.2250738585072014e-308 -> 0.5 +atan2pi10048 atan2pi 3.4028234663852886e+38 -2.004168360008973e-292 -> 0.5 +atan2pi10049 atan2pi 1.7976931348623157e+308 -1.1754943508222875e-38 -> 0.5 +atan2pi10050 atan2pi 1.7976931348623157e+308 -2.2250738585072014e-308 -> 0.5 +atan2pi10051 atan2pi 1.7976931348623157e+308 -2.004168360008973e-292 -> 0.5 +atan2pi10052 atan2pi 3.4028234663852886e+38 1.401298464324817e-45 -> 0.5 +atan2pi10053 atan2pi 3.4028234663852886e+38 5e-324 -> 0.5 +atan2pi10054 atan2pi 1.7976931348623157e+308 1.401298464324817e-45 -> 0.5 +atan2pi10055 atan2pi 1.7976931348623157e+308 5e-324 -> 0.5 +atan2pi10056 atan2pi -3.4028234663852886e+38 -1.401298464324817e-45 -> -0.5 +atan2pi10057 atan2pi -3.4028234663852886e+38 -5e-324 -> -0.5 +atan2pi10058 atan2pi -1.7976931348623157e+308 -1.401298464324817e-45 -> -0.5 +atan2pi10059 atan2pi -1.7976931348623157e+308 -5e-324 -> -0.5 +atan2pi10060 atan2pi -3.4028234663852886e+38 1.401298464324817e-45 -> -0.5 +atan2pi10061 atan2pi -3.4028234663852886e+38 5e-324 -> -0.5 +atan2pi10062 atan2pi -1.7976931348623157e+308 1.401298464324817e-45 -> -0.5 +atan2pi10063 atan2pi -1.7976931348623157e+308 5e-324 -> -0.5 +atan2pi10064 atan2pi 3.4028234663852886e+38 -1.401298464324817e-45 -> 0.5 +atan2pi10065 atan2pi 3.4028234663852886e+38 -5e-324 -> 0.5 +atan2pi10066 atan2pi 1.7976931348623157e+308 -1.401298464324817e-45 -> 0.5 +atan2pi10067 atan2pi 1.7976931348623157e+308 -5e-324 -> 0.5 +atan2pi10068 atan2pi 0.75 1.0 -> 0.20483276469913345 +atan2pi10069 atan2pi -0.75 1.0 -> -0.20483276469913345 +atan2pi10070 atan2pi 0.75 -1.0 -> 0.7951672353008665 +atan2pi10071 atan2pi -0.75 -1.0 -> -0.7951672353008665 +atan2pi10072 atan2pi 0.390625 0.00029000002541579306 -> 0.4997636867632018 +atan2pi10073 atan2pi 0.390625 0.0002899999963119626 -> 0.49976368678691774 +atan2pi10074 atan2pi 0.390625 0.00029 -> 0.4997636867839124 +atan2pi10075 atan2pi 0.390625 0.00028999999999999995 -> 0.4997636867839124 +atan2pi10076 atan2pi 1.390625 0.9296875 -> 0.3124206399141909 +atan2pi10077 atan2pi -0.007568269968032837 -0.0017927357694134116 -> -0.5740351606831543 +atan2pi10078 atan2pi -0.007568269968032837 -0.0017927358858287334 -> -0.574035165319273 +atan2pi10079 atan2pi -0.007568269968032837 -0.001792735857538728 -> -0.5740351641926531 +atan2pi10080 atan2pi -0.007568269968032837 -0.0017927358575387281 -> -0.5740351641926531 +atan2pi10081 atan2pi -0.007568270433694124 -0.0017927357694134116 -> -0.5740351562904277 +atan2pi10082 atan2pi -0.007568270433694124 -0.0017927358858287334 -> -0.574035160926546 +atan2pi10083 atan2pi -0.007568270433694124 -0.001792735857538728 -> -0.5740351597999263 +atan2pi10084 atan2pi -0.007568270433694124 -0.0017927358575387281 -> -0.5740351597999263 +atan2pi10085 atan2pi -0.0075682704267110625 -0.0017927357694134116 -> -0.574035156356301 +atan2pi10086 atan2pi -0.0075682704267110625 -0.0017927358858287334 -> -0.5740351609924195 +atan2pi10087 atan2pi -0.0075682704267110625 -0.001792735857538728 -> -0.5740351598657996 +atan2pi10088 atan2pi -0.0075682704267110625 -0.0017927358575387281 -> -0.5740351598657996 +atan2pi10089 atan2pi -0.007568270426711063 -0.0017927357694134116 -> -0.574035156356301 +atan2pi10090 atan2pi -0.007568270426711063 -0.0017927358858287334 -> -0.5740351609924195 +atan2pi10091 atan2pi -0.007568270426711063 -0.001792735857538728 -> -0.5740351598657996 +atan2pi10092 atan2pi -0.007568270426711063 -0.0017927358575387281 -> -0.5740351598657996 +atan2pi10093 atan2pi 1.0000001192092896 1.0000001192092896 -> 0.25 +atan2pi10094 atan2pi 1.0000001192092896 1.0 -> 0.25000001897274654 +atan2pi10095 atan2pi 1.0000001192092896 1.0000000000000002 -> 0.25000001897274654 +atan2pi10096 atan2pi 1.0 1.0000001192092896 -> 0.24999998102725343 +atan2pi10097 atan2pi 1.0 1.0 -> 0.25 +atan2pi10098 atan2pi 1.0 1.0000000000000002 -> 0.24999999999999997 +atan2pi10099 atan2pi 1.0000000000000002 1.0000001192092896 -> 0.24999998102725346 +atan2pi10100 atan2pi 1.0000000000000002 1.0 -> 0.25000000000000006 +atan2pi10101 atan2pi 1.0000000000000002 1.0000000000000002 -> 0.25 +atan2pi10102 atan2pi 0.2977333664894104 2.9490208625793457 -> 0.032028064084188616 +atan2pi10103 atan2pi -1.346861771417407e-11 1.9269377760089412e-10 -> -0.022212615773618685 +atan2pi10104 atan2pi 0.3338441550731659 2.132420539855957 -> 0.04943220711796703 +atan2pi10105 atan2pi -0.2600718140602112 1.9549157619476318 -> -0.04209909110779262 +atan2pi10106 atan2pi -11223297.0 -0.9756588935852051 -> -0.50000002767118 +atan2pi10107 atan2pi 0.2590596377849579 7.252630710601807 -> 0.011365007240777592 +atan2pi10108 atan2pi 3.7716573755879195e+20 3.019230078687783e+21 -> 0.03955871588967838 +atan2pi10109 atan2pi -0.41808438301086426 3.5622718334198 -> -0.03718816634303022 +atan2pi10110 atan2pi -0.0 3.4028234663852886e+38 -> -0.0 +atan2pi10111 atan2pi -1.401298464324817e-45 3.4028234663852886e+38 -> -1.3108148544731147e-84 +atan2pi10112 atan2pi -8.028930810726831e-218 3.4028234663852886e+38 -> -7.510492618222167e-257 +atan2pi10113 atan2pi 3.943100929260254 -1.3295949697494507 -> 0.6035214600176284 +atan2pi10114 atan2pi -9.648418597407726e-08 1.6104578133839705e-15 -> -0.49999999468695683 +atan2pi10115 atan2pi -1112287276433408.0 4415830896934912.0 -> -0.07854398572055855 +atan2pi10116 atan2pi 9.642213451703619e-17 9.633302400400708e-17 -> 0.2501471543249841 +atan2pi10117 atan2pi 9.642213451703619e-17 9.633301738656218e-17 -> 0.25014716525787717 +atan2pi10118 atan2pi 9.642213451703619e-17 9.633302010283342e-17 -> 0.2501471607702382 +atan2pi10119 atan2pi 9.642212789959129e-17 9.633302400400708e-17 -> 0.25014714340219485 +atan2pi10120 atan2pi 9.642212789959129e-17 9.633301738656218e-17 -> 0.25014715433508794 +atan2pi10121 atan2pi 9.642212789959129e-17 9.633302010283342e-17 -> 0.25014714984744896 +atan2pi10122 atan2pi 9.642213402663379e-17 9.633302400400708e-17 -> 0.25014715351552336 +atan2pi10123 atan2pi 9.642213402663379e-17 9.633301738656218e-17 -> 0.25014716444841645 +atan2pi10124 atan2pi 9.642213402663379e-17 9.633302010283342e-17 -> 0.25014715996077747 +atan2pi10125 atan2pi 1.401298464324817e-45 1.401298464324817e-45 -> 0.25 +atan2pi10126 atan2pi 1.401298464324817e-45 0.0 -> 0.5 +atan2pi10127 atan2pi 1.401298464324817e-45 2.6248243660802892e-105 -> 0.5 +atan2pi10128 atan2pi 0.0 1.401298464324817e-45 -> 0.0 +atan2pi10129 atan2pi 0.0 0.0 -> 0.0 +atan2pi10130 atan2pi 0.0 2.6248243660802892e-105 -> 0.0 +atan2pi10131 atan2pi 2.841564668406762e-105 1.401298464324817e-45 -> 6.454714318268012e-61 +atan2pi10132 atan2pi 2.841564668406762e-105 0.0 -> 0.5 +atan2pi10133 atan2pi 2.841564668406762e-105 2.6248243660802892e-105 -> 0.262614261697575 +atan2pi10134 atan2pi 1.401298464324817e-45 1.401298464324817e-45 -> 0.25 +atan2pi10135 atan2pi 1.401298464324817e-45 0.0 -> 0.5 +atan2pi10136 atan2pi 1.401298464324817e-45 1.6955960445645656e-177 -> 0.5 +atan2pi10137 atan2pi 0.0 1.401298464324817e-45 -> 0.0 +atan2pi10138 atan2pi 0.0 0.0 -> 0.0 +atan2pi10139 atan2pi 0.0 1.6955960445645656e-177 -> 0.0 +atan2pi10140 atan2pi 1.697410900901164e-177 1.401298464324817e-45 -> 3.855728700404359e-133 +atan2pi10141 atan2pi 1.697410900901164e-177 0.0 -> 0.5 +atan2pi10142 atan2pi 1.697410900901164e-177 1.6955960445645656e-177 -> 0.2501702580242645 +atan2pi10143 atan2pi 1.401298464324817e-45 1.401298464324817e-45 -> 0.25 +atan2pi10144 atan2pi 1.401298464324817e-45 0.0 -> 0.5 +atan2pi10145 atan2pi 1.401298464324817e-45 4.642262744535978e-191 -> 0.5 +atan2pi10146 atan2pi 0.0 1.401298464324817e-45 -> 0.0 +atan2pi10147 atan2pi 0.0 0.0 -> 0.0 +atan2pi10148 atan2pi 0.0 4.642262744535978e-191 -> 0.0 +atan2pi10149 atan2pi 4.6493548767531986e-191 1.401298464324817e-45 -> 1.0561173506747795e-146 +atan2pi10150 atan2pi 4.6493548767531986e-191 0.0 -> 0.5 +atan2pi10151 atan2pi 4.6493548767531986e-191 4.642262744535978e-191 -> 0.2502429604243814 +atan2pi10152 atan2pi 1.401298464324817e-45 1.401298464324817e-45 -> 0.25 +atan2pi10153 atan2pi 1.401298464324817e-45 0.0 -> 0.5 +atan2pi10154 atan2pi 1.401298464324817e-45 5e-324 -> 0.5 +atan2pi10155 atan2pi 0.0 1.401298464324817e-45 -> 0.0 +atan2pi10156 atan2pi 0.0 0.0 -> 0.0 +atan2pi10157 atan2pi 0.0 5e-324 -> 0.0 +atan2pi10158 atan2pi 5e-324 1.401298464324817e-45 -> 1.122287531877252e-279 +atan2pi10159 atan2pi 5e-324 0.0 -> 0.5 +atan2pi10160 atan2pi 5e-324 5e-324 -> 0.25 +atan2pi10161 atan2pi 1.1754943508222875e-38 1.1754943508222875e-38 -> 0.25 +atan2pi10162 atan2pi 1.1754943508222875e-38 2.2250738585072014e-308 -> 0.5 +atan2pi10163 atan2pi 1.1754943508222875e-38 2.004168360008973e-292 -> 0.5 +atan2pi10164 atan2pi 2.2250738585072014e-308 1.1754943508222875e-38 -> 6.025235307651693e-271 +atan2pi10165 atan2pi 2.2250738585072014e-308 2.2250738585072014e-308 -> 0.25 +atan2pi10166 atan2pi 2.2250738585072014e-308 2.004168360008973e-292 -> 3.533949646070574e-17 +atan2pi10167 atan2pi 2.004168360008973e-292 1.1754943508222875e-38 -> 5.427049497271944e-255 +atan2pi10168 atan2pi 2.004168360008973e-292 2.2250738585072014e-308 -> 0.49999999999999994 +atan2pi10169 atan2pi 2.004168360008973e-292 2.004168360008973e-292 -> 0.25 +atan2pi10170 atan2pi 1.1754943508222875e-38 -1.1754943508222875e-38 -> 0.75 +atan2pi10171 atan2pi 1.1754943508222875e-38 -2.2250738585072014e-308 -> 0.5 +atan2pi10172 atan2pi 1.1754943508222875e-38 -2.004168360008973e-292 -> 0.5 +atan2pi10173 atan2pi 2.2250738585072014e-308 -1.1754943508222875e-38 -> 1.0 +atan2pi10174 atan2pi 2.2250738585072014e-308 -2.2250738585072014e-308 -> 0.75 +atan2pi10175 atan2pi 2.2250738585072014e-308 -2.004168360008973e-292 -> 1.0 +atan2pi10176 atan2pi 2.004168360008973e-292 -1.1754943508222875e-38 -> 1.0 +atan2pi10177 atan2pi 2.004168360008973e-292 -2.2250738585072014e-308 -> 0.5 +atan2pi10178 atan2pi 2.004168360008973e-292 -2.004168360008973e-292 -> 0.75 +atan2pi10179 atan2pi -1.1754943508222875e-38 1.1754943508222875e-38 -> -0.25 +atan2pi10180 atan2pi -1.1754943508222875e-38 2.2250738585072014e-308 -> -0.5 +atan2pi10181 atan2pi -1.1754943508222875e-38 2.004168360008973e-292 -> -0.5 +atan2pi10182 atan2pi -2.2250738585072014e-308 1.1754943508222875e-38 -> -6.025235307651693e-271 +atan2pi10183 atan2pi -2.2250738585072014e-308 2.2250738585072014e-308 -> -0.25 +atan2pi10184 atan2pi -2.2250738585072014e-308 2.004168360008973e-292 -> -3.533949646070574e-17 +atan2pi10185 atan2pi -2.004168360008973e-292 1.1754943508222875e-38 -> -5.427049497271944e-255 +atan2pi10186 atan2pi -2.004168360008973e-292 2.2250738585072014e-308 -> -0.49999999999999994 +atan2pi10187 atan2pi -2.004168360008973e-292 2.004168360008973e-292 -> -0.25 +atan2pi10188 atan2pi -1.1754943508222875e-38 -1.1754943508222875e-38 -> -0.75 +atan2pi10189 atan2pi -1.1754943508222875e-38 -2.2250738585072014e-308 -> -0.5 +atan2pi10190 atan2pi -1.1754943508222875e-38 -2.004168360008973e-292 -> -0.5 +atan2pi10191 atan2pi -2.2250738585072014e-308 -1.1754943508222875e-38 -> -1.0 +atan2pi10192 atan2pi -2.2250738585072014e-308 -2.2250738585072014e-308 -> -0.75 +atan2pi10193 atan2pi -2.2250738585072014e-308 -2.004168360008973e-292 -> -1.0 +atan2pi10194 atan2pi -2.004168360008973e-292 -1.1754943508222875e-38 -> -1.0 +atan2pi10195 atan2pi -2.004168360008973e-292 -2.2250738585072014e-308 -> -0.5 +atan2pi10196 atan2pi -2.004168360008973e-292 -2.004168360008973e-292 -> -0.75 +atan2pi10197 atan2pi 1.401298464324817e-45 1.401298464324817e-45 -> 0.25 +atan2pi10198 atan2pi 1.401298464324817e-45 5e-324 -> 0.5 +atan2pi10199 atan2pi 5e-324 1.401298464324817e-45 -> 1.122287531877252e-279 +atan2pi10200 atan2pi 5e-324 5e-324 -> 0.25 +atan2pi10201 atan2pi 1.401298464324817e-45 -1.401298464324817e-45 -> 0.75 +atan2pi10202 atan2pi 1.401298464324817e-45 -5e-324 -> 0.5 +atan2pi10203 atan2pi 5e-324 -1.401298464324817e-45 -> 1.0 +atan2pi10204 atan2pi 5e-324 -5e-324 -> 0.75 +atan2pi10205 atan2pi -1.401298464324817e-45 1.401298464324817e-45 -> -0.25 +atan2pi10206 atan2pi -1.401298464324817e-45 5e-324 -> -0.5 +atan2pi10207 atan2pi -5e-324 1.401298464324817e-45 -> -1.122287531877252e-279 +atan2pi10208 atan2pi -5e-324 5e-324 -> -0.25 +atan2pi10209 atan2pi -1.401298464324817e-45 -1.401298464324817e-45 -> -0.75 +atan2pi10210 atan2pi -1.401298464324817e-45 -5e-324 -> -0.5 +atan2pi10211 atan2pi -5e-324 -1.401298464324817e-45 -> -1.0 +atan2pi10212 atan2pi -5e-324 -5e-324 -> -0.75 +atan2pi10213 atan2pi 1.1754943508222875e-38 1.401298464324817e-45 -> 0.4999999620545046 +atan2pi10214 atan2pi 1.1754943508222875e-38 5e-324 -> 0.5 +atan2pi10215 atan2pi 2.2250738585072014e-308 1.401298464324817e-45 -> 5.054333710364945e-264 +atan2pi10216 atan2pi 2.2250738585072014e-308 5e-324 -> 0.49999999999999994 +atan2pi10217 atan2pi 2.004168360008973e-292 1.401298464324817e-45 -> 4.552539082921141e-248 +atan2pi10218 atan2pi 2.004168360008973e-292 5e-324 -> 0.5 +atan2pi10219 atan2pi 1.1754943508222875e-38 -1.401298464324817e-45 -> 0.5000000379454954 +atan2pi10220 atan2pi 1.1754943508222875e-38 -5e-324 -> 0.5 +atan2pi10221 atan2pi 2.2250738585072014e-308 -1.401298464324817e-45 -> 1.0 +atan2pi10222 atan2pi 2.2250738585072014e-308 -5e-324 -> 0.5000000000000001 +atan2pi10223 atan2pi 2.004168360008973e-292 -1.401298464324817e-45 -> 1.0 +atan2pi10224 atan2pi 2.004168360008973e-292 -5e-324 -> 0.5 +atan2pi10225 atan2pi -1.1754943508222875e-38 1.401298464324817e-45 -> -0.4999999620545046 +atan2pi10226 atan2pi -1.1754943508222875e-38 5e-324 -> -0.5 +atan2pi10227 atan2pi -2.2250738585072014e-308 1.401298464324817e-45 -> -5.054333710364945e-264 +atan2pi10228 atan2pi -2.2250738585072014e-308 5e-324 -> -0.49999999999999994 +atan2pi10229 atan2pi -2.004168360008973e-292 1.401298464324817e-45 -> -4.552539082921141e-248 +atan2pi10230 atan2pi -2.004168360008973e-292 5e-324 -> -0.5 +atan2pi10231 atan2pi -1.1754943508222875e-38 -1.401298464324817e-45 -> -0.5000000379454954 +atan2pi10232 atan2pi -1.1754943508222875e-38 -5e-324 -> -0.5 +atan2pi10233 atan2pi -2.2250738585072014e-308 -1.401298464324817e-45 -> -1.0 +atan2pi10234 atan2pi -2.2250738585072014e-308 -5e-324 -> -0.5000000000000001 +atan2pi10235 atan2pi -2.004168360008973e-292 -1.401298464324817e-45 -> -1.0 +atan2pi10236 atan2pi -2.004168360008973e-292 -5e-324 -> -0.5 +atan2pi10237 atan2pi 1.401298464324817e-45 1.1754943508222875e-38 -> 3.7945495388959543e-08 +atan2pi10238 atan2pi 1.401298464324817e-45 2.2250738585072014e-308 -> 0.5 +atan2pi10239 atan2pi 1.401298464324817e-45 2.004168360008973e-292 -> 0.5 +atan2pi10240 atan2pi 5e-324 1.1754943508222875e-38 -> 1.3378709934678696e-286 +atan2pi10241 atan2pi 5e-324 2.2250738585072014e-308 -> 7.067899292141149e-17 +atan2pi10242 atan2pi 5e-324 2.004168360008973e-292 -> 7.846944529866949e-33 +atan2pi10243 atan2pi 1.401298464324817e-45 -1.1754943508222875e-38 -> 0.9999999620545046 +atan2pi10244 atan2pi 1.401298464324817e-45 -2.2250738585072014e-308 -> 0.5 +atan2pi10245 atan2pi 1.401298464324817e-45 -2.004168360008973e-292 -> 0.5 +atan2pi10246 atan2pi 5e-324 -1.1754943508222875e-38 -> 1.0 +atan2pi10247 atan2pi 5e-324 -2.2250738585072014e-308 -> 0.9999999999999999 +atan2pi10248 atan2pi 5e-324 -2.004168360008973e-292 -> 1.0 +atan2pi10249 atan2pi -1.401298464324817e-45 1.1754943508222875e-38 -> -3.7945495388959543e-08 +atan2pi10250 atan2pi -1.401298464324817e-45 2.2250738585072014e-308 -> -0.5 +atan2pi10251 atan2pi -1.401298464324817e-45 2.004168360008973e-292 -> -0.5 +atan2pi10252 atan2pi -5e-324 1.1754943508222875e-38 -> -1.3378709934678696e-286 +atan2pi10253 atan2pi -5e-324 2.2250738585072014e-308 -> -7.067899292141149e-17 +atan2pi10254 atan2pi -5e-324 2.004168360008973e-292 -> -7.846944529866949e-33 +atan2pi10255 atan2pi -1.401298464324817e-45 -1.1754943508222875e-38 -> -0.9999999620545046 +atan2pi10256 atan2pi -1.401298464324817e-45 -2.2250738585072014e-308 -> -0.5 +atan2pi10257 atan2pi -1.401298464324817e-45 -2.004168360008973e-292 -> -0.5 +atan2pi10258 atan2pi -5e-324 -1.1754943508222875e-38 -> -1.0 +atan2pi10259 atan2pi -5e-324 -2.2250738585072014e-308 -> -0.9999999999999999 +atan2pi10260 atan2pi -5e-324 -2.004168360008973e-292 -> -1.0 +atan2pi10261 atan2pi 1.0 -3.4028234663852886e+38 -> 1.0 +atan2pi10262 atan2pi 1.0 -1.7976931348623157e+308 -> 1.0 +atan2pi10263 atan2pi -1.0 -3.4028234663852886e+38 -> -1.0 +atan2pi10264 atan2pi -1.0 -1.7976931348623157e+308 -> -1.0 +atan2pi10265 atan2pi 1.1754943508222875e-38 -3.4028234663852886e+38 -> 1.0 +atan2pi10266 atan2pi 1.1754943508222875e-38 -1.7976931348623157e+308 -> 1.0 +atan2pi10267 atan2pi 2.2250738585072014e-308 -3.4028234663852886e+38 -> 1.0 +atan2pi10268 atan2pi 2.2250738585072014e-308 -1.7976931348623157e+308 -> 1.0 +atan2pi10269 atan2pi 2.004168360008973e-292 -3.4028234663852886e+38 -> 1.0 +atan2pi10270 atan2pi 2.004168360008973e-292 -1.7976931348623157e+308 -> 1.0 +atan2pi10271 atan2pi -1.1754943508222875e-38 -3.4028234663852886e+38 -> -1.0 +atan2pi10272 atan2pi -1.1754943508222875e-38 -1.7976931348623157e+308 -> -1.0 +atan2pi10273 atan2pi -2.2250738585072014e-308 -3.4028234663852886e+38 -> -1.0 +atan2pi10274 atan2pi -2.2250738585072014e-308 -1.7976931348623157e+308 -> -1.0 +atan2pi10275 atan2pi -2.004168360008973e-292 -3.4028234663852886e+38 -> -1.0 +atan2pi10276 atan2pi -2.004168360008973e-292 -1.7976931348623157e+308 -> -1.0 +atan2pi10277 atan2pi 1.401298464324817e-45 -3.4028234663852886e+38 -> 1.0 +atan2pi10278 atan2pi 1.401298464324817e-45 -1.7976931348623157e+308 -> 1.0 +atan2pi10279 atan2pi 5e-324 -3.4028234663852886e+38 -> 1.0 +atan2pi10280 atan2pi 5e-324 -1.7976931348623157e+308 -> 1.0 +atan2pi10281 atan2pi -1.401298464324817e-45 -3.4028234663852886e+38 -> -1.0 +atan2pi10282 atan2pi -1.401298464324817e-45 -1.7976931348623157e+308 -> -1.0 +atan2pi10283 atan2pi -5e-324 -3.4028234663852886e+38 -> -1.0 +atan2pi10284 atan2pi -5e-324 -1.7976931348623157e+308 -> -1.0 +atan2pi10285 atan2pi 1.0 3.4028234663852886e+38 -> 9.354287383057258e-40 +atan2pi10286 atan2pi 1.0 1.7976931348623157e+308 -> 1.770657516629887e-309 +atan2pi10287 atan2pi -1.0 3.4028234663852886e+38 -> -9.354287383057258e-40 +atan2pi10288 atan2pi -1.0 1.7976931348623157e+308 -> -1.770657516629887e-309 +atan2pi10289 atan2pi 1.1754943508222875e-38 3.4028234663852886e+38 -> 1.0995911974752006e-77 +atan2pi10290 atan2pi 1.1754943508222875e-38 1.7976931348623157e+308 -> 0.0 +atan2pi10291 atan2pi 2.2250738585072014e-308 3.4028234663852886e+38 -> 0.0 +atan2pi10292 atan2pi 2.2250738585072014e-308 1.7976931348623157e+308 -> 0.0 +atan2pi10293 atan2pi 2.004168360008973e-292 3.4028234663852886e+38 -> 0.0 +atan2pi10294 atan2pi 2.004168360008973e-292 1.7976931348623157e+308 -> 0.0 +atan2pi10295 atan2pi -1.1754943508222875e-38 3.4028234663852886e+38 -> -1.0995911974752006e-77 +atan2pi10296 atan2pi -1.1754943508222875e-38 1.7976931348623157e+308 -> -0.0 +atan2pi10297 atan2pi -2.2250738585072014e-308 3.4028234663852886e+38 -> -0.0 +atan2pi10298 atan2pi -2.2250738585072014e-308 1.7976931348623157e+308 -> -0.0 +atan2pi10299 atan2pi -2.004168360008973e-292 3.4028234663852886e+38 -> -0.0 +atan2pi10300 atan2pi -2.004168360008973e-292 1.7976931348623157e+308 -> -0.0 +atan2pi10301 atan2pi 1.401298464324817e-45 3.4028234663852886e+38 -> 1.3108148544731147e-84 +atan2pi10302 atan2pi 1.401298464324817e-45 1.7976931348623157e+308 -> 0.0 +atan2pi10303 atan2pi 5e-324 3.4028234663852886e+38 -> 0.0 +atan2pi10304 atan2pi 5e-324 1.7976931348623157e+308 -> 0.0 +atan2pi10305 atan2pi -1.401298464324817e-45 3.4028234663852886e+38 -> -1.3108148544731147e-84 +atan2pi10306 atan2pi -1.401298464324817e-45 1.7976931348623157e+308 -> -0.0 +atan2pi10307 atan2pi -5e-324 3.4028234663852886e+38 -> -0.0 +atan2pi10308 atan2pi -5e-324 1.7976931348623157e+308 -> -0.0 +atan2pi10309 atan2pi 1.1754943508222875e-38 1.0 -> 3.7417147301993126e-39 +atan2pi10310 atan2pi 2.2250738585072014e-308 1.0 -> 7.082630066519554e-309 +atan2pi10311 atan2pi 2.004168360008973e-292 1.0 -> 6.379466025676106e-293 +atan2pi10312 atan2pi -1.1754943508222875e-38 1.0 -> -3.7417147301993126e-39 +atan2pi10313 atan2pi -2.2250738585072014e-308 1.0 -> -7.082630066519554e-309 +atan2pi10314 atan2pi -2.004168360008973e-292 1.0 -> -6.379466025676106e-293 +atan2pi10315 atan2pi 1.401298464324817e-45 1.0 -> 4.460471546887532e-46 +atan2pi10316 atan2pi 5e-324 1.0 -> 0.0 +atan2pi10317 atan2pi -1.401298464324817e-45 1.0 -> -4.460471546887532e-46 +atan2pi10318 atan2pi -5e-324 1.0 -> -0.0 +atan2pi10319 atan2pi 1.1754943508222875e-38 -1.0 -> 1.0 +atan2pi10320 atan2pi 2.2250738585072014e-308 -1.0 -> 1.0 +atan2pi10321 atan2pi 2.004168360008973e-292 -1.0 -> 1.0 +atan2pi10322 atan2pi -1.1754943508222875e-38 -1.0 -> -1.0 +atan2pi10323 atan2pi -2.2250738585072014e-308 -1.0 -> -1.0 +atan2pi10324 atan2pi -2.004168360008973e-292 -1.0 -> -1.0 +atan2pi10325 atan2pi 1.401298464324817e-45 -1.0 -> 1.0 +atan2pi10326 atan2pi 5e-324 -1.0 -> 1.0 +atan2pi10327 atan2pi -1.401298464324817e-45 -1.0 -> -1.0 +atan2pi10328 atan2pi -5e-324 -1.0 -> -1.0 +atan2pi10329 atan2pi -4.3609299609670416e-05 0.05748599022626877 -> -0.0002414722000586593 +atan2pi10330 atan2pi -3.4028234663852886e+38 3.4028234663852886e+38 -> -0.25 +atan2pi10331 atan2pi -3.4028234663852886e+38 2.8061581043948965e+103 -> -3.8599120576714543e-66 +atan2pi10332 atan2pi -9.822050856552661e+114 3.4028234663852886e+38 -> -0.5 +atan2pi10333 atan2pi -9.822050856552661e+114 2.8061581043948965e+103 -> -0.4999999999990906 +atan2pi10334 atan2pi 3.4028234663852886e+38 3.4028234663852886e+38 -> 0.25 +atan2pi10335 atan2pi 3.4028234663852886e+38 1.7976931348623157e+308 -> 6.025234948519683e-271 +atan2pi10336 atan2pi 2.962077767429005e+64 3.4028234663852886e+38 -> 0.5 +atan2pi10337 atan2pi 2.962077767429005e+64 1.7976931348623157e+308 -> 5.244825263740446e-245 +atan2pi10338 atan2pi 2.9620777674290047e+64 3.4028234663852886e+38 -> 0.5 +atan2pi10339 atan2pi 2.9620777674290047e+64 1.7976931348623157e+308 -> 5.244825263740445e-245 +atan2pi10340 atan2pi -3.4028234663852886e+38 3.4028234663852886e+38 -> -0.25 +atan2pi10341 atan2pi -3.4028234663852886e+38 1.7976931348623157e+308 -> -6.025234948519683e-271 +atan2pi10342 atan2pi -1.7976931348623157e+308 3.4028234663852886e+38 -> -0.5 +atan2pi10343 atan2pi -1.7976931348623157e+308 1.7976931348623157e+308 -> -0.25 + +atan2pi20000 atan2pi inf 0 -> 0.5 +atan2pi20001 atan2pi -inf 0 -> -0.5 +atan2pi20002 atan2pi nan 0 -> nan + +atan2pi20000 atan2pi inf -0 -> 0.5 +atan2pi20001 atan2pi -inf -0 -> -0.5 +atan2pi20002 atan2pi nan -0 -> nan + +atan2pi20003 atan2pi inf 1 -> 0.5 +atan2pi20004 atan2pi -inf 1 -> -0.5 +atan2pi20005 atan2pi nan 1 -> nan + +atan2pi20006 atan2pi inf -1 -> 0.5 +atan2pi20007 atan2pi -inf -1 -> -0.5 +atan2pi20008 atan2pi nan -1 -> nan + +atan2pi20009 atan2pi inf inf -> 0.25 +atan2pi20010 atan2pi -inf inf -> -0.25 +atan2pi20011 atan2pi nan inf -> nan + +atan2pi20012 atan2pi inf -inf -> .75 +atan2pi20013 atan2pi -inf -inf -> -.75 +atan2pi20014 atan2pi nan -inf -> nan + +atan2pi20015 atan2pi inf nan -> nan +atan2pi20016 atan2pi -inf nan -> nan +atan2pi20017 atan2pi nan nan -> nan + +atan2pi30000 atan2pi 0 inf -> 0.0 +atan2pi30001 atan2pi 0 -inf -> 1.0 +atan2pi30002 atan2pi 0 nan -> nan + +atan2pi30000 atan2pi -0 inf -> -0.0 +atan2pi30001 atan2pi -0 -inf -> -1.0 +atan2pi30002 atan2pi -0 nan -> nan + +atan2pi30003 atan2pi 1 inf -> 0.0 +atan2pi30004 atan2pi 1 -inf -> 1.0 +atan2pi30005 atan2pi 1 nan -> nan + +atan2pi30006 atan2pi -1 inf -> -0.0 +atan2pi30007 atan2pi -1 -inf -> -1.0 +atan2pi30008 atan2pi -1 nan -> nan + +----------- +-- cospi -- +----------- + +cospi10000 cospi 0.0 -> 1.0 +cospi10001 cospi -0.0 -> 1.0 +cospi10002 cospi 1.1754943508222875e-38 -> 1.0 +cospi10003 cospi 2.2250738585072014e-308 -> 1.0 +cospi10004 cospi 2.004168360008973e-292 -> 1.0 +cospi10005 cospi -1.1754943508222875e-38 -> 1.0 +cospi10006 cospi -2.2250738585072014e-308 -> 1.0 +cospi10007 cospi -2.004168360008973e-292 -> 1.0 +cospi10008 cospi 1.401298464324817e-45 -> 1.0 +cospi10009 cospi 5e-324 -> 1.0 +cospi10010 cospi -1.401298464324817e-45 -> 1.0 +cospi10011 cospi -5e-324 -> 1.0 +cospi10012 cospi 3.4028234663852886e+38 -> 1.0 +cospi10013 cospi 1.7976931348623157e+308 -> 1.0 +cospi10014 cospi -3.4028234663852886e+38 -> 1.0 +cospi10015 cospi -1.7976931348623157e+308 -> 1.0 +cospi10016 cospi 1.0 -> -1.0 +cospi10017 cospi -1.0 -> -1.0 +cospi10018 cospi 2.0 -> 1.0 +cospi10019 cospi -2.0 -> 1.0 +cospi10020 cospi 3.0 -> -1.0 +cospi10021 cospi -3.0 -> -1.0 +cospi10022 cospi 0.10000000149011612 -> 0.9510565148485406 +cospi10023 cospi 0.09999999403953552 -> 0.9510565220816052 +cospi10024 cospi 0.1 -> 0.9510565162951535 +cospi10025 cospi 0.09999999999999999 -> 0.9510565162951535 +cospi10026 cospi -0.09999999403953552 -> 0.9510565220816052 +cospi10027 cospi -0.10000000149011612 -> 0.9510565148485406 +cospi10028 cospi -0.09999999999999999 -> 0.9510565162951535 +cospi10029 cospi -0.1 -> 0.9510565162951535 +cospi10030 cospi 0.20000000298023224 -> 0.8090169888717047 +cospi10031 cospi 0.19999998807907104 -> 0.8090170163879177 +cospi10032 cospi 0.2 -> 0.8090169943749475 +cospi10033 cospi 0.19999999999999998 -> 0.8090169943749475 +cospi10034 cospi -0.19999998807907104 -> 0.8090170163879177 +cospi10035 cospi -0.20000000298023224 -> 0.8090169888717047 +cospi10036 cospi -0.19999999999999998 -> 0.8090169943749475 +cospi10037 cospi -0.2 -> 0.8090169943749475 +cospi10038 cospi 0.30000001192092896 -> 0.5877852219942177 +cospi10039 cospi 0.29999998211860657 -> 0.5877852977398548 +cospi10040 cospi 0.30000000000000004 -> 0.587785252292473 +cospi10041 cospi 0.3 -> 0.5877852522924731 +cospi10042 cospi -0.29999998211860657 -> 0.5877852977398548 +cospi10043 cospi -0.30000001192092896 -> 0.5877852219942177 +cospi10044 cospi -0.3 -> 0.5877852522924731 +cospi10045 cospi -0.30000000000000004 -> 0.587785252292473 +cospi10046 cospi 0.4000000059604645 -> 0.3090169765660799 +cospi10047 cospi 0.3999999761581421 -> 0.3090170656104165 +cospi10048 cospi 0.4 -> 0.30901699437494734 +cospi10049 cospi 0.39999999999999997 -> 0.3090169943749475 +cospi10050 cospi -0.3999999761581421 -> 0.3090170656104165 +cospi10051 cospi -0.4000000059604645 -> 0.3090169765660799 +cospi10052 cospi -0.39999999999999997 -> 0.3090169943749475 +cospi10053 cospi -0.4 -> 0.30901699437494734 +cospi10054 cospi 0.5 -> 0.0 +cospi10055 cospi -0.5 -> 0.0 +cospi10056 cospi 0.6000000238418579 -> -0.3090170656104165 +cospi10057 cospi 0.5999999642372131 -> -0.30901688752174056 +cospi10058 cospi 0.6000000000000001 -> -0.3090169943749477 +cospi10059 cospi 0.6 -> -0.30901699437494734 +cospi10060 cospi -0.5999999642372131 -> -0.30901688752174056 +cospi10061 cospi -0.6000000238418579 -> -0.3090170656104165 +cospi10062 cospi -0.6 -> -0.30901699437494734 +cospi10063 cospi -0.6000000000000001 -> -0.3090169943749477 +cospi10064 cospi 0.7000000476837158 -> -0.5877853734854867 +cospi10065 cospi 0.699999988079071 -> -0.5877852219942177 +cospi10066 cospi 0.7000000000000001 -> -0.5877852522924732 +cospi10067 cospi 0.7 -> -0.587785252292473 +cospi10068 cospi -0.699999988079071 -> -0.5877852219942177 +cospi10069 cospi -0.7000000476837158 -> -0.5877853734854867 +cospi10070 cospi -0.7 -> -0.587785252292473 +cospi10071 cospi -0.7000000000000001 -> -0.5877852522924732 +cospi10072 cospi 0.800000011920929 -> -0.8090170163879177 +cospi10073 cospi 0.7999999523162842 -> -0.8090169063230551 +cospi10074 cospi 0.8 -> -0.8090169943749475 +cospi10075 cospi 0.7999999999999999 -> -0.8090169943749473 +cospi10076 cospi -0.7999999523162842 -> -0.8090169063230551 +cospi10077 cospi -0.800000011920929 -> -0.8090170163879177 +cospi10078 cospi -0.7999999999999999 -> -0.8090169943749473 +cospi10079 cospi -0.8 -> -0.8090169943749475 +cospi10080 cospi 0.9000000357627869 -> -0.9510565510138584 +cospi10081 cospi 0.8999999761581421 -> -0.9510564931493437 +cospi10082 cospi 0.9 -> -0.9510565162951536 +cospi10083 cospi 0.8999999999999999 -> -0.9510565162951535 +cospi10084 cospi -0.8999999761581421 -> -0.9510564931493437 +cospi10085 cospi -0.9000000357627869 -> -0.9510565510138584 +cospi10086 cospi -0.8999999999999999 -> -0.9510565162951535 +cospi10087 cospi -0.9 -> -0.9510565162951536 +cospi10088 cospi 1.100000023841858 -> -0.9510564931493437 +cospi10089 cospi 1.0999999046325684 -> -0.9510566088783399 +cospi10090 cospi 1.1 -> -0.9510565162951535 +cospi10091 cospi 1.0999999999999999 -> -0.9510565162951538 +cospi10092 cospi -1.0999999046325684 -> -0.9510566088783399 +cospi10093 cospi -1.100000023841858 -> -0.9510564931493437 +cospi10094 cospi -1.0999999999999999 -> -0.9510565162951538 +cospi10095 cospi -1.1 -> -0.9510565162951535 +cospi10096 cospi 1.2000000476837158 -> -0.8090169063230551 +cospi10097 cospi 1.1999999284744263 -> -0.8090171264527518 +cospi10098 cospi 1.2000000000000002 -> -0.8090169943749471 +cospi10099 cospi 1.2 -> -0.8090169943749475 +cospi10100 cospi -1.1999999284744263 -> -0.8090171264527518 +cospi10101 cospi -1.2000000476837158 -> -0.8090169063230551 +cospi10102 cospi -1.2 -> -0.8090169943749475 +cospi10103 cospi -1.2000000000000002 -> -0.8090169943749471 +cospi10104 cospi 1.3000000715255737 -> -0.587785070502928 +cospi10105 cospi 1.2999999523162842 -> -0.5877853734854867 +cospi10106 cospi 1.3 -> -0.587785252292473 +cospi10107 cospi 1.2999999999999998 -> -0.5877852522924736 +cospi10108 cospi -1.2999999523162842 -> -0.5877853734854867 +cospi10109 cospi -1.3000000715255737 -> -0.587785070502928 +cospi10110 cospi -1.2999999999999998 -> -0.5877852522924736 +cospi10111 cospi -1.3 -> -0.587785252292473 +cospi10112 cospi 1.4000000953674316 -> -0.3090167094330538 +cospi10113 cospi 1.399999976158142 -> -0.3090170656104165 +cospi10114 cospi 1.4000000000000001 -> -0.309016994374947 +cospi10115 cospi 1.4 -> -0.3090169943749477 +cospi10116 cospi -1.399999976158142 -> -0.3090170656104165 +cospi10117 cospi -1.4000000953674316 -> -0.3090167094330538 +cospi10118 cospi -1.4 -> -0.3090169943749477 +cospi10119 cospi -1.4000000000000001 -> -0.309016994374947 +cospi10120 cospi 1.5 -> 0.0 +cospi10121 cospi -1.5 -> 0.0 +cospi10122 cospi 2.5 -> 0.0 +cospi10123 cospi -2.5 -> 0.0 +cospi10124 cospi 3.5 -> 0.0 +cospi10125 cospi -3.5 -> 0.0 +cospi10126 cospi 4.5 -> 0.0 +cospi10127 cospi -4.5 -> 0.0 +cospi10128 cospi 0.24000000953674316 -> 0.728968606911995 +cospi10129 cospi 0.23999999463558197 -> 0.728968638957958 +cospi10130 cospi 0.24000000000000002 -> 0.7289686274214114 +cospi10131 cospi 0.24 -> 0.7289686274214116 +cospi10132 cospi -0.23999999463558197 -> 0.728968638957958 +cospi10133 cospi -0.24000000953674316 -> 0.728968606911995 +cospi10134 cospi -0.24 -> 0.7289686274214116 +cospi10135 cospi -0.24000000000000002 -> 0.7289686274214114 +cospi10136 cospi 0.49000000953674316 -> 0.03141072913234975 +cospi10137 cospi 0.4899999797344208 -> 0.03141082271290761 +cospi10138 cospi 0.49000000000000005 -> 0.031410759078128146 +cospi10139 cospi 0.49 -> 0.03141075907812832 +cospi10140 cospi -0.4899999797344208 -> 0.03141082271290761 +cospi10141 cospi -0.49000000953674316 -> 0.03141072913234975 +cospi10142 cospi -0.49 -> 0.03141075907812832 +cospi10143 cospi -0.49000000000000005 -> 0.031410759078128146 +cospi10144 cospi 0.5100000500679016 -> -0.031410916293465214 +cospi10145 cospi 0.5099999904632568 -> -0.03141072913234975 +cospi10146 cospi 0.51 -> -0.03141075907812832 +cospi10147 cospi 0.5099999999999999 -> -0.03141075907812797 +cospi10148 cospi -0.5099999904632568 -> -0.03141072913234975 +cospi10149 cospi -0.5100000500679016 -> -0.031410916293465214 +cospi10150 cospi -0.5099999999999999 -> -0.03141075907812797 +cospi10151 cospi -0.51 -> -0.03141075907812832 +cospi10152 cospi 0.7600000500679016 -> -0.7289687350958375 +cospi10153 cospi 0.7599999904632568 -> -0.728968606911995 +cospi10154 cospi 0.76 -> -0.7289686274214116 +cospi10155 cospi 0.7599999999999999 -> -0.7289686274214113 +cospi10156 cospi -0.7599999904632568 -> -0.728968606911995 +cospi10157 cospi -0.7600000500679016 -> -0.7289687350958375 +cospi10158 cospi -0.7599999999999999 -> -0.7289686274214113 +cospi10159 cospi -0.76 -> -0.7289686274214116 +cospi10160 cospi 1.2400000095367432 -> -0.728968606911995 +cospi10161 cospi 1.2399998903274536 -> -0.7289688632796544 +cospi10162 cospi 1.2400000000000002 -> -0.7289686274214111 +cospi10163 cospi 1.24 -> -0.7289686274214116 +cospi10164 cospi -1.2399998903274536 -> -0.7289688632796544 +cospi10165 cospi -1.2400000095367432 -> -0.728968606911995 +cospi10166 cospi -1.24 -> -0.7289686274214116 +cospi10167 cospi -1.2400000000000002 -> -0.7289686274214111 +cospi10168 cospi 1.4900000095367432 -> -0.03141072913234975 +cospi10169 cospi 1.4899998903274536 -> -0.03141110345457958 +cospi10170 cospi 1.4900000000000002 -> -0.031410759078127626 +cospi10171 cospi 1.49 -> -0.03141075907812832 +cospi10172 cospi -1.4899998903274536 -> -0.03141110345457958 +cospi10173 cospi -1.4900000095367432 -> -0.03141072913234975 +cospi10174 cospi -1.49 -> -0.03141075907812832 +cospi10175 cospi -1.4900000000000002 -> -0.031410759078127626 +cospi10176 cospi 1.5100001096725464 -> 0.03141110345457958 +cospi10177 cospi 1.5099999904632568 -> 0.03141072913234975 +cospi10178 cospi 1.51 -> 0.03141075907812832 +cospi10179 cospi 1.5099999999999998 -> 0.031410759078127626 +cospi10180 cospi -1.5099999904632568 -> 0.03141072913234975 +cospi10181 cospi -1.5100001096725464 -> 0.03141110345457958 +cospi10182 cospi -1.5099999999999998 -> 0.031410759078127626 +cospi10183 cospi -1.51 -> 0.03141075907812832 +cospi10184 cospi 1.7600001096725464 -> 0.7289688632796544 +cospi10185 cospi 1.7599999904632568 -> 0.728968606911995 +cospi10186 cospi 1.76 -> 0.7289686274214116 +cospi10187 cospi 1.7599999999999998 -> 0.7289686274214111 +cospi10188 cospi -1.7599999904632568 -> 0.728968606911995 +cospi10189 cospi -1.7600001096725464 -> 0.7289688632796544 +cospi10190 cospi -1.7599999999999998 -> 0.7289686274214111 +cospi10191 cospi -1.76 -> 0.7289686274214116 +cospi10192 cospi 100.24000549316406 -> 0.7289568138891386 +cospi10193 cospi 100.23999786376953 -> 0.7289732215142216 +cospi10194 cospi 100.24000000000001 -> 0.728968627421392 +cospi10195 cospi 100.24 -> 0.7289686274214225 +cospi10196 cospi -100.23999786376953 -> 0.7289732215142216 +cospi10197 cospi -100.24000549316406 -> 0.7289568138891386 +cospi10198 cospi -100.24 -> 0.7289686274214225 +cospi10199 cospi -100.24000000000001 -> 0.728968627421392 +cospi10200 cospi 100.49000549316406 -> 0.031393510305016 +cospi10201 cospi 100.48999786376953 -> 0.031417466931812604 +cospi10202 cospi 100.49000000000001 -> 0.03141075907809974 +cospi10203 cospi 100.49 -> 0.031410759078144355 +cospi10204 cospi -100.48999786376953 -> 0.031417466931812604 +cospi10205 cospi -100.49000549316406 -> 0.031393510305016 +cospi10206 cospi -100.49 -> 0.031410759078144355 +cospi10207 cospi -100.49000000000001 -> 0.03141075907809974 +cospi10208 cospi 100.51000213623047 -> -0.031417466931812604 +cospi10209 cospi 100.50999450683594 -> -0.031393510305016 +cospi10210 cospi 100.51 -> -0.031410759078144355 +cospi10211 cospi 100.50999999999999 -> -0.03141075907809974 +cospi10212 cospi -100.50999450683594 -> -0.031393510305016 +cospi10213 cospi -100.51000213623047 -> -0.031417466931812604 +cospi10214 cospi -100.50999999999999 -> -0.03141075907809974 +cospi10215 cospi -100.51 -> -0.031410759078144355 +cospi10216 cospi 100.76000213623047 -> -0.7289732215142216 +cospi10217 cospi 100.75999450683594 -> -0.7289568138891386 +cospi10218 cospi 100.76 -> -0.7289686274214225 +cospi10219 cospi 100.75999999999999 -> -0.728968627421392 +cospi10220 cospi -100.75999450683594 -> -0.7289568138891386 +cospi10221 cospi -100.76000213623047 -> -0.7289732215142216 +cospi10222 cospi -100.75999999999999 -> -0.728968627421392 +cospi10223 cospi -100.76 -> -0.7289686274214225 +cospi10224 cospi 1234.56005859375 -> -0.1875621285825296 +cospi10225 cospi 1234.5599365234375 -> -0.18718542559099033 +cospi10226 cospi 1234.5600000000002 -> -0.1873813145862579 +cospi10227 cospi 1234.56 -> -0.18738131458555624 +cospi10228 cospi -1234.5599365234375 -> -0.18718542559099033 +cospi10229 cospi -1234.56005859375 -> -0.1875621285825296 +cospi10230 cospi -1234.56 -> -0.18738131458555624 +cospi10231 cospi -1234.5600000000002 -> -0.1873813145862579 +cospi10232 cospi 2097151.375 -> -0.3826834323650898 +cospi10233 cospi -2097151.375 -> -0.3826834323650898 +cospi10234 cospi 2097151.625 -> 0.3826834323650898 +cospi10235 cospi -2097151.625 -> 0.3826834323650898 +cospi10236 cospi 1125899906842624.0 -> 1.0 +cospi10237 cospi 1125899839733760.0 -> 1.0 +cospi10238 cospi 1125899906842623.4 -> -0.3826834323650898 +cospi10239 cospi -1125899839733760.0 -> 1.0 +cospi10240 cospi -1125899906842624.0 -> 1.0 +cospi10241 cospi -1125899906842623.4 -> -0.3826834323650898 +cospi10242 cospi 1125899906842624.0 -> 1.0 +cospi10243 cospi 1125899839733760.0 -> 1.0 +cospi10244 cospi 1125899906842623.6 -> 0.3826834323650898 +cospi10245 cospi -1125899839733760.0 -> 1.0 +cospi10246 cospi -1125899906842624.0 -> 1.0 +cospi10247 cospi -1125899906842623.6 -> 0.3826834323650898 +cospi10248 cospi 2.305843009213694e+18 -> 1.0 +cospi10249 cospi 2.3058428717747405e+18 -> 1.0 +cospi10250 cospi 2.3058430092136937e+18 -> 1.0 +cospi10251 cospi -2.3058428717747405e+18 -> 1.0 +cospi10252 cospi -2.305843009213694e+18 -> 1.0 +cospi10253 cospi -2.3058430092136937e+18 -> 1.0 +cospi10254 cospi 2.305843009213694e+18 -> 1.0 +cospi10255 cospi 2.3058428717747405e+18 -> 1.0 +cospi10256 cospi 2.3058430092136937e+18 -> 1.0 +cospi10257 cospi -2.3058428717747405e+18 -> 1.0 +cospi10258 cospi -2.305843009213694e+18 -> 1.0 +cospi10259 cospi -2.3058430092136937e+18 -> 1.0 +cospi10260 cospi 1.298074214633707e+33 -> 1.0 +cospi10261 cospi 1.2980741372624545e+33 -> 1.0 +cospi10262 cospi 1.2980742146337068e+33 -> 1.0 +cospi10263 cospi -1.2980741372624545e+33 -> 1.0 +cospi10264 cospi -1.298074214633707e+33 -> 1.0 +cospi10265 cospi -1.2980742146337068e+33 -> 1.0 +cospi10266 cospi 1.298074214633707e+33 -> 1.0 +cospi10267 cospi 1.2980741372624545e+33 -> 1.0 +cospi10268 cospi 1.2980742146337068e+33 -> 1.0 +cospi10269 cospi -1.2980741372624545e+33 -> 1.0 +cospi10270 cospi -1.298074214633707e+33 -> 1.0 +cospi10271 cospi -1.2980742146337068e+33 -> 1.0 +cospi10272 cospi 0.419987291097641 -> 0.24872855880609973 +cospi10273 cospi -70.50994110107422 -> -0.031225813413226215 +cospi10274 cospi -70.50994873046875 -> -0.031249770165979865 +cospi10275 cospi -70.50994733870746 -> -0.03124539995406979 +cospi10276 cospi 0.4801006019115448 -> 0.06247508982524335 +cospi10277 cospi 0.4801005721092224 -> 0.06247518326910248 +cospi10278 cospi 0.48010059600345173 -> 0.062475108349807 +cospi10279 cospi 0.4801005960034517 -> 0.06247510834980717 +cospi10280 cospi -629430784.0 -> 1.0 +cospi10281 cospi -629430848.0 -> 1.0 +cospi10282 cospi -629430785.4800935 -> -0.06249742289199851 +cospi10283 cospi -629430785.4800936 -> -0.06249704911708015 + +cospi20001 cospi inf -> nan invalid +cospi20002 cospi -inf -> nan invalid +cospi20003 cospi nan -> nan + +----------- +-- sinpi -- +----------- + +sinpi10000 sinpi 0.0 -> 0.0 +sinpi10001 sinpi -0.0 -> -0.0 +sinpi10002 sinpi 1.1754943508222875e-38 -> 3.6929244168796014e-38 +sinpi10003 sinpi 2.2250738585072014e-308 -> 6.990275687580919e-308 +sinpi10004 sinpi 2.004168360008973e-292 -> 6.296280596361293e-292 +sinpi10005 sinpi -1.1754943508222875e-38 -> -3.6929244168796014e-38 +sinpi10006 sinpi -2.2250738585072014e-308 -> -6.990275687580919e-308 +sinpi10007 sinpi -2.004168360008973e-292 -> -6.296280596361293e-292 +sinpi10008 sinpi 1.401298464324817e-45 -> 4.402308961009504e-45 +sinpi10009 sinpi 5e-324 -> 1.5e-323 +sinpi10010 sinpi -1.401298464324817e-45 -> -4.402308961009504e-45 +sinpi10011 sinpi -5e-324 -> -1.5e-323 +sinpi10012 sinpi 3.4028234663852886e+38 -> 0.0 +sinpi10013 sinpi 1.7976931348623157e+308 -> 0.0 +sinpi10014 sinpi -3.4028234663852886e+38 -> -0.0 +sinpi10015 sinpi -1.7976931348623157e+308 -> -0.0 +sinpi10016 sinpi 1.0 -> 0.0 +sinpi10017 sinpi -1.0 -> -0.0 +sinpi10018 sinpi 2.0 -> 0.0 +sinpi10019 sinpi -2.0 -> -0.0 +sinpi10020 sinpi 3.0 -> 0.0 +sinpi10021 sinpi -3.0 -> -0.0 +sinpi10022 sinpi 0.10000000149011612 -> 0.30901699882716427 +sinpi10023 sinpi 0.09999999403953552 -> 0.3090169765660799 +sinpi10024 sinpi 0.1 -> 0.30901699437494745 +sinpi10025 sinpi 0.09999999999999999 -> 0.3090169943749474 +sinpi10026 sinpi -0.09999999403953552 -> -0.3090169765660799 +sinpi10027 sinpi -0.10000000149011612 -> -0.30901699882716427 +sinpi10028 sinpi -0.09999999999999999 -> -0.3090169943749474 +sinpi10029 sinpi -0.1 -> -0.30901699437494745 +sinpi10030 sinpi 0.20000000298023224 -> 0.5877852598670369 +sinpi10031 sinpi 0.19999998807907104 -> 0.5877852219942177 +sinpi10032 sinpi 0.2 -> 0.5877852522924731 +sinpi10033 sinpi 0.19999999999999998 -> 0.5877852522924731 +sinpi10034 sinpi -0.19999998807907104 -> -0.5877852219942177 +sinpi10035 sinpi -0.20000000298023224 -> -0.5877852598670369 +sinpi10036 sinpi -0.19999999999999998 -> -0.5877852522924731 +sinpi10037 sinpi -0.2 -> -0.5877852522924731 +sinpi10038 sinpi 0.30000001192092896 -> 0.8090170163879177 +sinpi10039 sinpi 0.29999998211860657 -> 0.80901696135549 +sinpi10040 sinpi 0.30000000000000004 -> 0.8090169943749475 +sinpi10041 sinpi 0.3 -> 0.8090169943749475 +sinpi10042 sinpi -0.29999998211860657 -> -0.80901696135549 +sinpi10043 sinpi -0.30000001192092896 -> -0.8090170163879177 +sinpi10044 sinpi -0.3 -> -0.8090169943749475 +sinpi10045 sinpi -0.30000000000000004 -> -0.8090169943749475 +sinpi10046 sinpi 0.4000000059604645 -> 0.9510565220816052 +sinpi10047 sinpi 0.3999999761581421 -> 0.9510564931493437 +sinpi10048 sinpi 0.4 -> 0.9510565162951536 +sinpi10049 sinpi 0.39999999999999997 -> 0.9510565162951535 +sinpi10050 sinpi -0.3999999761581421 -> -0.9510564931493437 +sinpi10051 sinpi -0.4000000059604645 -> -0.9510565220816052 +sinpi10052 sinpi -0.39999999999999997 -> -0.9510565162951535 +sinpi10053 sinpi -0.4 -> -0.9510565162951536 +sinpi10054 sinpi 0.5 -> 1.0 +sinpi10055 sinpi -0.5 -> -1.0 +sinpi10056 sinpi 0.6000000238418579 -> 0.9510564931493437 +sinpi10057 sinpi 0.5999999642372131 -> 0.9510565510138584 +sinpi10058 sinpi 0.6000000000000001 -> 0.9510565162951535 +sinpi10059 sinpi 0.6 -> 0.9510565162951536 +sinpi10060 sinpi -0.5999999642372131 -> -0.9510565510138584 +sinpi10061 sinpi -0.6000000238418579 -> -0.9510564931493437 +sinpi10062 sinpi -0.6 -> -0.9510565162951536 +sinpi10063 sinpi -0.6000000000000001 -> -0.9510565162951535 +sinpi10064 sinpi 0.7000000476837158 -> 0.8090169063230551 +sinpi10065 sinpi 0.699999988079071 -> 0.8090170163879177 +sinpi10066 sinpi 0.7000000000000001 -> 0.8090169943749473 +sinpi10067 sinpi 0.7 -> 0.8090169943749475 +sinpi10068 sinpi -0.699999988079071 -> -0.8090170163879177 +sinpi10069 sinpi -0.7000000476837158 -> -0.8090169063230551 +sinpi10070 sinpi -0.7 -> -0.8090169943749475 +sinpi10071 sinpi -0.7000000000000001 -> -0.8090169943749473 +sinpi10072 sinpi 0.800000011920929 -> 0.5877852219942177 +sinpi10073 sinpi 0.7999999523162842 -> 0.5877853734854867 +sinpi10074 sinpi 0.8 -> 0.587785252292473 +sinpi10075 sinpi 0.7999999999999999 -> 0.5877852522924732 +sinpi10076 sinpi -0.7999999523162842 -> -0.5877853734854867 +sinpi10077 sinpi -0.800000011920929 -> -0.5877852219942177 +sinpi10078 sinpi -0.7999999999999999 -> -0.5877852522924732 +sinpi10079 sinpi -0.8 -> -0.587785252292473 +sinpi10080 sinpi 0.9000000357627869 -> 0.30901688752174056 +sinpi10081 sinpi 0.8999999761581421 -> 0.3090170656104165 +sinpi10082 sinpi 0.9 -> 0.30901699437494734 +sinpi10083 sinpi 0.8999999999999999 -> 0.3090169943749477 +sinpi10084 sinpi -0.8999999761581421 -> -0.3090170656104165 +sinpi10085 sinpi -0.9000000357627869 -> -0.30901688752174056 +sinpi10086 sinpi -0.8999999999999999 -> -0.3090169943749477 +sinpi10087 sinpi -0.9 -> -0.30901699437494734 +sinpi10088 sinpi 1.100000023841858 -> -0.3090170656104165 +sinpi10089 sinpi 1.0999999046325684 -> -0.3090167094330538 +sinpi10090 sinpi 1.1 -> -0.3090169943749477 +sinpi10091 sinpi 1.0999999999999999 -> -0.309016994374947 +sinpi10092 sinpi -1.0999999046325684 -> 0.3090167094330538 +sinpi10093 sinpi -1.100000023841858 -> 0.3090170656104165 +sinpi10094 sinpi -1.0999999999999999 -> 0.309016994374947 +sinpi10095 sinpi -1.1 -> 0.3090169943749477 +sinpi10096 sinpi 1.2000000476837158 -> -0.5877853734854867 +sinpi10097 sinpi 1.1999999284744263 -> -0.587785070502928 +sinpi10098 sinpi 1.2000000000000002 -> -0.5877852522924736 +sinpi10099 sinpi 1.2 -> -0.587785252292473 +sinpi10100 sinpi -1.1999999284744263 -> 0.587785070502928 +sinpi10101 sinpi -1.2000000476837158 -> 0.5877853734854867 +sinpi10102 sinpi -1.2 -> 0.587785252292473 +sinpi10103 sinpi -1.2000000000000002 -> 0.5877852522924736 +sinpi10104 sinpi 1.3000000715255737 -> -0.8090171264527518 +sinpi10105 sinpi 1.2999999523162842 -> -0.8090169063230551 +sinpi10106 sinpi 1.3 -> -0.8090169943749475 +sinpi10107 sinpi 1.2999999999999998 -> -0.8090169943749471 +sinpi10108 sinpi -1.2999999523162842 -> 0.8090169063230551 +sinpi10109 sinpi -1.3000000715255737 -> 0.8090171264527518 +sinpi10110 sinpi -1.2999999999999998 -> 0.8090169943749471 +sinpi10111 sinpi -1.3 -> 0.8090169943749475 +sinpi10112 sinpi 1.4000000953674316 -> -0.9510566088783399 +sinpi10113 sinpi 1.399999976158142 -> -0.9510564931493437 +sinpi10114 sinpi 1.4000000000000001 -> -0.9510565162951538 +sinpi10115 sinpi 1.4 -> -0.9510565162951535 +sinpi10116 sinpi -1.399999976158142 -> 0.9510564931493437 +sinpi10117 sinpi -1.4000000953674316 -> 0.9510566088783399 +sinpi10118 sinpi -1.4 -> 0.9510565162951535 +sinpi10119 sinpi -1.4000000000000001 -> 0.9510565162951538 +sinpi10120 sinpi 1.5 -> -1.0 +sinpi10121 sinpi -1.5 -> 1.0 +sinpi10122 sinpi 2.5 -> 1.0 +sinpi10123 sinpi -2.5 -> -1.0 +sinpi10124 sinpi 3.5 -> -1.0 +sinpi10125 sinpi -3.5 -> 1.0 +sinpi10126 sinpi 4.5 -> 1.0 +sinpi10127 sinpi -4.5 -> -1.0 +sinpi10128 sinpi 0.24000000953674316 -> 0.6845471277689983 +sinpi10129 sinpi 0.23999999463558197 -> 0.6845470936435142 +sinpi10130 sinpi 0.24000000000000002 -> 0.6845471059286887 +sinpi10131 sinpi 0.24 -> 0.6845471059286886 +sinpi10132 sinpi -0.23999999463558197 -> -0.6845470936435142 +sinpi10133 sinpi -0.24000000953674316 -> -0.6845471277689983 +sinpi10134 sinpi -0.24 -> -0.6845471059286886 +sinpi10135 sinpi -0.24000000000000002 -> -0.6845471059286887 +sinpi10136 sinpi 0.49000000953674316 -> 0.9995065613068151 +sinpi10137 sinpi 0.4899999797344208 -> 0.9995065583659261 +sinpi10138 sinpi 0.49000000000000005 -> 0.9995065603657316 +sinpi10139 sinpi 0.49 -> 0.9995065603657316 +sinpi10140 sinpi -0.4899999797344208 -> -0.9995065583659261 +sinpi10141 sinpi -0.49000000953674316 -> -0.9995065613068151 +sinpi10142 sinpi -0.49 -> -0.9995065603657316 +sinpi10143 sinpi -0.49000000000000005 -> -0.9995065603657316 +sinpi10144 sinpi 0.5100000500679016 -> 0.9995065554250282 +sinpi10145 sinpi 0.5099999904632568 -> 0.9995065613068151 +sinpi10146 sinpi 0.51 -> 0.9995065603657316 +sinpi10147 sinpi 0.5099999999999999 -> 0.9995065603657316 +sinpi10148 sinpi -0.5099999904632568 -> -0.9995065613068151 +sinpi10149 sinpi -0.5100000500679016 -> -0.9995065554250282 +sinpi10150 sinpi -0.5099999999999999 -> -0.9995065603657316 +sinpi10151 sinpi -0.51 -> -0.9995065603657316 +sinpi10152 sinpi 0.7600000500679016 -> 0.684546991267053 +sinpi10153 sinpi 0.7599999904632568 -> 0.6845471277689983 +sinpi10154 sinpi 0.76 -> 0.6845471059286886 +sinpi10155 sinpi 0.7599999999999999 -> 0.684547105928689 +sinpi10156 sinpi -0.7599999904632568 -> -0.6845471277689983 +sinpi10157 sinpi -0.7600000500679016 -> -0.684546991267053 +sinpi10158 sinpi -0.7599999999999999 -> -0.684547105928689 +sinpi10159 sinpi -0.76 -> -0.6845471059286886 +sinpi10160 sinpi 1.2400000095367432 -> -0.6845471277689983 +sinpi10161 sinpi 1.2399998903274536 -> -0.6845468547650836 +sinpi10162 sinpi 1.2400000000000002 -> -0.6845471059286892 +sinpi10163 sinpi 1.24 -> -0.6845471059286886 +sinpi10164 sinpi -1.2399998903274536 -> 0.6845468547650836 +sinpi10165 sinpi -1.2400000095367432 -> 0.6845471277689983 +sinpi10166 sinpi -1.24 -> 0.6845471059286886 +sinpi10167 sinpi -1.2400000000000002 -> 0.6845471059286892 +sinpi10168 sinpi 1.4900000095367432 -> -0.9995065613068151 +sinpi10169 sinpi 1.4899998903274536 -> -0.9995065495432062 +sinpi10170 sinpi 1.4900000000000002 -> -0.9995065603657316 +sinpi10171 sinpi 1.49 -> -0.9995065603657316 +sinpi10172 sinpi -1.4899998903274536 -> 0.9995065495432062 +sinpi10173 sinpi -1.4900000095367432 -> 0.9995065613068151 +sinpi10174 sinpi -1.49 -> 0.9995065603657316 +sinpi10175 sinpi -1.4900000000000002 -> 0.9995065603657316 +sinpi10176 sinpi 1.5100001096725464 -> -0.9995065495432062 +sinpi10177 sinpi 1.5099999904632568 -> -0.9995065613068151 +sinpi10178 sinpi 1.51 -> -0.9995065603657316 +sinpi10179 sinpi 1.5099999999999998 -> -0.9995065603657316 +sinpi10180 sinpi -1.5099999904632568 -> 0.9995065613068151 +sinpi10181 sinpi -1.5100001096725464 -> 0.9995065495432062 +sinpi10182 sinpi -1.5099999999999998 -> 0.9995065603657316 +sinpi10183 sinpi -1.51 -> 0.9995065603657316 +sinpi10184 sinpi 1.7600001096725464 -> -0.6845468547650836 +sinpi10185 sinpi 1.7599999904632568 -> -0.6845471277689983 +sinpi10186 sinpi 1.76 -> -0.6845471059286886 +sinpi10187 sinpi 1.7599999999999998 -> -0.6845471059286892 +sinpi10188 sinpi -1.7599999904632568 -> 0.6845471277689983 +sinpi10189 sinpi -1.7600001096725464 -> 0.6845468547650836 +sinpi10190 sinpi -1.7599999999999998 -> 0.6845471059286892 +sinpi10191 sinpi -1.76 -> 0.6845471059286886 +sinpi10192 sinpi 100.24000549316406 -> 0.6845596858452854 +sinpi10193 sinpi 100.23999786376953 -> 0.684542213683844 +sinpi10194 sinpi 100.24000000000001 -> 0.6845471059287095 +sinpi10195 sinpi 100.24 -> 0.684547105928677 +sinpi10196 sinpi -100.23999786376953 -> -0.684542213683844 +sinpi10197 sinpi -100.24000549316406 -> -0.6845596858452854 +sinpi10198 sinpi -100.24 -> -0.684547105928677 +sinpi10199 sinpi -100.24000000000001 -> -0.6845471059287095 +sinpi10200 sinpi 100.49000549316406 -> 0.9995071022812839 +sinpi10201 sinpi 100.48999786376953 -> 0.9995063495404061 +sinpi10202 sinpi 100.49000000000001 -> 0.9995065603657325 +sinpi10203 sinpi 100.49 -> 0.999506560365731 +sinpi10204 sinpi -100.48999786376953 -> -0.9995063495404061 +sinpi10205 sinpi -100.49000549316406 -> -0.9995071022812839 +sinpi10206 sinpi -100.49 -> -0.999506560365731 +sinpi10207 sinpi -100.49000000000001 -> -0.9995065603657325 +sinpi10208 sinpi 100.51000213623047 -> 0.9995063495404061 +sinpi10209 sinpi 100.50999450683594 -> 0.9995071022812839 +sinpi10210 sinpi 100.51 -> 0.999506560365731 +sinpi10211 sinpi 100.50999999999999 -> 0.9995065603657325 +sinpi10212 sinpi -100.50999450683594 -> -0.9995071022812839 +sinpi10213 sinpi -100.51000213623047 -> -0.9995063495404061 +sinpi10214 sinpi -100.50999999999999 -> -0.9995065603657325 +sinpi10215 sinpi -100.51 -> -0.999506560365731 +sinpi10216 sinpi 100.76000213623047 -> 0.684542213683844 +sinpi10217 sinpi 100.75999450683594 -> 0.6845596858452854 +sinpi10218 sinpi 100.76 -> 0.684547105928677 +sinpi10219 sinpi 100.75999999999999 -> 0.6845471059287095 +sinpi10220 sinpi -100.75999450683594 -> -0.6845596858452854 +sinpi10221 sinpi -100.76000213623047 -> -0.684542213683844 +sinpi10222 sinpi -100.75999999999999 -> -0.6845471059287095 +sinpi10223 sinpi -100.76 -> -0.684547105928677 +sinpi10224 sinpi 1234.56005859375 -> 0.9822527413662894 +sinpi10225 sinpi 1234.5599365234375 -> 0.9823245983107213 +sinpi10226 sinpi 1234.5600000000002 -> 0.9822872507285869 +sinpi10227 sinpi 1234.56 -> 0.9822872507287208 +sinpi10228 sinpi -1234.5599365234375 -> -0.9823245983107213 +sinpi10229 sinpi -1234.56005859375 -> -0.9822527413662894 +sinpi10230 sinpi -1234.56 -> -0.9822872507287208 +sinpi10231 sinpi -1234.5600000000002 -> -0.9822872507285869 +sinpi10232 sinpi 2097151.125 -> -0.3826834323650898 +sinpi10233 sinpi -2097151.125 -> 0.3826834323650898 +sinpi10234 sinpi 2097151.875 -> -0.3826834323650898 +sinpi10235 sinpi -2097151.875 -> 0.3826834323650898 +sinpi10236 sinpi 1125899906842624.0 -> 0.0 +sinpi10237 sinpi 1125899839733760.0 -> 0.0 +sinpi10238 sinpi 1125899906842623.1 -> -0.3826834323650898 +sinpi10239 sinpi -1125899839733760.0 -> -0.0 +sinpi10240 sinpi -1125899906842624.0 -> -0.0 +sinpi10241 sinpi -1125899906842623.1 -> 0.3826834323650898 +sinpi10242 sinpi 1125899906842624.0 -> 0.0 +sinpi10243 sinpi 1125899839733760.0 -> 0.0 +sinpi10244 sinpi 1125899906842623.9 -> -0.3826834323650898 +sinpi10245 sinpi -1125899839733760.0 -> -0.0 +sinpi10246 sinpi -1125899906842624.0 -> -0.0 +sinpi10247 sinpi -1125899906842623.9 -> 0.3826834323650898 +sinpi10248 sinpi 2.305843009213694e+18 -> 0.0 +sinpi10249 sinpi 2.3058428717747405e+18 -> 0.0 +sinpi10250 sinpi 2.3058430092136937e+18 -> 0.0 +sinpi10251 sinpi -2.3058428717747405e+18 -> -0.0 +sinpi10252 sinpi -2.305843009213694e+18 -> -0.0 +sinpi10253 sinpi -2.3058430092136937e+18 -> -0.0 +sinpi10254 sinpi 2.305843009213694e+18 -> 0.0 +sinpi10255 sinpi 2.3058428717747405e+18 -> 0.0 +sinpi10256 sinpi 2.3058430092136937e+18 -> 0.0 +sinpi10257 sinpi -2.3058428717747405e+18 -> -0.0 +sinpi10258 sinpi -2.305843009213694e+18 -> -0.0 +sinpi10259 sinpi -2.3058430092136937e+18 -> -0.0 +sinpi10260 sinpi 1.298074214633707e+33 -> 0.0 +sinpi10261 sinpi 1.2980741372624545e+33 -> 0.0 +sinpi10262 sinpi 1.2980742146337068e+33 -> 0.0 +sinpi10263 sinpi -1.2980741372624545e+33 -> -0.0 +sinpi10264 sinpi -1.298074214633707e+33 -> -0.0 +sinpi10265 sinpi -1.2980742146337068e+33 -> -0.0 +sinpi10266 sinpi 1.298074214633707e+33 -> 0.0 +sinpi10267 sinpi 1.2980741372624545e+33 -> 0.0 +sinpi10268 sinpi 1.2980742146337068e+33 -> 0.0 +sinpi10269 sinpi -1.2980741372624545e+33 -> -0.0 +sinpi10270 sinpi -1.298074214633707e+33 -> -0.0 +sinpi10271 sinpi -1.2980742146337068e+33 -> -0.0 +sinpi10272 sinpi 2.938735877055719e-39 -> 9.232311042199004e-39 +sinpi10273 sinpi -2.938735877055719e-39 -> -9.232311042199004e-39 +sinpi10274 sinpi 1.401298464324817e-45 -> 4.402308961009504e-45 +sinpi10275 sinpi 0.0 -> 0.0 +sinpi10276 sinpi 5.010420900022432e-293 -> 1.5740701490903231e-292 +sinpi10277 sinpi -0.0 -> -0.0 +sinpi10278 sinpi -1.401298464324817e-45 -> -4.402308961009504e-45 +sinpi10279 sinpi -5.010420900022432e-293 -> -1.5740701490903231e-292 +sinpi10280 sinpi 1.401298464324817e-45 -> 4.402308961009504e-45 +sinpi10281 sinpi 0.0 -> 0.0 +sinpi10282 sinpi 5.562684646268003e-309 -> 1.7475689218952297e-308 +sinpi10283 sinpi -0.0 -> -0.0 +sinpi10284 sinpi -1.401298464324817e-45 -> -4.402308961009504e-45 +sinpi10285 sinpi -5.562684646268003e-309 -> -1.7475689218952297e-308 +sinpi10286 sinpi 1.401298464324817e-45 -> 4.402308961009504e-45 +sinpi10287 sinpi 0.0 -> 0.0 +sinpi10288 sinpi 5e-324 -> 1.5e-323 +sinpi10289 sinpi -0.0 -> -0.0 +sinpi10290 sinpi -1.401298464324817e-45 -> -4.402308961009504e-45 +sinpi10291 sinpi -5e-324 -> -1.5e-323 +sinpi10292 sinpi 1.401298464324817e-45 -> 4.402308961009504e-45 +sinpi10293 sinpi 0.0 -> 0.0 +sinpi10294 sinpi 5e-324 -> 1.5e-323 +sinpi10295 sinpi -0.0 -> -0.0 +sinpi10296 sinpi -1.401298464324817e-45 -> -4.402308961009504e-45 +sinpi10297 sinpi -5e-324 -> -1.5e-323 +sinpi10298 sinpi 0.039865780621767044 -> 0.12491488475763585 +sinpi10299 sinpi -0.009947286918759346 -> -0.03124523733449905 +sinpi10300 sinpi -0.00994728785008192 -> -0.03124524025890666 +sinpi10301 sinpi -0.009947287497407154 -> -0.03124523915148717 +sinpi10302 sinpi 0.009947438724339008 -> 0.03124571401293662 +sinpi10303 sinpi 0.009947437793016434 -> 0.03124571108852905 +sinpi10304 sinpi 0.009947437917099084 -> 0.03124571147815586 +sinpi10305 sinpi 0.009947437917099082 -> 0.031245711478155854 +sinpi10306 sinpi -0.0049737198278307915 -> -0.015624765847977973 +sinpi10307 sinpi -0.004973720293492079 -> -0.015624767310717469 +sinpi10308 sinpi -0.004973719955059152 -> -0.015624766247628864 +sinpi10309 sinpi -0.004973719955059153 -> -0.015624766247628865 + +sinpi20001 sinpi inf -> nan invalid +sinpi20002 sinpi -inf -> nan invalid +sinpi20003 sinpi nan -> nan + +----------- +-- tanpi -- +----------- + +tanpi10000 tanpi 0.0 -> 0.0 +tanpi10001 tanpi -0.0 -> -0.0 +tanpi10002 tanpi 1.1754943508222875e-38 -> 3.6929244168796014e-38 +tanpi10003 tanpi 2.2250738585072014e-308 -> 6.990275687580919e-308 +tanpi10004 tanpi 2.004168360008973e-292 -> 6.296280596361293e-292 +tanpi10005 tanpi -1.1754943508222875e-38 -> -3.6929244168796014e-38 +tanpi10006 tanpi -2.2250738585072014e-308 -> -6.990275687580919e-308 +tanpi10007 tanpi -2.004168360008973e-292 -> -6.296280596361293e-292 +tanpi10008 tanpi 1.401298464324817e-45 -> 4.402308961009504e-45 +tanpi10009 tanpi 5e-324 -> 1.5e-323 +tanpi10010 tanpi -1.401298464324817e-45 -> -4.402308961009504e-45 +tanpi10011 tanpi -5e-324 -> -1.5e-323 +tanpi10012 tanpi 3.4028234663852886e+38 -> 0.0 +tanpi10013 tanpi 1.7976931348623157e+308 -> 0.0 +tanpi10014 tanpi -3.4028234663852886e+38 -> -0.0 +tanpi10015 tanpi -1.7976931348623157e+308 -> -0.0 +tanpi10016 tanpi 1.0 -> -0.0 +tanpi10017 tanpi -1.0 -> 0.0 +tanpi10018 tanpi 2.0 -> 0.0 +tanpi10019 tanpi -2.0 -> -0.0 +tanpi10020 tanpi 3.0 -> -0.0 +tanpi10021 tanpi -3.0 -> 0.0 +tanpi10022 tanpi 4.0 -> 0.0 +tanpi10023 tanpi -4.0 -> -0.0 +tanpi10024 tanpi 0.10000000149011612 -> 0.32491970140846616 +tanpi10025 tanpi 0.09999999403953552 -> 0.3249196755306671 +tanpi10026 tanpi 0.1 -> 0.32491969623290634 +tanpi10027 tanpi 0.09999999999999999 -> 0.3249196962329063 +tanpi10028 tanpi -0.09999999403953552 -> -0.3249196755306671 +tanpi10029 tanpi -0.10000000149011612 -> -0.32491970140846616 +tanpi10030 tanpi -0.09999999999999999 -> -0.3249196962329063 +tanpi10031 tanpi -0.1 -> -0.32491969623290634 +tanpi10032 tanpi 0.20000000298023224 -> 0.7265425423102566 +tanpi10033 tanpi 0.19999998807907104 -> 0.7265424707857802 +tanpi10034 tanpi 0.2 -> 0.7265425280053609 +tanpi10035 tanpi 0.19999999999999998 -> 0.7265425280053608 +tanpi10036 tanpi -0.19999998807907104 -> -0.7265424707857802 +tanpi10037 tanpi -0.20000000298023224 -> -0.7265425423102566 +tanpi10038 tanpi -0.19999999999999998 -> -0.7265425280053608 +tanpi10039 tanpi -0.2 -> -0.7265425280053609 +tanpi10040 tanpi 0.30000001192092896 -> 1.3763820288695117 +tanpi10041 tanpi 0.29999998211860657 -> 1.3763817578736872 +tanpi10042 tanpi 0.30000000000000004 -> 1.376381920471174 +tanpi10043 tanpi 0.3 -> 1.3763819204711734 +tanpi10044 tanpi -0.29999998211860657 -> -1.3763817578736872 +tanpi10045 tanpi -0.30000001192092896 -> -1.3763820288695117 +tanpi10046 tanpi -0.3 -> -1.3763819204711734 +tanpi10047 tanpi -0.30000000000000004 -> -1.376381920471174 +tanpi10048 tanpi 0.4000000059604645 -> 3.0776837332696907 +tanpi10049 tanpi 0.3999999761581421 -> 3.077682752797731 +tanpi10050 tanpi 0.4 -> 3.077683537175254 +tanpi10051 tanpi 0.39999999999999997 -> 3.0776835371752522 +tanpi10052 tanpi -0.3999999761581421 -> -3.077682752797731 +tanpi10053 tanpi -0.4000000059604645 -> -3.0776837332696907 +tanpi10054 tanpi -0.39999999999999997 -> -3.0776835371752522 +tanpi10055 tanpi -0.4 -> -3.077683537175254 +tanpi10056 tanpi 0.6000000238418579 -> -3.077682752797731 +tanpi10057 tanpi 0.5999999642372131 -> -3.077684713742215 +tanpi10058 tanpi 0.6000000000000001 -> -3.0776835371752504 +tanpi10059 tanpi 0.6 -> -3.077683537175254 +tanpi10060 tanpi -0.5999999642372131 -> 3.077684713742215 +tanpi10061 tanpi -0.6000000238418579 -> 3.077682752797731 +tanpi10062 tanpi -0.6 -> 3.077683537175254 +tanpi10063 tanpi -0.6000000000000001 -> 3.0776835371752504 +tanpi10064 tanpi 0.7000000476837158 -> -1.3763814868779325 +tanpi10065 tanpi 0.699999988079071 -> -1.3763820288695117 +tanpi10066 tanpi 0.7000000000000001 -> -1.376381920471173 +tanpi10067 tanpi 0.7 -> -1.376381920471174 +tanpi10068 tanpi -0.699999988079071 -> 1.3763820288695117 +tanpi10069 tanpi -0.7000000476837158 -> 1.3763814868779325 +tanpi10070 tanpi -0.7 -> 1.376381920471174 +tanpi10071 tanpi -0.7000000000000001 -> 1.376381920471173 +tanpi10072 tanpi 0.800000011920929 -> -0.7265424707857802 +tanpi10073 tanpi 0.7999999523162842 -> -0.7265427568837151 +tanpi10074 tanpi 0.8 -> -0.7265425280053607 +tanpi10075 tanpi 0.7999999999999999 -> -0.7265425280053612 +tanpi10076 tanpi -0.7999999523162842 -> 0.7265427568837151 +tanpi10077 tanpi -0.800000011920929 -> 0.7265424707857802 +tanpi10078 tanpi -0.7999999999999999 -> 0.7265425280053612 +tanpi10079 tanpi -0.8 -> 0.7265425280053607 +tanpi10080 tanpi 0.9000000357627869 -> -0.3249195720194747 +tanpi10081 tanpi 0.8999999761581421 -> -0.3249197790418658 +tanpi10082 tanpi 0.9 -> -0.32491969623290623 +tanpi10083 tanpi 0.8999999999999999 -> -0.3249196962329066 +tanpi10084 tanpi -0.8999999761581421 -> 0.3249197790418658 +tanpi10085 tanpi -0.9000000357627869 -> 0.3249195720194747 +tanpi10086 tanpi -0.8999999999999999 -> 0.3249196962329066 +tanpi10087 tanpi -0.9 -> 0.32491969623290623 +tanpi10088 tanpi 1.100000023841858 -> 0.3249197790418658 +tanpi10089 tanpi 1.0999999046325684 -> 0.3249193649971088 +tanpi10090 tanpi 1.1 -> 0.3249196962329066 +tanpi10091 tanpi 1.0999999999999999 -> 0.32491969623290584 +tanpi10092 tanpi -1.0999999046325684 -> -0.3249193649971088 +tanpi10093 tanpi -1.100000023841858 -> -0.3249197790418658 +tanpi10094 tanpi -1.0999999999999999 -> -0.32491969623290584 +tanpi10095 tanpi -1.1 -> -0.3249196962329066 +tanpi10096 tanpi 1.2000000476837158 -> 0.7265427568837151 +tanpi10097 tanpi 1.1999999284744263 -> 0.7265421846879231 +tanpi10098 tanpi 1.2000000000000002 -> 0.7265425280053618 +tanpi10099 tanpi 1.2 -> 0.7265425280053607 +tanpi10100 tanpi -1.1999999284744263 -> -0.7265421846879231 +tanpi10101 tanpi -1.2000000476837158 -> -0.7265427568837151 +tanpi10102 tanpi -1.2 -> -0.7265425280053607 +tanpi10103 tanpi -1.2000000000000002 -> -0.7265425280053618 +tanpi10104 tanpi 1.3000000715255737 -> 1.3763825708613702 +tanpi10105 tanpi 1.2999999523162842 -> 1.3763814868779325 +tanpi10106 tanpi 1.3 -> 1.376381920471174 +tanpi10107 tanpi 1.2999999999999998 -> 1.376381920471172 +tanpi10108 tanpi -1.2999999523162842 -> -1.3763814868779325 +tanpi10109 tanpi -1.3000000715255737 -> -1.3763825708613702 +tanpi10110 tanpi -1.2999999999999998 -> -1.376381920471172 +tanpi10111 tanpi -1.3 -> -1.376381920471174 +tanpi10112 tanpi 1.4000000953674316 -> 3.0776866746889597 +tanpi10113 tanpi 1.399999976158142 -> 3.077682752797731 +tanpi10114 tanpi 1.4000000000000001 -> 3.077683537175258 +tanpi10115 tanpi 1.4 -> 3.0776835371752504 +tanpi10116 tanpi -1.399999976158142 -> -3.077682752797731 +tanpi10117 tanpi -1.4000000953674316 -> -3.0776866746889597 +tanpi10118 tanpi -1.4 -> -3.0776835371752504 +tanpi10119 tanpi -1.4000000000000001 -> -3.077683537175258 +tanpi10120 tanpi 0.24000000953674316 -> 0.9390625621984302 +tanpi10121 tanpi 0.23999999463558197 -> 0.9390624741032162 +tanpi10122 tanpi 0.24000000000000002 -> 0.9390625058174925 +tanpi10123 tanpi 0.24 -> 0.9390625058174923 +tanpi10124 tanpi -0.23999999463558197 -> -0.9390624741032162 +tanpi10125 tanpi -0.24000000953674316 -> -0.9390625621984302 +tanpi10126 tanpi -0.24 -> -0.9390625058174923 +tanpi10127 tanpi -0.24000000000000002 -> -0.9390625058174925 +tanpi10128 tanpi 0.49000000953674316 -> 31.820546320188043 +tanpi10129 tanpi 0.4899999797344208 -> 31.82045142533627 +tanpi10130 tanpi 0.49000000000000005 -> 31.820515953774105 +tanpi10131 tanpi 0.49 -> 31.82051595377393 +tanpi10132 tanpi -0.4899999797344208 -> -31.82045142533627 +tanpi10133 tanpi -0.49000000953674316 -> -31.820546320188043 +tanpi10134 tanpi -0.49 -> -31.82051595377393 +tanpi10135 tanpi -0.49000000000000005 -> -31.820515953774105 +tanpi10136 tanpi 0.5100000500679016 -> -31.820356531049924 +tanpi10137 tanpi 0.5099999904632568 -> -31.820546320188043 +tanpi10138 tanpi 0.51 -> -31.82051595377393 +tanpi10139 tanpi 0.5099999999999999 -> -31.820515953774283 +tanpi10140 tanpi -0.5099999904632568 -> 31.820546320188043 +tanpi10141 tanpi -0.5100000500679016 -> 31.820356531049924 +tanpi10142 tanpi -0.5099999999999999 -> 31.820515953774283 +tanpi10143 tanpi -0.51 -> 31.82051595377393 +tanpi10144 tanpi 0.7600000500679016 -> -0.9390622098176208 +tanpi10145 tanpi 0.7599999904632568 -> -0.9390625621984302 +tanpi10146 tanpi 0.76 -> -0.9390625058174923 +tanpi10147 tanpi 0.7599999999999999 -> -0.939062505817493 +tanpi10148 tanpi -0.7599999904632568 -> 0.9390625621984302 +tanpi10149 tanpi -0.7600000500679016 -> 0.9390622098176208 +tanpi10150 tanpi -0.7599999999999999 -> 0.939062505817493 +tanpi10151 tanpi -0.76 -> 0.9390625058174923 +tanpi10152 tanpi 1.2400000095367432 -> 0.9390625621984302 +tanpi10153 tanpi 1.2399998903274536 -> 0.9390618574369353 +tanpi10154 tanpi 1.2400000000000002 -> 0.9390625058174936 +tanpi10155 tanpi 1.24 -> 0.9390625058174923 +tanpi10156 tanpi -1.2399998903274536 -> -0.9390618574369353 +tanpi10157 tanpi -1.2400000095367432 -> -0.9390625621984302 +tanpi10158 tanpi -1.24 -> -0.9390625058174923 +tanpi10159 tanpi -1.2400000000000002 -> -0.9390625058174936 +tanpi10160 tanpi 1.4900000095367432 -> 31.820546320188043 +tanpi10161 tanpi 1.4899998903274536 -> 31.820166744173495 +tanpi10162 tanpi 1.4900000000000002 -> 31.82051595377464 +tanpi10163 tanpi 1.49 -> 31.82051595377393 +tanpi10164 tanpi -1.4899998903274536 -> -31.820166744173495 +tanpi10165 tanpi -1.4900000095367432 -> -31.820546320188043 +tanpi10166 tanpi -1.49 -> -31.82051595377393 +tanpi10167 tanpi -1.4900000000000002 -> -31.82051595377464 +tanpi10168 tanpi 1.5100001096725464 -> -31.820166744173495 +tanpi10169 tanpi 1.5099999904632568 -> -31.820546320188043 +tanpi10170 tanpi 1.51 -> -31.82051595377393 +tanpi10171 tanpi 1.5099999999999998 -> -31.82051595377464 +tanpi10172 tanpi -1.5099999904632568 -> 31.820546320188043 +tanpi10173 tanpi -1.5100001096725464 -> 31.820166744173495 +tanpi10174 tanpi -1.5099999999999998 -> 31.82051595377464 +tanpi10175 tanpi -1.51 -> 31.82051595377393 +tanpi10176 tanpi 1.7600001096725464 -> -0.9390618574369353 +tanpi10177 tanpi 1.7599999904632568 -> -0.9390625621984302 +tanpi10178 tanpi 1.76 -> -0.9390625058174923 +tanpi10179 tanpi 1.7599999999999998 -> -0.9390625058174936 +tanpi10180 tanpi -1.7599999904632568 -> 0.9390625621984302 +tanpi10181 tanpi -1.7600001096725464 -> 0.9390618574369353 +tanpi10182 tanpi -1.7599999999999998 -> 0.9390625058174936 +tanpi10183 tanpi -1.76 -> 0.9390625058174923 +tanpi10184 tanpi 100.24000549316406 -> 0.9390949817630689 +tanpi10185 tanpi 100.23999786376953 -> 0.9390498765673647 +tanpi10186 tanpi 100.24000000000001 -> 0.9390625058175461 +tanpi10187 tanpi 100.24 -> 0.9390625058174621 +tanpi10188 tanpi -100.23999786376953 -> -0.9390498765673647 +tanpi10189 tanpi -100.24000549316406 -> -0.9390949817630689 +tanpi10190 tanpi -100.24 -> -0.9390625058174621 +tanpi10191 tanpi -100.24000000000001 -> -0.9390625058175461 +tanpi10192 tanpi 100.49000549316406 -> 31.83801660184476 +tanpi10193 tanpi 100.48999786376953 -> 31.81371533579395 +tanpi10194 tanpi 100.49000000000001 -> 31.820515953802918 +tanpi10195 tanpi 100.49 -> 31.820515953757667 +tanpi10196 tanpi -100.48999786376953 -> -31.81371533579395 +tanpi10197 tanpi -100.49000549316406 -> -31.83801660184476 +tanpi10198 tanpi -100.49 -> -31.820515953757667 +tanpi10199 tanpi -100.49000000000001 -> -31.820515953802918 +tanpi10200 tanpi 100.51000213623047 -> -31.81371533579395 +tanpi10201 tanpi 100.50999450683594 -> -31.83801660184476 +tanpi10202 tanpi 100.51 -> -31.820515953757667 +tanpi10203 tanpi 100.50999999999999 -> -31.820515953802918 +tanpi10204 tanpi -100.50999450683594 -> 31.83801660184476 +tanpi10205 tanpi -100.51000213623047 -> 31.81371533579395 +tanpi10206 tanpi -100.50999999999999 -> 31.820515953802918 +tanpi10207 tanpi -100.51 -> 31.820515953757667 +tanpi10208 tanpi 100.76000213623047 -> -0.9390498765673647 +tanpi10209 tanpi 100.75999450683594 -> -0.9390949817630689 +tanpi10210 tanpi 100.76 -> -0.9390625058174621 +tanpi10211 tanpi 100.75999999999999 -> -0.9390625058175461 +tanpi10212 tanpi -100.75999450683594 -> 0.9390949817630689 +tanpi10213 tanpi -100.76000213623047 -> 0.9390498765673647 +tanpi10214 tanpi -100.75999999999999 -> 0.9390625058175461 +tanpi10215 tanpi -100.76 -> 0.9390625058174621 +tanpi10216 tanpi 1234.56005859375 -> -5.236946012446678 +tanpi10217 tanpi 1234.5599365234375 -> -5.247869032587774 +tanpi10218 tanpi 1234.5600000000002 -> -5.2421835810977155 +tanpi10219 tanpi 1234.56 -> -5.242183581118059 +tanpi10220 tanpi -1234.5599365234375 -> 5.247869032587774 +tanpi10221 tanpi -1234.56005859375 -> 5.236946012446678 +tanpi10222 tanpi -1234.56 -> 5.242183581118059 +tanpi10223 tanpi -1234.5600000000002 -> 5.2421835810977155 +tanpi10224 tanpi 2097151.375 -> 2.414213562373095 +tanpi10225 tanpi -2097151.375 -> -2.414213562373095 +tanpi10226 tanpi 2097151.625 -> -2.414213562373095 +tanpi10227 tanpi -2097151.625 -> 2.414213562373095 +tanpi10228 tanpi 1125899906842624.0 -> 0.0 +tanpi10229 tanpi 1125899839733760.0 -> 0.0 +tanpi10230 tanpi 1125899906842623.4 -> 2.414213562373095 +tanpi10231 tanpi -1125899839733760.0 -> -0.0 +tanpi10232 tanpi -1125899906842624.0 -> -0.0 +tanpi10233 tanpi -1125899906842623.4 -> -2.414213562373095 +tanpi10234 tanpi 1125899906842624.0 -> 0.0 +tanpi10235 tanpi 1125899839733760.0 -> 0.0 +tanpi10236 tanpi 1125899906842623.6 -> -2.414213562373095 +tanpi10237 tanpi -1125899839733760.0 -> -0.0 +tanpi10238 tanpi -1125899906842624.0 -> -0.0 +tanpi10239 tanpi -1125899906842623.6 -> 2.414213562373095 +tanpi10240 tanpi 2.305843009213694e+18 -> 0.0 +tanpi10241 tanpi 2.3058428717747405e+18 -> 0.0 +tanpi10242 tanpi 2.3058430092136937e+18 -> 0.0 +tanpi10243 tanpi -2.3058428717747405e+18 -> -0.0 +tanpi10244 tanpi -2.305843009213694e+18 -> -0.0 +tanpi10245 tanpi -2.3058430092136937e+18 -> -0.0 +tanpi10246 tanpi 2.305843009213694e+18 -> 0.0 +tanpi10247 tanpi 2.3058428717747405e+18 -> 0.0 +tanpi10248 tanpi 2.3058430092136937e+18 -> 0.0 +tanpi10249 tanpi -2.3058428717747405e+18 -> -0.0 +tanpi10250 tanpi -2.305843009213694e+18 -> -0.0 +tanpi10251 tanpi -2.3058430092136937e+18 -> -0.0 +tanpi10252 tanpi 1.298074214633707e+33 -> 0.0 +tanpi10253 tanpi 1.2980741372624545e+33 -> 0.0 +tanpi10254 tanpi 1.2980742146337068e+33 -> 0.0 +tanpi10255 tanpi -1.2980741372624545e+33 -> -0.0 +tanpi10256 tanpi -1.298074214633707e+33 -> -0.0 +tanpi10257 tanpi -1.2980742146337068e+33 -> -0.0 +tanpi10258 tanpi 1.298074214633707e+33 -> 0.0 +tanpi10259 tanpi 1.2980741372624545e+33 -> 0.0 +tanpi10260 tanpi 1.2980742146337068e+33 -> 0.0 +tanpi10261 tanpi -1.2980741372624545e+33 -> -0.0 +tanpi10262 tanpi -1.298074214633707e+33 -> -0.0 +tanpi10263 tanpi -1.2980742146337068e+33 -> -0.0 +tanpi10264 tanpi 0.48004481196403503 -> 15.930332115434627 +tanpi10265 tanpi -0.4950249195098877 -> -63.97564155226383 +tanpi10266 tanpi -0.4950249493122101 -> -63.97602485152189 +tanpi10267 tanpi -0.49502493342943843 -> -63.97582057644752 +tanpi10268 tanpi -3.659269094467163 -> 1.828928514937406 +tanpi10269 tanpi -3.659269332885742 -> 1.8289252604911337 +tanpi10270 tanpi -3.659269111498701 -> 1.8289282824542836 +tanpi10271 tanpi -3.6592691114987015 -> 1.8289282824542774 +tanpi10272 tanpi -1.6591962575912476 -> 1.829923165916756 +tanpi10273 tanpi -1.659196376800537 -> 1.8299215373295707 +tanpi10274 tanpi -1.6591963470121214 -> 1.8299219442861712 +tanpi10275 tanpi -1.6591963470121216 -> 1.829921944286168 + +tanpi20001 tanpi inf -> nan invalid +tanpi20002 tanpi -inf -> nan invalid +tanpi20003 tanpi nan -> nan diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index 85ddf68b3f36efc..cc37697189af3e5 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -75,7 +75,7 @@ def parse_mtestfile(fname): -- starts a comment blank lines, or lines containing only a comment, are ignored other lines are expected to have the form - id fn arg -> expected [flag]* + id fn arg... -> expected [flag]* """ with open(fname, encoding="utf-8") as fp: @@ -87,12 +87,12 @@ def parse_mtestfile(fname): continue lhs, rhs = line.split('->') - id, fn, arg = lhs.split() + id, fn, *args = lhs.split() rhs_pieces = rhs.split() exp = rhs_pieces[0] flags = rhs_pieces[1:] - yield (id, fn, float(arg), float(exp), flags) + yield (id, fn, [float(arg) for arg in args], float(exp), flags) def parse_testfile(fname): @@ -2141,10 +2141,10 @@ def test_testfile(self): @requires_IEEE_754 def test_mtestfile(self): - fail_fmt = "{}: {}({!r}): {}" + fail_fmt = "{}: {}{!r}: {}" failures = [] - for id, fn, arg, expected, flags in parse_mtestfile(math_testcases): + for id, fn, args, expected, flags in parse_mtestfile(math_testcases): func = getattr(math, fn) if 'invalid' in flags or 'divide-by-zero' in flags: @@ -2153,7 +2153,7 @@ def test_mtestfile(self): expected = 'OverflowError' try: - got = func(arg) + got = func(*args) except ValueError: got = 'ValueError' except OverflowError: @@ -2176,7 +2176,7 @@ def test_mtestfile(self): # general. abs_tol = 1e-15 - elif fn == 'erfc' and arg >= 0.0: + elif fn == 'erfc' and (arg := args[0]) >= 0.0: # erfc has less-than-ideal accuracy for large # arguments (x ~ 25 or so), mainly due to the # error involved in computing exp(-x*x). @@ -2199,7 +2199,7 @@ def test_mtestfile(self): if failure is None: continue - msg = fail_fmt.format(id, fn, arg, failure) + msg = fail_fmt.format(id, fn, args, failure) failures.append(msg) if failures: diff --git a/Misc/NEWS.d/next/Library/2026-05-27-15-51-29.gh-issue-150534.3Nan6S.rst b/Misc/NEWS.d/next/Library/2026-05-27-15-51-29.gh-issue-150534.3Nan6S.rst new file mode 100644 index 000000000000000..2ead9990513015f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-27-15-51-29.gh-issue-150534.3Nan6S.rst @@ -0,0 +1,6 @@ +Added trigonometric functions that work in units of half turns, rather than +radians. The new functions :func:`math.acospi`, :func:`math.asinpi`, +:func:`math.atanpi`, and :func:`math.atan2pi` return half-turn angles. The +new functions :func:`math.cospi`, :func:`math.sinpi`, and :func:`math.tanpi` +take half-turn angle arguments. These functions are recommended by IEEE +754-2019 and standardized in C23. diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 5636a00afe10c4b..64e5372d73d2f29 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -187,13 +187,6 @@ tl_to_d(TripleLength total) } -/* - sin(pi*x), giving accurate results for all finite x (especially x - integral or close to an integer). This is here for use in the - reflection formula for the gamma function. It conforms to IEEE - 754-2008 for finite arguments, but not for infinities or nans. -*/ - static const double pi = 3.141592653589793238462643383279502884197; static const double logpi = 1.144729885849400174143427351353058711647; @@ -219,13 +212,120 @@ static const double logpi = 1.144729885849400174143427351353058711647; } \ } +#ifndef HAVE_ACOSPI +/* + acos(x)/pi. It conforms to C23 Annex 'F'. +*/ + +static double +m_acospi(double x) +{ + double r = acos(x)/pi; + if (isgreater(r, 1.0)) { + return 1.0; + } + return r; +} +#else +#define m_acospi acospi +#endif + +#ifndef HAVE_ASINPI +/* + asin(x)/pi. It conforms to C23 Annex 'F'. +*/ + +static double +m_asinpi(double x) +{ + double r = asin(x)/pi; + if (isgreater(fabs(r), 0.5)) { + return copysign(0.5, r); + } + return r; +} +#else +#define m_asinpi asinpi +#endif + +#ifndef HAVE_ATANPI +/* + asin(x)/pi. It conforms to C23 Annex 'F'. +*/ + +static double +m_atanpi(double x) +{ + double r = atan(x)/pi; + if (isgreater(fabs(r), 0.5)) { + return copysign(0.5, r); + } + return r; +} +#else +#define m_atanpi atanpi +#endif + +#ifndef HAVE_ATAN2PI +/* + asin(x)/pi. It conforms to C23 Annex 'F'. +*/ + +static double +m_atan2pi(double y, double x) +{ + double r = atan2(y, x)/pi; + if (isgreater(fabs(r), 1.0)) { + return copysign(1.0, r); + } + return r; +} +#else +#define m_atan2pi atan2pi +#endif + +#ifndef HAVE_COSPI +/* + cos(pi*x), giving accurate results for all finite x (especially x + integral or close to an integer). It conforms to C23 Annex 'F'. +*/ + +static double +m_cospi(double x) +{ + if (!isfinite(x)) { + return cos(x); + } + x = fabs(x - 2.0 * round(0.5 * x)); + if (x <= 0.25) { + return cos(pi * x); + } + if (x == 0.5) { + return 0.0; + } + if (x <= 0.75) { + return sin(pi * (0.5 - x)); + } + return -cos(pi * (1.0 - x)); +} +#else +#define m_cospi cospi +#endif + +#ifndef HAVE_SINPI +/* + sin(pi*x), giving accurate results for all finite x (especially x + integral or close to an integer). It conforms to C23 Annex 'F'. +*/ + static double m_sinpi(double x) { double y, r; int n; - /* this function should only ever be called for finite arguments */ - assert(isfinite(x)); + if (!isfinite(x)) { + return sin(x); + } y = fmod(fabs(x), 2.0); n = (int)round(2.0*y); assert(0 <= n && n <= 4); @@ -252,6 +352,47 @@ m_sinpi(double x) } return copysign(1.0, x)*r; } +#else +#define m_sinpi sinpi +#endif + +#ifndef HAVE_TANPI +/* + tan(pi*x), giving accurate results for all finite x (especially x + integral or close to an integer). It conforms to C23 Annex 'F'. +*/ + +static double +m_tanpi(double x) +{ + double y, absy; + if (!isfinite(x)) { + return tan(x); + } + y = x - 2.0 * round(0.5 * x); + absy = fabs(y); + if (absy == 0.0) { + return copysign(0.0, x); + } + if (absy == 1.0) { + return copysign(0.0, -x); + } + if (absy == 0.5) { + errno = ERANGE; + return 1.0 / copysign(0.0, y); + } + if (absy > 0.5) { + y -= copysign(1.0, y); + absy = fabs(y); + } + if (absy <= 0.25) { + return tan(pi * y); + } + return copysign(1.0 / tan(pi * (0.5 - absy)), y); +} +#else +#define m_tanpi tanpi +#endif /* Implementation of the real gamma function. Kept here to work around issues (see e.g. gh-70309) with quality of libm's tgamma/lgamma implementations @@ -952,6 +1093,11 @@ FUNC1D(acosh, acosh, 0, "acosh($module, x, /)\n--\n\n" "Return the inverse hyperbolic cosine of x.", "expected argument value not less than 1, got %s") +FUNC1D(acospi, m_acospi, 0, + "acospi($module, x, /)\n--\n\n" + "Return the arc cosine (measured in half-turns) of x.\n\n" + "The result is between 0 and 1.", + "expected a number in range from -1 up to 1, got %s") FUNC1D(asin, asin, 0, "asin($module, x, /)\n--\n\n" "Return the arc sine (measured in radians) of x.\n\n" @@ -960,6 +1106,11 @@ FUNC1D(asin, asin, 0, FUNC1(asinh, asinh, 0, "asinh($module, x, /)\n--\n\n" "Return the inverse hyperbolic sine of x.") +FUNC1D(asinpi, m_asinpi, 0, + "asinpi($module, x, /)\n--\n\n" + "Return the arc sine (measured in half-turns) of x.\n\n" + "The result is between -1/2 and 1/2.", + "expected a number in range from -1 up to 1, got %s") FUNC1(atan, atan, 0, "atan($module, x, /)\n--\n\n" "Return the arc tangent (measured in radians) of x.\n\n" @@ -968,10 +1119,19 @@ FUNC2(atan2, atan2, "atan2($module, y, x, /)\n--\n\n" "Return the arc tangent (measured in radians) of y/x.\n\n" "Unlike atan(y/x), the signs of both x and y are considered.") +FUNC2(atan2pi, m_atan2pi, + "atan2pi($module, y, x, /)\n--\n\n" + "Return the arc tangent (measured in half-turns) of y/x.\n\n" + "Unlike atanpi(y/x), the signs of both x and y are considered.") FUNC1D(atanh, atanh, 0, "atanh($module, x, /)\n--\n\n" "Return the inverse hyperbolic tangent of x.", "expected a number between -1 and 1, got %s") +FUNC1D(atanpi, m_atanpi, 0, + "atanpi($module, x, /)\n--\n\n" + "Return the arc tangent (measured in half-turns) of x.\n\n" + "The result is between 0 and 1.", + "expected a number in range from -1 up to 1, got %s") FUNC1(cbrt, cbrt, 0, "cbrt($module, x, /)\n--\n\n" "Return the cube root of x.") @@ -1024,6 +1184,10 @@ FUNC1D(cos, cos, 0, FUNC1(cosh, cosh, 1, "cosh($module, x, /)\n--\n\n" "Return the hyperbolic cosine of x.") +FUNC1D(cospi, m_cospi, 0, + "cospi($module, x, /)\n--\n\n" + "Return the cosine of x (measured in half-turns).", + "expected a finite input, got %s") FUNC1A(erf, erf, "erf($module, x, /)\n--\n\n" "Error function at x.") @@ -1158,6 +1322,10 @@ FUNC1D(sin, sin, 0, FUNC1(sinh, sinh, 1, "sinh($module, x, /)\n--\n\n" "Return the hyperbolic sine of x.") +FUNC1D(sinpi, m_sinpi, 0, + "sinpi($module, x, /)\n--\n\n" + "Return the sine of x (measured in half-turns).", + "expected a finite input, got %s") FUNC1D(sqrt, sqrt, 0, "sqrt($module, x, /)\n--\n\n" "Return the square root of x.", @@ -1169,6 +1337,10 @@ FUNC1D(tan, tan, 0, FUNC1(tanh, tanh, 0, "tanh($module, x, /)\n--\n\n" "Return the hyperbolic tangent of x.") +FUNC1D(tanpi, m_tanpi, 1, + "tanpi($module, x, /)\n--\n\n" + "Return the tangent of x (measured in half-turns).", + "expected a finite input, got %s") /* Precision summation function as msum() by Raymond Hettinger in , @@ -3047,16 +3219,21 @@ math_exec(PyObject *module) static PyMethodDef math_methods[] = { {"acos", math_acos, METH_O, math_acos_doc}, {"acosh", math_acosh, METH_O, math_acosh_doc}, + {"acospi", math_acospi, METH_O, math_acospi_doc}, {"asin", math_asin, METH_O, math_asin_doc}, {"asinh", math_asinh, METH_O, math_asinh_doc}, + {"asinpi", math_asinpi, METH_O, math_asinpi_doc}, {"atan", math_atan, METH_O, math_atan_doc}, {"atan2", _PyCFunction_CAST(math_atan2), METH_FASTCALL, math_atan2_doc}, {"atanh", math_atanh, METH_O, math_atanh_doc}, + {"atan2pi", _PyCFunction_CAST(math_atan2pi), METH_FASTCALL, math_atan2pi_doc}, + {"atanpi", math_atanpi, METH_O, math_atanpi_doc}, {"cbrt", math_cbrt, METH_O, math_cbrt_doc}, MATH_CEIL_METHODDEF {"copysign", _PyCFunction_CAST(math_copysign), METH_FASTCALL, math_copysign_doc}, {"cos", math_cos, METH_O, math_cos_doc}, {"cosh", math_cosh, METH_O, math_cosh_doc}, + {"cospi", math_cospi, METH_O, math_cospi_doc}, MATH_DEGREES_METHODDEF MATH_DIST_METHODDEF {"erf", math_erf, METH_O, math_erf_doc}, @@ -3093,9 +3270,11 @@ static PyMethodDef math_methods[] = { MATH_SIGNBIT_METHODDEF {"sin", math_sin, METH_O, math_sin_doc}, {"sinh", math_sinh, METH_O, math_sinh_doc}, + {"sinpi", math_sinpi, METH_O, math_sinpi_doc}, {"sqrt", math_sqrt, METH_O, math_sqrt_doc}, {"tan", math_tan, METH_O, math_tan_doc}, {"tanh", math_tanh, METH_O, math_tanh_doc}, + {"tanpi", math_tanpi, METH_O, math_tanpi_doc}, MATH_SUMPROD_METHODDEF MATH_TRUNC_METHODDEF MATH_PROD_METHODDEF diff --git a/configure b/configure index 67e5b3e46828024..0cd98a193030dd5 100755 --- a/configure +++ b/configure @@ -642,6 +642,7 @@ ac_includes_default="\ #endif" ac_header_c_list= +ac_func_c_list= ac_subst_vars='LTLIBOBJS MODULE_BLOCK JIT_SHIM_BUILD_O @@ -3421,6 +3422,13 @@ as_fn_append ac_header_c_list " sys/types.h sys_types_h HAVE_SYS_TYPES_H" as_fn_append ac_header_c_list " unistd.h unistd_h HAVE_UNISTD_H" as_fn_append ac_header_c_list " wchar.h wchar_h HAVE_WCHAR_H" as_fn_append ac_header_c_list " minix/config.h minix_config_h HAVE_MINIX_CONFIG_H" +as_fn_append ac_func_c_list " acospi HAVE_ACOSPI" +as_fn_append ac_func_c_list " asinpi HAVE_ASINPI" +as_fn_append ac_func_c_list " atanpi HAVE_ATANPI" +as_fn_append ac_func_c_list " atan2pi HAVE_ATAN2PI" +as_fn_append ac_func_c_list " cospi HAVE_COSPI" +as_fn_append ac_func_c_list " sinpi HAVE_SINPI" +as_fn_append ac_func_c_list " tanpi HAVE_TANPI" # Auxiliary files required by this configure script. ac_aux_files="install-sh config.guess config.sub" @@ -27003,6 +27011,27 @@ esac fi done + +ac_func= +for ac_item in $ac_func_c_list +do + if test $ac_func; then + ac_fn_c_check_func "$LINENO" $ac_func ac_cv_func_$ac_func + if eval test \"x\$ac_cv_func_$ac_func\" = xyes; then + echo "#define $ac_item 1" >> confdefs.h + fi + ac_func= + else + ac_func=$ac_item + fi +done + + + + + + + LIBS=$LIBS_SAVE { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether POSIX semaphores are enabled" >&5 diff --git a/configure.ac b/configure.ac index cdff5ec78a5fb75..d3320f0e45fe146 100644 --- a/configure.ac +++ b/configure.ac @@ -6367,6 +6367,8 @@ AC_CHECK_FUNCS( [], [AC_MSG_ERROR([Python requires C99 compatible libm])] ) + +AC_CHECK_FUNCS_ONCE(acospi asinpi atanpi atan2pi cospi sinpi tanpi) LIBS=$LIBS_SAVE dnl For multiprocessing module, check that sem_open diff --git a/pyconfig.h.in b/pyconfig.h.in index 7ef83fcd0b9e0bf..dc2404fe85fa232 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -55,6 +55,9 @@ /* Define to 1 if you have the 'acosh' function. */ #undef HAVE_ACOSH +/* Define to 1 if you have the 'acospi' function. */ +#undef HAVE_ACOSPI + /* struct addrinfo (netdb.h) */ #undef HAVE_ADDRINFO @@ -73,12 +76,21 @@ /* Define to 1 if you have the 'asinh' function. */ #undef HAVE_ASINH +/* Define to 1 if you have the 'asinpi' function. */ +#undef HAVE_ASINPI + /* Define to 1 if you have the header file. */ #undef HAVE_ASM_TYPES_H +/* Define to 1 if you have the 'atan2pi' function. */ +#undef HAVE_ATAN2PI + /* Define to 1 if you have the 'atanh' function. */ #undef HAVE_ATANH +/* Define to 1 if you have the 'atanpi' function. */ +#undef HAVE_ATANPI + /* Define to 1 if you have the 'backtrace' function. */ #undef HAVE_BACKTRACE @@ -179,6 +191,9 @@ /* Define to 1 if you have the 'copy_file_range' function. */ #undef HAVE_COPY_FILE_RANGE +/* Define to 1 if you have the 'cospi' function. */ +#undef HAVE_COSPI + /* Define to 1 if you have the 'ctermid' function. */ #undef HAVE_CTERMID @@ -1255,6 +1270,9 @@ /* Define to 1 if you have the 'sigwaitinfo' function. */ #undef HAVE_SIGWAITINFO +/* Define to 1 if you have the 'sinpi' function. */ +#undef HAVE_SINPI + /* Define to 1 if you have the 'snprintf' function. */ #undef HAVE_SNPRINTF @@ -1527,6 +1545,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_SYS_XATTR_H +/* Define to 1 if you have the 'tanpi' function. */ +#undef HAVE_TANPI + /* Define to 1 if you have the 'tcgetpgrp' function. */ #undef HAVE_TCGETPGRP From 34e8d6fb9639bfa1b16da2798acbae3cec53a690 Mon Sep 17 00:00:00 2001 From: Inada Naoki Date: Sat, 6 Jun 2026 20:37:49 +0900 Subject: [PATCH 047/185] Doc: fix order of PyLong_FromUnsignedLongLong (#150937) --- Doc/c-api/long.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Doc/c-api/long.rst b/Doc/c-api/long.rst index 60e3ae4a064e729..874e422d4701dd8 100644 --- a/Doc/c-api/long.rst +++ b/Doc/c-api/long.rst @@ -71,6 +71,12 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate. on failure. +.. c:function:: PyObject* PyLong_FromUnsignedLongLong(unsigned long long v) + + Return a new :c:type:`PyLongObject` object from a C :c:expr:`unsigned long long`, + or ``NULL`` on failure. + + .. c:function:: PyObject* PyLong_FromInt32(int32_t value) PyObject* PyLong_FromInt64(int64_t value) @@ -81,12 +87,6 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate. .. versionadded:: 3.14 -.. c:function:: PyObject* PyLong_FromUnsignedLongLong(unsigned long long v) - - Return a new :c:type:`PyLongObject` object from a C :c:expr:`unsigned long long`, - or ``NULL`` on failure. - - .. c:function:: PyObject* PyLong_FromUInt32(uint32_t value) PyObject* PyLong_FromUInt64(uint64_t value) From 6dd6ce57d90611180a0c1927477049031463b897 Mon Sep 17 00:00:00 2001 From: Alexander Shadchin Date: Sat, 6 Jun 2026 14:52:01 +0300 Subject: [PATCH 048/185] Delete mention encoding and errors for importlib.resources.path() (GH-143111) --- Doc/library/importlib.resources.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/Doc/library/importlib.resources.rst b/Doc/library/importlib.resources.rst index 653fa61420be869..72db66f9f06f890 100644 --- a/Doc/library/importlib.resources.rst +++ b/Doc/library/importlib.resources.rst @@ -240,7 +240,6 @@ For all the following functions: .. versionchanged:: 3.13 Multiple *path_names* are accepted. - *encoding* and *errors* must be given as keyword arguments. .. function:: is_resource(anchor, *path_names) From e4308c5fc79dd6522f457515c29b9ed603f12a68 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 6 Jun 2026 05:47:33 -0700 Subject: [PATCH 049/185] gh-149083: Use sentinel for urllib.parse._UNSPECIFIED (#149612) This was added in 3.15; let's use a real sentinel instead of an ad-hoc list object. --- Lib/urllib/parse.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/urllib/parse.py b/Lib/urllib/parse.py index d64f678d235b6f9..82b95adbdc283ef 100644 --- a/Lib/urllib/parse.py +++ b/Lib/urllib/parse.py @@ -277,7 +277,7 @@ def _hostinfo(self): return hostname, port -_UNSPECIFIED = ['not specified'] +_UNSPECIFIED = sentinel("_UNSPECIFIED", repr="") _MISSING_AS_NONE_DEFAULT = False class _ResultBase: From fa2eb1ea6f0e05d41de9960fd7867720acf91005 Mon Sep 17 00:00:00 2001 From: Edward Xu Date: Sun, 7 Jun 2026 01:03:04 +0800 Subject: [PATCH 050/185] gh-150411: fix `gc_generation.count` race in free-threading (#150413) --- Lib/test/test_free_threading/test_gc.py | 29 +++++++++++++++++++ ...-05-26-00-06-30.gh-issue-150411.u-d-_5.rst | 2 ++ Modules/gcmodule.c | 2 +- 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-05-26-00-06-30.gh-issue-150411.u-d-_5.rst diff --git a/Lib/test/test_free_threading/test_gc.py b/Lib/test/test_free_threading/test_gc.py index cc1888dae48bc03..399010234509408 100644 --- a/Lib/test/test_free_threading/test_gc.py +++ b/Lib/test/test_free_threading/test_gc.py @@ -124,6 +124,35 @@ def setter(): finally: gc.set_threshold(*current_threshold) + def test_get_count(self): + class CyclicReference: + def __init__(self): + self.ref = self + + NUM_ALLOCATORS = 7 + NUM_READERS = 1 + NUM_THREADS = NUM_ALLOCATORS + NUM_READERS + NUM_ITERS = 1000 + + barrier = threading.Barrier(NUM_THREADS) + + def allocator(): + barrier.wait() + for _ in range(NUM_ITERS): + CyclicReference() + + + def reader(): + barrier.wait() + for _ in range(NUM_ITERS): + gc.get_count() + + threads = [Thread(target=allocator) for _ in range(NUM_ALLOCATORS)] + threads.extend(Thread(target=reader) for _ in range(NUM_READERS)) + + with threading_helper.start_threads(threads): + pass + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-05-26-00-06-30.gh-issue-150411.u-d-_5.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-26-00-06-30.gh-issue-150411.u-d-_5.rst new file mode 100644 index 000000000000000..5b19a4fff5ddc73 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-26-00-06-30.gh-issue-150411.u-d-_5.rst @@ -0,0 +1,2 @@ +Fix a data race in the free-threaded build when :func:`gc.get_count` reads +the young generation allocation count while another thread updates it. diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index 8762e592b258104..0093995441e390d 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -233,7 +233,7 @@ gc_get_count_impl(PyObject *module) gcstate->generations[2].count); #else return Py_BuildValue("(iii)", - gcstate->young.count, + _Py_atomic_load_int_relaxed(&gcstate->young.count), gcstate->old[0].count, gcstate->old[1].count); #endif From 944bc52da0a6528027c3d054ceab5b85d2fdea02 Mon Sep 17 00:00:00 2001 From: Zackery Spytz Date: Sat, 6 Jun 2026 10:14:45 -0700 Subject: [PATCH 051/185] gh-50948: IDLE: Warn if saving a file will overwrite a newer version (GH-17578) Co-authored-by: Guilherme Polo Co-authored-by: Priya Pappachan Co-authored-by: Serhiy Storchaka --- Lib/idlelib/iomenu.py | 27 +++++++++++++++++++ .../2019-12-12-03-18-02.bpo-6699.1CqJFG.rst | 1 + 2 files changed, 28 insertions(+) create mode 100644 Misc/NEWS.d/next/IDLE/2019-12-12-03-18-02.bpo-6699.1CqJFG.rst diff --git a/Lib/idlelib/iomenu.py b/Lib/idlelib/iomenu.py index 464126e2df06682..fc502f7fde17808 100644 --- a/Lib/idlelib/iomenu.py +++ b/Lib/idlelib/iomenu.py @@ -61,6 +61,7 @@ def set_filename_change_hook(self, hook): self.filename_change_hook = hook filename = None + file_timestamp = None dirname = None def set_filename(self, filename): @@ -127,6 +128,7 @@ def loadfile(self, filename): chars = f.read() fileencoding = f.encoding eol_convention = f.newlines + file_timestamp = self.getmtime(filename) converted = False except (UnicodeDecodeError, SyntaxError): # Wait for the editor window to appear @@ -142,6 +144,7 @@ def loadfile(self, filename): chars = f.read() fileencoding = f.encoding eol_convention = f.newlines + file_timestamp = self.getmtime(filename) converted = True except OSError as err: messagebox.showerror("I/O Error", str(err), parent=self.text) @@ -170,6 +173,7 @@ def loadfile(self, filename): self.text.insert("1.0", chars) self.reset_undo() self.set_filename(filename) + self.file_timestamp = file_timestamp if converted: # We need to save the conversion results first # before being able to execute the code @@ -206,7 +210,26 @@ def save(self, event): if not self.filename: self.save_as(event) else: + # Check the time of most recent content modification so the + # user doesn't accidentally overwrite a newer version of the file. + try: + file_timestamp = self.getmtime(self.filename) + except OSError: + pass + else: + if self.file_timestamp != file_timestamp: + confirm = messagebox.askokcancel( + title="File has changed", + message=( + "The file has changed on disk since reading it!\n\n" + "Do you really want to overwrite it?"), + default=messagebox.CANCEL, + parent=self.text) + if not confirm: + return "break" + if self.writefile(self.filename): + self.file_timestamp = self.getmtime(self.filename) self.set_saved(True) try: self.editwin.store_file_breaks() @@ -219,6 +242,7 @@ def save_as(self, event): filename = self.asksavefile() if filename: if self.writefile(filename): + self.file_timestamp = self.getmtime(filename) self.set_filename(filename) self.set_saved(1) try: @@ -251,6 +275,9 @@ def writefile(self, filename): parent=self.text) return False + def getmtime(self, filename): + return os.stat(filename).st_mtime + def fixnewlines(self): """Return text with os eols. diff --git a/Misc/NEWS.d/next/IDLE/2019-12-12-03-18-02.bpo-6699.1CqJFG.rst b/Misc/NEWS.d/next/IDLE/2019-12-12-03-18-02.bpo-6699.1CqJFG.rst new file mode 100644 index 000000000000000..e7fb9bf1b3bdf6a --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2019-12-12-03-18-02.bpo-6699.1CqJFG.rst @@ -0,0 +1 @@ +Warn the user if a file will be overwritten when saving. From ff25dca0e0a8d33e1352029613704103de4f03cf Mon Sep 17 00:00:00 2001 From: Sanyam Kumat <124618873+sanyamk23@users.noreply.github.com> Date: Sun, 7 Jun 2026 03:08:15 +0530 Subject: [PATCH 052/185] gh-148954: Escape methodname in xmlrpc.client.dumps() to prevent XML injection (GH-148968) --- Lib/test/test_xmlrpc.py | 11 +++++++++++ Lib/xmlrpc/client.py | 2 +- .../2026-04-24-19-54-00.gh-issue-148954.v1.rst | 1 + 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2026-04-24-19-54-00.gh-issue-148954.v1.rst diff --git a/Lib/test/test_xmlrpc.py b/Lib/test/test_xmlrpc.py index 2803c6d45c27bfa..ee0e24f6e86ae33 100644 --- a/Lib/test/test_xmlrpc.py +++ b/Lib/test/test_xmlrpc.py @@ -208,6 +208,17 @@ def test_dump_encoding(self): self.assertEqual(xmlrpclib.loads(strg)[0][0], value) self.assertEqual(xmlrpclib.loads(strg)[1], methodname) + def test_dump_escape_methodname(self): + payload = 'foobar' + s = xmlrpclib.dumps((), methodname=payload) + self.assertIn( + 'foo</methodName><injected attr="evil"/>' + '<methodName>bar', s + ) + self.assertNotIn('', s) + load, m = xmlrpclib.loads(s) + self.assertEqual(m, payload) + def test_dump_bytes(self): sample = b"my dog has fleas" self.assertEqual(sample, xmlrpclib.Binary(sample)) diff --git a/Lib/xmlrpc/client.py b/Lib/xmlrpc/client.py index f441376d09c4aa2..84e4e4d11a7319e 100644 --- a/Lib/xmlrpc/client.py +++ b/Lib/xmlrpc/client.py @@ -965,7 +965,7 @@ def dumps(params, methodname=None, methodresponse=None, encoding=None, data = ( xmlheader, "\n" - "", methodname, "\n", + "", escape(methodname), "\n", data, "\n" ) diff --git a/Misc/NEWS.d/next/Library/2026-04-24-19-54-00.gh-issue-148954.v1.rst b/Misc/NEWS.d/next/Library/2026-04-24-19-54-00.gh-issue-148954.v1.rst new file mode 100644 index 000000000000000..6245af7e362e920 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-04-24-19-54-00.gh-issue-148954.v1.rst @@ -0,0 +1 @@ +Fix XML injection vulnerability in :func:`xmlrpc.client.dumps` where the ``methodname`` was not being escaped before interpolation into the XML body. From 74eba4eb6d9230fc88bb42ef91504a32c94e8ff6 Mon Sep 17 00:00:00 2001 From: Omkar Kabde Date: Sun, 7 Jun 2026 17:35:53 +0530 Subject: [PATCH 053/185] gh-150942: Speed up frame local item collection (gh-151002) --- .../2026-06-06-08-20-00.gh-issue-150942.Jk9pQr.rst | 3 +++ Objects/frameobject.c | 9 +++------ 2 files changed, 6 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-06-06-08-20-00.gh-issue-150942.Jk9pQr.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-06-08-20-00.gh-issue-150942.Jk9pQr.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-06-08-20-00.gh-issue-150942.Jk9pQr.rst new file mode 100644 index 000000000000000..9777b8932271404 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-06-08-20-00.gh-issue-150942.Jk9pQr.rst @@ -0,0 +1,3 @@ +Speed up frame local variable item collection by appending result pairs to the +output list without an extra reference-count round-trip (using the internal +reference-stealing list append helper). Patch by Omkar Kabde. diff --git a/Objects/frameobject.c b/Objects/frameobject.c index f60cdb2dd1bf20d..b19889d3034e715 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -9,6 +9,7 @@ #include "pycore_function.h" // _PyFunction_FromConstructor() #include "pycore_genobject.h" // _PyGen_GetGeneratorFromFrame() #include "pycore_interpframe.h" // _PyFrame_GetLocalsArray() +#include "pycore_list.h" // _PyList_AppendTakeRef() #include "pycore_modsupport.h" // _PyArg_CheckPositional() #include "pycore_object.h" // _PyObject_GC_UNTRACK() #include "pycore_opcode_metadata.h" // _PyOpcode_Caches @@ -636,9 +637,7 @@ framelocalsproxy_items(PyObject *self, PyObject *Py_UNUSED(ignored)) goto error; } - int rc = PyList_Append(items, pair); - Py_DECREF(pair); - if (rc < 0) { + if (_PyList_AppendTakeRef((PyListObject *)items, pair) < 0) { goto error; } } @@ -655,9 +654,7 @@ framelocalsproxy_items(PyObject *self, PyObject *Py_UNUSED(ignored)) goto error; } - int rc = PyList_Append(items, pair); - Py_DECREF(pair); - if (rc < 0) { + if (_PyList_AppendTakeRef((PyListObject *)items, pair) < 0) { goto error; } } From a0d73f383c1333a44dc2d89d40fb4dd37eac49e9 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Sun, 7 Jun 2026 14:06:36 +0200 Subject: [PATCH 054/185] gh-150942: Speed up re.findall and re.sub/subn result building (gh-150943) --- .../2026-05-31-12-00-00.gh-issue-150942.Re7Ref.rst | 3 +++ Modules/_sre/sre.c | 13 +++++-------- 2 files changed, 8 insertions(+), 8 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-05-31-12-00-00.gh-issue-150942.Re7Ref.rst diff --git a/Misc/NEWS.d/next/Library/2026-05-31-12-00-00.gh-issue-150942.Re7Ref.rst b/Misc/NEWS.d/next/Library/2026-05-31-12-00-00.gh-issue-150942.Re7Ref.rst new file mode 100644 index 000000000000000..63967108b1e0b3b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-31-12-00-00.gh-issue-150942.Re7Ref.rst @@ -0,0 +1,3 @@ +Speed up :func:`re.findall`, :func:`re.sub` and :func:`re.subn` by appending +result items to the output list without an extra reference-count round-trip +(using the internal reference-stealing list append helper). diff --git a/Modules/_sre/sre.c b/Modules/_sre/sre.c index ee6cb4a371ea505..32aa06bed4a409c 100644 --- a/Modules/_sre/sre.c +++ b/Modules/_sre/sre.c @@ -42,6 +42,7 @@ static const char copyright[] = #include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION #include "pycore_dict.h" // _PyDict_Next() #include "pycore_long.h" // _PyLong_GetZero() +#include "pycore_list.h" // _PyList_AppendTakeRef() #include "pycore_moduleobject.h" // _PyModule_GetState() #include "pycore_tuple.h" // _PyTuple_FromPairSteal #include "pycore_unicodeobject.h" // _PyUnicode_Copy @@ -986,8 +987,7 @@ _sre_SRE_Pattern_findall_impl(PatternObject *self, PyObject *string, break; } - status = PyList_Append(list, item); - Py_DECREF(item); + status = _PyList_AppendTakeRef((PyListObject *)list, item); if (status < 0) goto error; @@ -1333,8 +1333,7 @@ pattern_subx(_sremodulestate* module_state, string, i, b); if (!item) goto error; - status = PyList_Append(list, item); - Py_DECREF(item); + status = _PyList_AppendTakeRef((PyListObject *)list, item); if (status < 0) goto error; @@ -1363,8 +1362,7 @@ pattern_subx(_sremodulestate* module_state, /* add to list */ if (item != Py_None) { - status = PyList_Append(list, item); - Py_DECREF(item); + status = _PyList_AppendTakeRef((PyListObject *)list, item); if (status < 0) goto error; } @@ -1381,8 +1379,7 @@ pattern_subx(_sremodulestate* module_state, string, i, state.endpos); if (!item) goto error; - status = PyList_Append(list, item); - Py_DECREF(item); + status = _PyList_AppendTakeRef((PyListObject *)list, item); if (status < 0) goto error; } From ed142380eeae6e8fd2499b96315262d52f977fa4 Mon Sep 17 00:00:00 2001 From: esadomer <54475303+esadomer@users.noreply.github.com> Date: Sun, 7 Jun 2026 16:01:24 +0300 Subject: [PATCH 055/185] gh-151021: Fix mmap empty searches past the end (GH-151023) --- Lib/test/test_mmap.py | 2 ++ .../Library/2026-06-06-15-20-54.gh-issue-151021.J4qk2A.rst | 3 +++ Modules/mmapmodule.c | 2 -- 3 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-06-06-15-20-54.gh-issue-151021.J4qk2A.rst diff --git a/Lib/test/test_mmap.py b/Lib/test/test_mmap.py index 177fe45e8d97490..2e2ac147968dd4a 100644 --- a/Lib/test/test_mmap.py +++ b/Lib/test/test_mmap.py @@ -354,6 +354,8 @@ def test_find_end(self): self.assertEqual(m.find(b'one', 1, -1), 8) self.assertEqual(m.find(b'one', 1, -2), -1) self.assertEqual(m.find(bytearray(b'one')), 0) + self.assertEqual(m.find(b'', n + 1), -1) + self.assertEqual(m.rfind(b'', n + 1), -1) for i in range(-n-1, n+1): for j in range(-n-1, n+1): diff --git a/Misc/NEWS.d/next/Library/2026-06-06-15-20-54.gh-issue-151021.J4qk2A.rst b/Misc/NEWS.d/next/Library/2026-06-06-15-20-54.gh-issue-151021.J4qk2A.rst new file mode 100644 index 000000000000000..0617fa068c844d6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-06-15-20-54.gh-issue-151021.J4qk2A.rst @@ -0,0 +1,3 @@ +Fix :meth:`mmap.mmap.find` and :meth:`~mmap.mmap.rfind` to return ``-1`` +when searching for an empty subsequence with a start position past the end +of the mapping. diff --git a/Modules/mmapmodule.c b/Modules/mmapmodule.c index a30afe91f8fa171..6fb04ba7bd47c67 100644 --- a/Modules/mmapmodule.c +++ b/Modules/mmapmodule.c @@ -620,8 +620,6 @@ mmap_gfind_lock_held(mmap_object *self, Py_buffer *view, PyObject *start_obj, start += self->size; if (start < 0) start = 0; - else if (start > self->size) - start = self->size; if (end < 0) end += self->size; From 77b5cd375bd74cdca0f63d8e843bd34f80b24833 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bern=C3=A1t=20G=C3=A1bor?= Date: Sun, 7 Jun 2026 07:40:55 -0700 Subject: [PATCH 056/185] gh-150885: Remove unused shutil._ensure_directory (GH-150883) No longer used after gh-146581 (GH-146591). --- Lib/shutil.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Lib/shutil.py b/Lib/shutil.py index 4d5a283662101c4..5095318da233146 100644 --- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -1307,12 +1307,6 @@ def unregister_unpack_format(name): """Removes the pack format from the registry.""" del _UNPACK_FORMATS[name] -def _ensure_directory(path): - """Ensure that the parent directory of `path` exists""" - dirname = os.path.dirname(path) - if not os.path.isdir(dirname): - os.makedirs(dirname) - def _unpack_zipfile(filename, extract_dir): """Unpack zip `filename` to `extract_dir` """ From c6f2b1ecf9ea10a4c1d62d1d2126259f73beb3df Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Sun, 7 Jun 2026 16:19:05 +0100 Subject: [PATCH 057/185] gh-150599: Prevent bz2 decompressor reuse after errors (#150600) --- Lib/test/test_bz2.py | 15 +++++++++++++++ ...6-05-30-09-36-20.gh-issue-150599.nlHqU-.rst | 3 +++ Modules/_bz2module.c | 18 +++++++++++++++--- 3 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Security/2026-05-30-09-36-20.gh-issue-150599.nlHqU-.rst diff --git a/Lib/test/test_bz2.py b/Lib/test/test_bz2.py index d8e3b671ec229f9..64293d757331d75 100644 --- a/Lib/test/test_bz2.py +++ b/Lib/test/test_bz2.py @@ -1032,6 +1032,21 @@ def test_failure(self): # Previously, a second call could crash due to internal inconsistency self.assertRaises(Exception, bzd.decompress, self.BAD_DATA * 30) + def test_decompress_after_data_error(self): + data = bytes.fromhex( + "425a6839314159265359000000000000007fffff000000000000000000000000" + "00000000000000000000000000000000000000e0370000000000000000000000" + "000000000000000000000000000000000000000000000000000083f3" + ) + bzd = BZ2Decompressor() + with self.assertRaisesRegex(OSError, "Invalid data stream"): + bzd.decompress(data) + # Previously, a second call could crash due to internal inconsistency + self.assertFalse(bzd.needs_input) + self.assertFalse(bzd.eof) + with self.assertRaisesRegex(ValueError, "previous error"): + bzd.decompress(b'\x00' * 18) + @support.refcount_test def test_refleaks_in___init__(self): gettotalrefcount = support.get_attribute(sys, 'gettotalrefcount') diff --git a/Misc/NEWS.d/next/Security/2026-05-30-09-36-20.gh-issue-150599.nlHqU-.rst b/Misc/NEWS.d/next/Security/2026-05-30-09-36-20.gh-issue-150599.nlHqU-.rst new file mode 100644 index 000000000000000..a37d86cf423f820 --- /dev/null +++ b/Misc/NEWS.d/next/Security/2026-05-30-09-36-20.gh-issue-150599.nlHqU-.rst @@ -0,0 +1,3 @@ +Fix a possible stack buffer overflow in :mod:`bz2` when a +:class:`bz2.BZ2Decompressor` is reused after a decompression error. +The decompressor now becomes unusable after libbz2 reports an error. diff --git a/Modules/_bz2module.c b/Modules/_bz2module.c index 4cf8beed9ee3eba..9db3ac39da52099 100644 --- a/Modules/_bz2module.c +++ b/Modules/_bz2module.c @@ -108,6 +108,7 @@ typedef struct { typedef struct { PyObject_HEAD bz_stream bzs; + int bzerror; char eof; /* Py_T_BOOL expects a char */ PyObject *unused_data; char needs_input; @@ -435,8 +436,11 @@ decompress_buf(BZ2Decompressor *d, Py_ssize_t max_length) d->bzs_avail_in_real += bzs->avail_in; - if (catch_bz2_error(bzret)) + if (catch_bz2_error(bzret)) { + d->bzerror = bzret; + FT_ATOMIC_STORE_CHAR_RELAXED(d->needs_input, 0); goto error; + } if (bzret == BZ_STREAM_END) { FT_ATOMIC_STORE_CHAR_RELAXED(d->eof, 1); break; @@ -607,10 +611,17 @@ _bz2_BZ2Decompressor_decompress_impl(BZ2Decompressor *self, Py_buffer *data, PyObject *result = NULL; PyMutex_Lock(&self->mutex); - if (self->eof) + if (self->eof) { PyErr_SetString(PyExc_EOFError, "End of stream already reached"); - else + } + else if (self->bzerror) { + // Re-entering BZ2_bzDecompress() after an error can write out of bounds. + PyErr_SetString(PyExc_ValueError, + "Decompressor is unusable after a previous error"); + } + else { result = decompress(self, data->buf, data->len, max_length); + } PyMutex_Unlock(&self->mutex); return result; } @@ -638,6 +649,7 @@ _bz2_BZ2Decompressor_impl(PyTypeObject *type) } self->mutex = (PyMutex){0}; + self->bzerror = 0; self->needs_input = 1; self->bzs_avail_in_real = 0; self->input_buffer = NULL; From 022667aa7ff10bc4969d70e480a84c1c0a341952 Mon Sep 17 00:00:00 2001 From: saber-bit Date: Sun, 7 Jun 2026 12:47:50 -0400 Subject: [PATCH 058/185] Update an error message in the 'Functional Programming HOWTO' (#151047) --- Doc/howto/functional.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/howto/functional.rst b/Doc/howto/functional.rst index 552514063c95ab2..ebc7a100d91a646 100644 --- a/Doc/howto/functional.rst +++ b/Doc/howto/functional.rst @@ -1042,7 +1042,7 @@ first calculation. :: >>> functools.reduce(operator.concat, []) Traceback (most recent call last): ... - TypeError: reduce() of empty sequence with no initial value + TypeError: reduce() of empty iterable with no initial value >>> functools.reduce(operator.mul, [1, 2, 3], 1) 6 >>> functools.reduce(operator.mul, [], 1) From 7c7e3da2de80d5c9eb8c79b96e753e44d2f2b711 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=8D=E3=82=87=E3=81=86=E3=81=99=E3=81=91?= Date: Mon, 8 Jun 2026 03:25:50 +0900 Subject: [PATCH 059/185] gh-69619: Clarify "whitespace" in the `str.{l,r}strip()` documentation (#150901) 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> --- Doc/library/stdtypes.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index f770809dfb4006c..693bb199cbec69e 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -2376,7 +2376,8 @@ expression support in the :mod:`re` module). Return a copy of the string with leading characters removed. The *chars* argument is a string specifying the set of characters to be removed. If omitted - or ``None``, the *chars* argument defaults to removing whitespace. The *chars* + or ``None``, the *chars* argument defaults to removing whitespace, that is + characters for which :meth:`str.isspace` is true. The *chars* argument is not a prefix; rather, all combinations of its values are stripped:: >>> ' spacious '.lstrip() @@ -2579,7 +2580,8 @@ expression support in the :mod:`re` module). Return a copy of the string with trailing characters removed. The *chars* argument is a string specifying the set of characters to be removed. If omitted - or ``None``, the *chars* argument defaults to removing whitespace. The *chars* + or ``None``, the *chars* argument defaults to removing whitespace, that is + characters for which :meth:`str.isspace` is true. The *chars* argument is not a suffix; rather, all combinations of its values are stripped. For example: @@ -2755,11 +2757,9 @@ expression support in the :mod:`re` module). Return a copy of the string with the leading and trailing characters removed. The *chars* argument is a string specifying the set of characters to be removed. - If omitted or ``None``, the *chars* argument defaults to removing whitespace. - The *chars* argument is not a prefix or suffix; rather, all combinations of its - values are stripped. - - Whitespace characters are defined by :meth:`str.isspace`. + If omitted or ``None``, the *chars* argument defaults to removing whitespace, + that is characters for which :meth:`str.isspace` is true. The *chars* argument + is not a prefix or suffix; rather, all combinations of its values are stripped. For example: From a540b7a44032686406d50d38b59a817788bb6c1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20L=C3=B3pez=20G=C3=B3mez?= Date: Mon, 8 Jun 2026 11:39:03 +0200 Subject: [PATCH 060/185] Docs: Fix missing colon in `bisect` example function (GH-151061) --- Doc/library/bisect.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/bisect.rst b/Doc/library/bisect.rst index 84c009907ed9a3c..39ab75c0904df9c 100644 --- a/Doc/library/bisect.rst +++ b/Doc/library/bisect.rst @@ -200,7 +200,7 @@ example uses :py:func:`~bisect.bisect` to look up a letter grade for an exam sco based on a set of ordered numeric breakpoints: 90 and up is an 'A', 80 to 89 is a 'B', and so on:: - >>> def grade(score) + >>> def grade(score): ... i = bisect([60, 70, 80, 90], score) ... return "FDCBA"[i] ... From 90e9443c1d5e2b095487d6fb79df36498c556821 Mon Sep 17 00:00:00 2001 From: Christian Zinck Date: Mon, 8 Jun 2026 06:24:26 -0400 Subject: [PATCH 061/185] gh-101100: Fix Sphinx warnings in 'Buffer Object Structures' documentation (#151058) --- Doc/c-api/typeobj.rst | 10 +++++----- Doc/tools/.nitignore | 1 - 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index 38db69e5c6db96d..dcc9e243c2f3147 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -2975,13 +2975,13 @@ Buffer Object Structures steps: (1) Check if the request can be met. If not, raise :exc:`BufferError`, - set :c:expr:`view->obj` to ``NULL`` and return ``-1``. + set ``view->obj`` to ``NULL`` and return ``-1``. (2) Fill in the requested fields. (3) Increment an internal counter for the number of exports. - (4) Set :c:expr:`view->obj` to *exporter* and increment :c:expr:`view->obj`. + (4) Set ``view->obj`` to *exporter* and increment ``view->obj``. (5) Return ``0``. @@ -3007,10 +3007,10 @@ Buffer Object Structures schemes can be used: * Re-export: Each member of the tree acts as the exporting object and - sets :c:expr:`view->obj` to a new reference to itself. + sets ``view->obj`` to a new reference to itself. * Redirect: The buffer request is redirected to the root object of the - tree. Here, :c:expr:`view->obj` will be a new reference to the root + tree. Here, ``view->obj`` will be a new reference to the root object. The individual fields of *view* are described in section @@ -3064,7 +3064,7 @@ Buffer Object Structures *view* argument. - This function MUST NOT decrement :c:expr:`view->obj`, since that is + This function MUST NOT decrement ``view->obj``, since that is done automatically in :c:func:`PyBuffer_Release` (this scheme is useful for breaking reference cycles). diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 189173a5f8a75f4..31173134dd5019f 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -5,7 +5,6 @@ Doc/c-api/init_config.rst Doc/c-api/intro.rst Doc/c-api/stable.rst -Doc/c-api/typeobj.rst Doc/library/ast.rst Doc/library/asyncio-extending.rst Doc/library/email.charset.rst From 37f93537e0c1b1b1948a1f9aef2af0cfaf7450e3 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Mon, 8 Jun 2026 13:11:36 +0200 Subject: [PATCH 062/185] gh-151059: [perf] Use `PyObject_CallMethodOneArg` in datetime's `call_tzinfo_method` (#151062) --- .../internal/pycore_global_objects_fini_generated.h | 1 + Include/internal/pycore_global_strings.h | 1 + Include/internal/pycore_runtime_init_generated.h | 1 + Include/internal/pycore_unicodeobject_generated.h | 4 ++++ Modules/_datetimemodule.c | 10 +++++----- 5 files changed, 12 insertions(+), 5 deletions(-) diff --git a/Include/internal/pycore_global_objects_fini_generated.h b/Include/internal/pycore_global_objects_fini_generated.h index 99a1ffb8ad5229b..1cf766ddb382dd2 100644 --- a/Include/internal/pycore_global_objects_fini_generated.h +++ b/Include/internal/pycore_global_objects_fini_generated.h @@ -2153,6 +2153,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(updates)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(uri)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(usedforsecurity)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(utcoffset)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(value)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(values)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(version)); diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h index d5818402a508cb9..017d62e002fdff9 100644 --- a/Include/internal/pycore_global_strings.h +++ b/Include/internal/pycore_global_strings.h @@ -876,6 +876,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(updates) STRUCT_FOR_ID(uri) STRUCT_FOR_ID(usedforsecurity) + STRUCT_FOR_ID(utcoffset) STRUCT_FOR_ID(value) STRUCT_FOR_ID(values) STRUCT_FOR_ID(version) diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h index 8227f3fa9eedcf5..75273243ef6df06 100644 --- a/Include/internal/pycore_runtime_init_generated.h +++ b/Include/internal/pycore_runtime_init_generated.h @@ -2151,6 +2151,7 @@ extern "C" { INIT_ID(updates), \ INIT_ID(uri), \ INIT_ID(usedforsecurity), \ + INIT_ID(utcoffset), \ INIT_ID(value), \ INIT_ID(values), \ INIT_ID(version), \ diff --git a/Include/internal/pycore_unicodeobject_generated.h b/Include/internal/pycore_unicodeobject_generated.h index cb731e9a6888781..164d9d412ef5e95 100644 --- a/Include/internal/pycore_unicodeobject_generated.h +++ b/Include/internal/pycore_unicodeobject_generated.h @@ -3284,6 +3284,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(utcoffset); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(value); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 59af7afcfcc644e..30317ea823ff7ee 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -1509,7 +1509,7 @@ get_tzinfo_member(PyObject *self) * this returns NULL. Else result is returned. */ static PyObject * -call_tzinfo_method(PyObject *tzinfo, const char *name, PyObject *tzinfoarg) +call_tzinfo_method(PyObject *tzinfo, PyObject *name, PyObject *tzinfoarg) { PyObject *offset; @@ -1519,7 +1519,7 @@ call_tzinfo_method(PyObject *tzinfo, const char *name, PyObject *tzinfoarg) if (tzinfo == Py_None) Py_RETURN_NONE; - offset = PyObject_CallMethod(tzinfo, name, "O", tzinfoarg); + offset = PyObject_CallMethodOneArg(tzinfo, name, tzinfoarg); if (offset == Py_None || offset == NULL) return offset; if (PyDelta_Check(offset)) { @@ -1536,7 +1536,7 @@ call_tzinfo_method(PyObject *tzinfo, const char *name, PyObject *tzinfoarg) } else { PyErr_Format(PyExc_TypeError, - "tzinfo.%s() must return None or " + "tzinfo.%U() must return None or " "timedelta, not '%.200s'", name, Py_TYPE(offset)->tp_name); Py_DECREF(offset); @@ -1557,7 +1557,7 @@ call_tzinfo_method(PyObject *tzinfo, const char *name, PyObject *tzinfoarg) static PyObject * call_utcoffset(PyObject *tzinfo, PyObject *tzinfoarg) { - return call_tzinfo_method(tzinfo, "utcoffset", tzinfoarg); + return call_tzinfo_method(tzinfo, &_Py_ID(utcoffset), tzinfoarg); } /* Call tzinfo.dst(tzinfoarg), and extract an integer from the @@ -1571,7 +1571,7 @@ call_utcoffset(PyObject *tzinfo, PyObject *tzinfoarg) static PyObject * call_dst(PyObject *tzinfo, PyObject *tzinfoarg) { - return call_tzinfo_method(tzinfo, "dst", tzinfoarg); + return call_tzinfo_method(tzinfo, &_Py_ID(dst), tzinfoarg); } /* Call tzinfo.tzname(tzinfoarg), and return the result. tzinfo must be From 4e40f6243e52d835635c00924d278c917c8e1592 Mon Sep 17 00:00:00 2001 From: Jonathan Dung Date: Mon, 8 Jun 2026 19:39:41 +0800 Subject: [PATCH 063/185] Mention `frozendict` in `object.__hash__()` documentation (#148867) --- Doc/reference/datamodel.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 8d1704a448e6394..2a961a062780f46 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -2230,12 +2230,12 @@ Basic customization pair: built-in function; hash Called by built-in function :func:`hash` and for operations on members of - hashed collections including :class:`set`, :class:`frozenset`, and - :class:`dict`. The ``__hash__()`` method should return an integer. The only required - property is that objects which compare equal have the same hash value; it is - advised to mix together the hash values of the components of the object that - also play a part in comparison of objects by packing them into a tuple and - hashing the tuple. Example:: + hashed collections including :class:`set`, :class:`frozenset`, :class:`dict`, + and :class:`frozendict`. The ``__hash__()`` method should return an integer. + The only required property is that objects which compare equal have the same + hash value; it is advised to mix together the hash values of the components + of the object that also play a part in comparison of objects by packing them + into a tuple and hashing the tuple. Example:: def __hash__(self): return hash((self.name, self.nick, self.color)) From bd13b769efac2ee29103897369107313fe11a65d Mon Sep 17 00:00:00 2001 From: Doron Behar Date: Mon, 8 Jun 2026 15:12:48 +0300 Subject: [PATCH 064/185] gh-141623: operator.rst: don't use term "bitwise" (GH-141846) --- Doc/library/operator.rst | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/Doc/library/operator.rst b/Doc/library/operator.rst index c0dab83977e427f..3d1c8cda13be381 100644 --- a/Doc/library/operator.rst +++ b/Doc/library/operator.rst @@ -110,7 +110,7 @@ The mathematical and bitwise operations are the most numerous: .. function:: and_(a, b) __and__(a, b) - Return the bitwise and of *a* and *b*. + Return ``a & b``. .. function:: floordiv(a, b) @@ -134,13 +134,13 @@ The mathematical and bitwise operations are the most numerous: __inv__(obj) __invert__(obj) - Return the bitwise inverse of the number *obj*. This is equivalent to ``~obj``. + Return ``~obj``. .. function:: lshift(a, b) __lshift__(a, b) - Return *a* shifted left by *b*. + Return ``a << b``. .. function:: mod(a, b) @@ -152,7 +152,7 @@ The mathematical and bitwise operations are the most numerous: .. function:: mul(a, b) __mul__(a, b) - Return ``a * b``, for *a* and *b* numbers. + Return ``a * b``. .. function:: matmul(a, b) @@ -172,25 +172,25 @@ The mathematical and bitwise operations are the most numerous: .. function:: or_(a, b) __or__(a, b) - Return the bitwise or of *a* and *b*. + Return ``a | b``. .. function:: pos(obj) __pos__(obj) - Return *obj* positive (``+obj``). + Return ``+obj``. .. function:: pow(a, b) __pow__(a, b) - Return ``a ** b``, for *a* and *b* numbers. + Return ``a ** b``. .. function:: rshift(a, b) __rshift__(a, b) - Return *a* shifted right by *b*. + Return ``a >> b``. .. function:: sub(a, b) @@ -209,7 +209,7 @@ The mathematical and bitwise operations are the most numerous: .. function:: xor(a, b) __xor__(a, b) - Return the bitwise exclusive or of *a* and *b*. + Return ``a ^ b``. Operations which work with sequences (some of them with mappings too) include: @@ -403,13 +403,18 @@ Python syntax and the functions in the :mod:`!operator` module. +-----------------------+-------------------------+---------------------------------------+ | Division | ``a // b`` | ``floordiv(a, b)`` | +-----------------------+-------------------------+---------------------------------------+ -| Bitwise And | ``a & b`` | ``and_(a, b)`` | +| Bitwise And, or | ``a & b`` | ``and_(a, b)`` | +| Intersection | | | +-----------------------+-------------------------+---------------------------------------+ -| Bitwise Exclusive Or | ``a ^ b`` | ``xor(a, b)`` | +| Bitwise Exclusive Or, | ``a ^ b`` | ``xor(a, b)`` | +| or Symmetric | | | +| Difference | | | +-----------------------+-------------------------+---------------------------------------+ -| Bitwise Inversion | ``~ a`` | ``invert(a)`` | +| Bitwise Inversion, or | ``~ a`` | ``invert(a)`` | +| Complement | | | +-----------------------+-------------------------+---------------------------------------+ -| Bitwise Or | ``a | b`` | ``or_(a, b)`` | +| Bitwise Or, or | ``a | b`` | ``or_(a, b)`` | +| Union | | | +-----------------------+-------------------------+---------------------------------------+ | Exponentiation | ``a ** b`` | ``pow(a, b)`` | +-----------------------+-------------------------+---------------------------------------+ From 70a3825613393ddb6505c404d78a02a80cc18fa2 Mon Sep 17 00:00:00 2001 From: Adorilson Bezerra Date: Mon, 8 Jun 2026 13:51:35 +0100 Subject: [PATCH 065/185] gh-106318: Add examples to the `str.isdigit()` method docs (#144721) Co-authored-by: Stan Ulbrych --- Doc/library/stdtypes.rst | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 693bb199cbec69e..a47e1ffb1a6afb1 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -2174,9 +2174,25 @@ expression support in the :mod:`re` module). character, ``False`` otherwise. Digits include decimal characters and digits that need special handling, such as the compatibility superscript digits. This covers digits which cannot be used to form numbers in base 10, - like the Kharosthi numbers. Formally, a digit is a character that has the + like the `Kharosthi numbers `__. + Formally, a digit is a character that has the property value Numeric_Type=Digit or Numeric_Type=Decimal. + For example: + + .. doctest:: + + >>> '0123456789'.isdigit() + True + >>> '٠١٢٣٤٥٦٧٨٩'.isdigit() # Arabic-Indic digits zero to nine + True + >>> '⅕'.isdigit() # Vulgar fraction one fifth + False + >>> '²'.isdecimal(), '²'.isdigit(), '²'.isnumeric() + (False, True, True) + + See also :meth:`isdecimal` and :meth:`isnumeric`. + .. method:: str.isidentifier() @@ -2217,15 +2233,14 @@ expression support in the :mod:`re` module). >>> '0123456789'.isnumeric() True - >>> '٠١٢٣٤٥٦٧٨٩'.isnumeric() # Arabic-indic digit zero to nine + >>> '٠١٢٣٤٥٦٧٨٩'.isnumeric() # Arabic-Indic digits zero to nine True >>> '⅕'.isnumeric() # Vulgar fraction one fifth True >>> '²'.isdecimal(), '²'.isdigit(), '²'.isnumeric() (False, True, True) - See also :meth:`isdecimal` and :meth:`isdigit`. Numeric characters are - a superset of decimal numbers. + See also :meth:`isdecimal` and :meth:`isdigit`. .. method:: str.isprintable() From af823218752a811da3bdb8ad4f51d237973dee34 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Mon, 8 Jun 2026 16:09:20 +0100 Subject: [PATCH 066/185] Fix a typo in the `SECURITY.md` (#151086) --- .github/SECURITY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/SECURITY.md b/.github/SECURITY.md index 6a8d4244d079897..5bf4b40947bea70 100644 --- a/.github/SECURITY.md +++ b/.github/SECURITY.md @@ -1,7 +1,7 @@ # Security Policy Python [provides a security policy and threat model](https://devguide.python.org/security/policy/) -in the Python Development Guide documenting what bugs are vulnerabilities, +in the Python Developer's Guide documenting what bugs are vulnerabilities, how to structure reports, and what versions of Python accept reports. Python Security Response Team (PSRT) members From a06d90a4bfe2d29920ca827e1dae4fd1069c70bf Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 8 Jun 2026 18:57:07 +0200 Subject: [PATCH 067/185] gh-151019: Fix test_os on 32-bit FreeBSD (#151087) Remove references to server.handler_instance. This attribute has been removed in 2022 by commit 3ae975f1ac880c47d51cca6c9e305547bd365be7. --- Lib/test/test_os/test_os.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/Lib/test/test_os/test_os.py b/Lib/test/test_os/test_os.py index 68cb32cd40be306..b88388e091312a0 100644 --- a/Lib/test/test_os/test_os.py +++ b/Lib/test/test_os/test_os.py @@ -3793,7 +3793,6 @@ async def test_trailers(self): @requires_headers_trailers @requires_32b async def test_headers_overflow_32bits(self): - self.server.handler_instance.accumulate = False with self.assertRaises(OSError) as cm: await self.async_sendfile(self.sockno, self.fileno, 0, 0, headers=[b"x" * 2**16] * 2**15) @@ -3802,7 +3801,6 @@ async def test_headers_overflow_32bits(self): @requires_headers_trailers @requires_32b async def test_trailers_overflow_32bits(self): - self.server.handler_instance.accumulate = False with self.assertRaises(OSError) as cm: await self.async_sendfile(self.sockno, self.fileno, 0, 0, trailers=[b"x" * 2**16] * 2**15) From ddfd9dc36c2f95f2ce8bec62dd8d0e7f32c95cf5 Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Mon, 8 Jun 2026 10:25:43 -0700 Subject: [PATCH 068/185] gh-148932: Fix `profiling.sampling` on Windows virtual environments (#150541) --- Doc/library/profiling.sampling.rst | 5 --- Lib/profiling/sampling/sample.py | 35 ++++++++++++++++--- ...-05-27-23-47-31.gh-issue-148932.Y1xmvA.rst | 1 + 3 files changed, 31 insertions(+), 10 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-05-27-23-47-31.gh-issue-148932.Y1xmvA.rst diff --git a/Doc/library/profiling.sampling.rst b/Doc/library/profiling.sampling.rst index 39b6ea4e31cde72..aeb1a429b58515a 100644 --- a/Doc/library/profiling.sampling.rst +++ b/Doc/library/profiling.sampling.rst @@ -387,11 +387,6 @@ This requires one of: On Windows, the profiler requires administrative privileges or the ``SeDebugPrivilege`` privilege to read another process's memory. -*Note*: On Windows, ``python -m profiling.sampling`` fails inside a virtual -environment because the venv's ``python.exe`` is just a launcher shim that -re-executes the base interpreter as a child process. The shim itself isn't -a Python process and has no ``PyRuntime`` section to attach to. Instead, -run it from the global Python installation. Version compatibility --------------------- diff --git a/Lib/profiling/sampling/sample.py b/Lib/profiling/sampling/sample.py index 2d379e1e16a35e3..50ccc57566d70d3 100644 --- a/Lib/profiling/sampling/sample.py +++ b/Lib/profiling/sampling/sample.py @@ -50,9 +50,38 @@ def _pause_threads(unwinder, blocking): # Maximum number of consecutive identical samples to keep before flushing. MAX_PENDING_SAMPLES = 8192 + +def _resolve_python_pid(pid): + """On Windows, if pid is a venvlauncher process, return the child Python PID. + + The venvlauncher (used as python.exe in venvs) spawns the real Python + interpreter as a child process via CreateProcessW. The RemoteUnwinder + needs the child's PID, not the launcher's. + + Returns the original pid if not on Windows, not a venv launcher, + or no child process is found. + """ + if os.name != "nt" or sys.prefix == sys.base_prefix: + return pid + try: + children = _remote_debugging.get_child_pids(pid, recursive=False) + python_children = [ + child for child in children + if _remote_debugging.is_python_process(child) + ] + if len(python_children) == 1: + return python_children[0] + except (OSError, RuntimeError) as err: + raise SystemExit( + f"Failed to initialize profiler from virtualenv: {err}\n" + f"Try running with the base interpreter: {sys._base_executable}" + ) from err + return pid + + class SampleProfiler: def __init__(self, pid, sample_interval_usec, all_threads, *, mode=PROFILING_MODE_WALL, native=False, gc=True, opcodes=False, skip_non_matching_threads=True, collect_stats=False, blocking=False): - self.pid = pid + self.pid = _resolve_python_pid(pid) self.sample_interval_usec = sample_interval_usec self.all_threads = all_threads self.mode = mode # Store mode for later use @@ -61,10 +90,6 @@ def __init__(self, pid, sample_interval_usec, all_threads, *, mode=PROFILING_MOD try: self.unwinder = self._new_unwinder(native, gc, opcodes, skip_non_matching_threads) except RuntimeError as err: - if os.name == "nt" and sys.executable.endswith("python.exe"): - raise SystemExit( - "Running profiling.sampling from virtualenv on Windows platform is not supported" - ) from err raise SystemExit(err) from err # Track sample intervals and total sample count self.sample_intervals = deque(maxlen=100) diff --git a/Misc/NEWS.d/next/Library/2026-05-27-23-47-31.gh-issue-148932.Y1xmvA.rst b/Misc/NEWS.d/next/Library/2026-05-27-23-47-31.gh-issue-148932.Y1xmvA.rst new file mode 100644 index 000000000000000..a0b7a9740cd518d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-27-23-47-31.gh-issue-148932.Y1xmvA.rst @@ -0,0 +1 @@ +Fix ``profiling.sampling`` on Windows virtual environments to resolve the actual Python PID from a virtual environment shim. From ae6200709c94b0c84a972af09bb70e2fa4d5d554 Mon Sep 17 00:00:00 2001 From: Thomas Kowalski Date: Mon, 8 Jun 2026 20:31:54 +0200 Subject: [PATCH 069/185] gh-150633: Properly handle null characters in the name when importing frozen modules (GH-150634) --- Lib/test/test_import/__init__.py | 9 +++++++++ .../2026-05-30-20-19-35.gh-issue-150633.XkNul0.rst | 3 +++ Python/import.c | 2 +- 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-05-30-20-19-35.gh-issue-150633.XkNul0.rst diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py index c905c0da0a12327..f8e77fc7c532c4b 100644 --- a/Lib/test/test_import/__init__.py +++ b/Lib/test/test_import/__init__.py @@ -364,6 +364,15 @@ def test_import_raises_ModuleNotFoundError(self): with self.assertRaises(ModuleNotFoundError): import something_that_should_not_exist_anywhere + def test_import_null_byte_in_name_raises_ModuleNotFoundError(self): + # gh-150633: module names containing null bytes should not + # lead to duplicates in sys.modules + before = set(sys.modules.keys()) + with self.assertRaises(ModuleNotFoundError): + __import__('zipimport\x00junk') + + self.assertEqual(set(sys.modules.keys()), before) + def test_from_import_missing_module_raises_ModuleNotFoundError(self): with self.assertRaises(ModuleNotFoundError): from something_that_should_not_exist_anywhere import blah diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-05-30-20-19-35.gh-issue-150633.XkNul0.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-30-20-19-35.gh-issue-150633.XkNul0.rst new file mode 100644 index 000000000000000..c397ad61f086c1b --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-30-20-19-35.gh-issue-150633.XkNul0.rst @@ -0,0 +1,3 @@ +Fix the frozen importer accepting module names with embedded null bytes, which +caused it to bypass the :data:`sys.modules` cache and create duplicate module +objects. diff --git a/Python/import.c b/Python/import.c index 82d15ad0683c190..42bfe15121f84f7 100644 --- a/Python/import.c +++ b/Python/import.c @@ -3174,7 +3174,7 @@ find_frozen(PyObject *nameobj, struct frozen_info *info) if (nameobj == NULL || nameobj == Py_None) { return FROZEN_BAD_NAME; } - const char *name = PyUnicode_AsUTF8(nameobj); + const char *name = _PyUnicode_AsUTF8NoNUL(nameobj); if (name == NULL) { // Note that this function previously used // _PyUnicode_EqualToASCIIString(). We clear the error here From e40cdf25058b130afa67de05aa3eea1b530e33f0 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Mon, 8 Jun 2026 12:21:00 -0700 Subject: [PATCH 070/185] gh-150633: Minor improvement of a newly added test (#151103) Minor improvement of a newly added test. --- Lib/test/test_import/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py index f8e77fc7c532c4b..9f3df8010d32339 100644 --- a/Lib/test/test_import/__init__.py +++ b/Lib/test/test_import/__init__.py @@ -367,11 +367,11 @@ def test_import_raises_ModuleNotFoundError(self): def test_import_null_byte_in_name_raises_ModuleNotFoundError(self): # gh-150633: module names containing null bytes should not # lead to duplicates in sys.modules - before = set(sys.modules.keys()) + before = set(sys.modules) with self.assertRaises(ModuleNotFoundError): __import__('zipimport\x00junk') - self.assertEqual(set(sys.modules.keys()), before) + self.assertEqual(set(sys.modules), before) def test_from_import_missing_module_raises_ModuleNotFoundError(self): with self.assertRaises(ModuleNotFoundError): From f46de357631d25bd9e4960b18cb5f902db4f97e0 Mon Sep 17 00:00:00 2001 From: Arshal Aromal Date: Tue, 9 Jun 2026 01:08:14 +0530 Subject: [PATCH 071/185] gh-151070: Fix class referencing typo in collections.abc docs (GH-151088) --- Doc/library/collections.abc.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/collections.abc.rst b/Doc/library/collections.abc.rst index 51853725b1b297c..10e3790717ed6ed 100644 --- a/Doc/library/collections.abc.rst +++ b/Doc/library/collections.abc.rst @@ -456,7 +456,7 @@ Notes on using :class:`Set` and :class:`MutableSet` as a mixin: The :class:`Set` mixin provides a :meth:`!_hash` method to compute a hash value for the set; however, :meth:`~object.__hash__` is not defined because not all sets are :term:`hashable` or immutable. To add set hashability using mixins, - inherit from both :meth:`Set` and :meth:`Hashable`, then define + inherit from both :class:`Set` and :class:`Hashable`, then define ``__hash__ = Set._hash``. .. seealso:: From 90e9b9244147bf710a770a829338808a091ce58d Mon Sep 17 00:00:00 2001 From: sobolevn Date: Tue, 9 Jun 2026 13:03:33 +0300 Subject: [PATCH 072/185] gh-151126: Fix missing `PyErr_NoMemory()` in `remove_unused_consts` (#151127) --- .../2026-06-09-10-28-30.gh-issue-151126.DKa6Sl.rst | 3 +++ Python/flowgraph.c | 2 ++ 2 files changed, 5 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-06-09-10-28-30.gh-issue-151126.DKa6Sl.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-09-10-28-30.gh-issue-151126.DKa6Sl.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-09-10-28-30.gh-issue-151126.DKa6Sl.rst new file mode 100644 index 000000000000000..3f699a50d7a42bb --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-09-10-28-30.gh-issue-151126.DKa6Sl.rst @@ -0,0 +1,3 @@ +Fix a crash, when there's no memory left on a device, +which happened in code compilation. +Now it raises a proper :exc:`MemoryError`. diff --git a/Python/flowgraph.c b/Python/flowgraph.c index eb0faf8cd183885..324a4fc1f10a68e 100644 --- a/Python/flowgraph.c +++ b/Python/flowgraph.c @@ -3279,6 +3279,7 @@ remove_unused_consts(basicblock *entryblock, PyObject *consts) index_map = PyMem_Malloc(nconsts * sizeof(Py_ssize_t)); if (index_map == NULL) { + PyErr_NoMemory(); goto end; } for (Py_ssize_t i = 1; i < nconsts; i++) { @@ -3331,6 +3332,7 @@ remove_unused_consts(basicblock *entryblock, PyObject *consts) /* adjust const indices in the bytecode */ reverse_index_map = PyMem_Malloc(nconsts * sizeof(Py_ssize_t)); if (reverse_index_map == NULL) { + PyErr_NoMemory(); goto end; } for (Py_ssize_t i = 0; i < nconsts; i++) { From c24e0bb81623b6ecf781fbeca4ae7b1fd167f791 Mon Sep 17 00:00:00 2001 From: Cody Maloney Date: Tue, 9 Jun 2026 03:31:44 -0700 Subject: [PATCH 073/185] gh-143008: Fix Null pointer dereferences in TextIOWrapper underlying stream access (#145957) TextIOWrapper keeps its underlying stream in a member called `self->buffer`. That stream can be detached by user code, such as custom `.flush` implementations resulting in `self->buffer` being set to NULL. The implementation often checked at the start of functions if `self->buffer` is in a good state, but did not always recheck after other Python code was called which could modify `self->buffer`. The cases which need to be re-checked are hard to spot so rather than rely on reviewer effort create better safety by making all self->buffer access go through helper functions. Thank you yihong0618 for the test, NEWS and initial implementation in gh-143041. Co-authored-by: yihong0618 Co-authored-by: Victor Stinner --- Lib/test/test_io/test_textio.py | 50 ++++++ ...-12-21-17-56-37.gh-issue-143008.aakErJ.rst | 2 + ...-06-04-18-22-56.gh-issue-143008.z5tw-J.rst | 1 + Modules/_io/clinic/textio.c.h | 4 +- Modules/_io/textio.c | 157 +++++++++++++----- 5 files changed, 176 insertions(+), 38 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-12-21-17-56-37.gh-issue-143008.aakErJ.rst create mode 100644 Misc/NEWS.d/next/Library/2026-06-04-18-22-56.gh-issue-143008.z5tw-J.rst diff --git a/Lib/test/test_io/test_textio.py b/Lib/test/test_io/test_textio.py index d725f9212ceaaef..82096ab09873955 100644 --- a/Lib/test/test_io/test_textio.py +++ b/Lib/test/test_io/test_textio.py @@ -1560,6 +1560,56 @@ def closed(self): wrapper = self.TextIOWrapper(raw) wrapper.close() # should not crash + def test_reentrant_detach_during_flush(self): + # gh-143008: Reentrant detach() during flush should not crash. + + class DetachOnce(self.BufferedRandom): + wrapper = None + + def detach_once(self): + original = self.wrapper + self.wrapper = None + if original is not None: + original.detach() + original.flush() + + class DetachOnFlush(DetachOnce): + def flush(self): + self.detach_once() + + class DetachOnWrite(DetachOnce): + def write(self, b): + self.detach_once() + return len(b) + + # Separate reference for after detach_once. + wrapper = None + + def make_text(buffer): + nonlocal wrapper + buffer.wrapper = self.TextIOWrapper(buffer, encoding='utf-8') + wrapper = buffer.wrapper + + # Many calls could result in the same null self->buffer crash. + tests = [ + ('truncate', lambda: wrapper.truncate(0)), + ('close', lambda: wrapper.close()), + ('detach', lambda: wrapper.detach()), + ('seek', lambda: wrapper.seek(0)), + ('tell', lambda: wrapper.tell()), + ('reconfigure', lambda: wrapper.reconfigure(line_buffering=True)), + ] + for name, method in tests: + with self.subTest(name): + make_text(DetachOnFlush(self.MockRawIO())) + self.assertRaisesRegex(ValueError, "detached", method) + + # Should not crash. + with self.subTest('read via writeflush'): + make_text(DetachOnWrite(self.MockRawIO())) + wrapper.write('x') + self.assertRaisesRegex(ValueError, "detached", wrapper.read) + class PyTextIOWrapperTest(TextIOWrapperTest, PyTestCase): shutdown_error = "LookupError: unknown encoding: ascii" diff --git a/Misc/NEWS.d/next/Library/2025-12-21-17-56-37.gh-issue-143008.aakErJ.rst b/Misc/NEWS.d/next/Library/2025-12-21-17-56-37.gh-issue-143008.aakErJ.rst new file mode 100644 index 000000000000000..907cdb770ab65f9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-12-21-17-56-37.gh-issue-143008.aakErJ.rst @@ -0,0 +1,2 @@ +Fix crash in :class:`io.TextIOWrapper` when reentrant +:meth:`io.TextIOBase.detach` is called reentrantly from the underlying buffer. diff --git a/Misc/NEWS.d/next/Library/2026-06-04-18-22-56.gh-issue-143008.z5tw-J.rst b/Misc/NEWS.d/next/Library/2026-06-04-18-22-56.gh-issue-143008.z5tw-J.rst new file mode 100644 index 000000000000000..e99bc39c45f9b8f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-04-18-22-56.gh-issue-143008.z5tw-J.rst @@ -0,0 +1 @@ +Fix race conditions when re-initializing a :class:`io.TextIOWrapper` object. diff --git a/Modules/_io/clinic/textio.c.h b/Modules/_io/clinic/textio.c.h index 9407076b850cee9..8d59bda5f74b386 100644 --- a/Modules/_io/clinic/textio.c.h +++ b/Modules/_io/clinic/textio.c.h @@ -666,7 +666,9 @@ _io_TextIOWrapper___init__(PyObject *self, PyObject *args, PyObject *kwargs) goto exit; } skip_optional_pos: + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io_TextIOWrapper___init___impl((textio *)self, buffer, encoding, errors, newline, line_buffering, write_through); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -1329,4 +1331,4 @@ _io_TextIOWrapper__CHUNK_SIZE_set(PyObject *self, PyObject *value, void *Py_UNUS return return_value; } -/*[clinic end generated code: output=f900b42090c9781c input=a9049054013a1b77]*/ +/*[clinic end generated code: output=8c571c9dba87d2b1 input=a9049054013a1b77]*/ diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index e80b75066c59a61..24e08cec88f2a3a 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -672,6 +672,8 @@ struct textio int ok; /* initialized? */ int detached; Py_ssize_t chunk_size; + /* Use helpers buffer_*() functions to access buffer; many operations can set it to + NULL (see gh-143008, gh-142594). */ PyObject *buffer; PyObject *encoding; PyObject *encoder; @@ -729,6 +731,67 @@ struct textio #define textio_CAST(op) ((textio *)(op)) +/* Helpers to safely operate on self->buffer. + + self->buffer can be detached (set to NULL) by any user code that is called + leading to NULL pointer dereferences (see gh-143008, gh-142594). Protect + against that by using helpers to check self->buffer validity at callsites. */ +static PyObject * +buffer_access_safe(textio *self) +{ + /* Check self->buffer directly but match errors of CHECK_ATTACHED since this + is called during construction and finalization where self->ok == 0. */ + if (self->buffer == NULL) { + if (self->ok <= 0) { + PyErr_SetString(PyExc_ValueError, + "I/O operation on uninitialized object"); + } + else { + PyErr_SetString(PyExc_ValueError, + "underlying buffer has been detached"); + } + return NULL; + } + + /* Returning a borrowed reference is safe since TextIOWrapper methods are + protected by critical sections. */ + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self); + return self->buffer; +} + +static PyObject * +buffer_getattr(textio *self, PyObject *attr_name) +{ + PyObject *buffer = buffer_access_safe(self); + if (buffer == NULL) { + return NULL; + } + + return PyObject_GetAttr(buffer, attr_name); +} + +static PyObject * +buffer_callmethod_noargs(textio *self, PyObject *name) +{ + PyObject *buffer = buffer_access_safe(self); + if (buffer == NULL) { + return NULL; + } + + return PyObject_CallMethodNoArgs(buffer, name); +} + +static PyObject * +buffer_callmethod_onearg(textio *self, PyObject *name, PyObject *arg) +{ + PyObject *buffer = buffer_access_safe(self); + if (buffer == NULL) { + return NULL; + } + + return PyObject_CallMethodOneArg(buffer, name, arg); +} + static void textiowrapper_set_decoded_chars(textio *self, PyObject *chars); @@ -898,7 +961,7 @@ _textiowrapper_set_decoder(textio *self, PyObject *codec_info, PyObject *res; int r; - res = PyObject_CallMethodNoArgs(self->buffer, &_Py_ID(readable)); + res = buffer_callmethod_noargs(self, &_Py_ID(readable)); if (res == NULL) return -1; @@ -954,7 +1017,7 @@ _textiowrapper_set_encoder(textio *self, PyObject *codec_info, PyObject *res; int r; - res = PyObject_CallMethodNoArgs(self->buffer, &_Py_ID(writable)); + res = buffer_callmethod_noargs(self, &_Py_ID(writable)); if (res == NULL) return -1; @@ -1000,8 +1063,7 @@ _textiowrapper_fix_encoder_state(textio *self) self->encoding_start_of_stream = 1; - PyObject *cookieObj = PyObject_CallMethodNoArgs( - self->buffer, &_Py_ID(tell)); + PyObject *cookieObj = buffer_callmethod_noargs(self, &_Py_ID(tell)); if (cookieObj == NULL) { return -1; } @@ -1061,6 +1123,7 @@ io_check_errors(PyObject *errors) /*[clinic input] +@critical_section _io.TextIOWrapper.__init__ buffer: object encoding: str(accept={str, NoneType}) = None @@ -1104,7 +1167,7 @@ _io_TextIOWrapper___init___impl(textio *self, PyObject *buffer, const char *encoding, PyObject *errors, const char *newline, int line_buffering, int write_through) -/*[clinic end generated code: output=72267c0c01032ed2 input=e6cfaaaf6059d4f5]*/ +/*[clinic end generated code: output=72267c0c01032ed2 input=0f077220214c40a4]*/ { PyObject *raw, *codec_info = NULL; PyObject *res; @@ -1568,11 +1631,14 @@ _io_TextIOWrapper_detach_impl(textio *self) /*[clinic end generated code: output=7ba3715cd032d5f2 input=c908a3b4ef203b0f]*/ { PyObject *buffer; - CHECK_ATTACHED(self); if (_PyFile_Flush((PyObject *)self) < 0) { return NULL; } - buffer = self->buffer; + /* _PyFile_Flush could detach before returning; raise an exception. */ + buffer = buffer_access_safe(self); + if (buffer == NULL) { + return NULL; + } self->buffer = NULL; self->detached = 1; return buffer; @@ -1641,7 +1707,7 @@ _textiowrapper_writeflush(textio *self) PyObject *ret; do { - ret = PyObject_CallMethodOneArg(self->buffer, &_Py_ID(write), b); + ret = buffer_callmethod_onearg(self, &_Py_ID(write), b); } while (ret == NULL && _PyIO_trap_eintr()); Py_DECREF(b); // NOTE: We cleared buffer but we don't know how many bytes are actually written @@ -1787,7 +1853,8 @@ _io_TextIOWrapper_write_impl(textio *self, PyObject *text) } if (needflush) { - if (_PyFile_Flush(self->buffer) < 0) { + PyObject *buffer = buffer_access_safe(self); + if (buffer == NULL || _PyFile_Flush(buffer) < 0) { return NULL; } } @@ -1917,9 +1984,10 @@ textiowrapper_read_chunk(textio *self, Py_ssize_t size_hint) if (chunk_size == NULL) goto fail; - input_chunk = PyObject_CallMethodOneArg(self->buffer, - (self->has_read1 ? &_Py_ID(read1): &_Py_ID(read)), - chunk_size); + input_chunk = buffer_callmethod_onearg(self, + (self->has_read1 ? &_Py_ID(read1) : + &_Py_ID(read)), + chunk_size); Py_DECREF(chunk_size); if (input_chunk == NULL) goto fail; @@ -2003,7 +2071,7 @@ _io_TextIOWrapper_read_impl(textio *self, Py_ssize_t n) if (n < 0) { /* Read everything */ - PyObject *bytes = PyObject_CallMethodNoArgs(self->buffer, &_Py_ID(read)); + PyObject *bytes = buffer_callmethod_noargs(self, &_Py_ID(read)); PyObject *decoded; if (bytes == NULL) goto fail; @@ -2600,7 +2668,11 @@ _io_TextIOWrapper_seek_impl(textio *self, PyObject *cookieObj, int whence) Py_DECREF(res); } - res = _PyObject_CallMethod(self->buffer, &_Py_ID(seek), "ii", 0, 2); + PyObject *buf = buffer_access_safe(self); + if (buf == NULL) { + goto fail; + } + res = _PyObject_CallMethod(buf, &_Py_ID(seek), "ii", 0, 2); Py_CLEAR(cookieObj); if (res == NULL) goto fail; @@ -2648,7 +2720,7 @@ _io_TextIOWrapper_seek_impl(textio *self, PyObject *cookieObj, int whence) posobj = PyLong_FromOff_t(cookie.start_pos); if (posobj == NULL) goto fail; - res = PyObject_CallMethodOneArg(self->buffer, &_Py_ID(seek), posobj); + res = buffer_callmethod_onearg(self, &_Py_ID(seek), posobj); Py_DECREF(posobj); if (res == NULL) goto fail; @@ -2665,8 +2737,15 @@ _io_TextIOWrapper_seek_impl(textio *self, PyObject *cookieObj, int whence) if (cookie.chars_to_skip) { /* Just like _read_chunk, feed the decoder and save a snapshot. */ - PyObject *input_chunk = _PyObject_CallMethod(self->buffer, &_Py_ID(read), - "i", cookie.bytes_to_feed); + PyObject *bytes_to_feed = PyLong_FromLong(cookie.bytes_to_feed); + if (bytes_to_feed == NULL) { + goto fail; + } + PyObject *input_chunk = buffer_callmethod_onearg(self, + &_Py_ID(read), + bytes_to_feed); + Py_DECREF(bytes_to_feed); + PyObject *decoded; if (input_chunk == NULL) @@ -2765,7 +2844,7 @@ _io_TextIOWrapper_tell_impl(textio *self) goto fail; } - posobj = PyObject_CallMethodNoArgs(self->buffer, &_Py_ID(tell)); + posobj = buffer_callmethod_noargs(self, &_Py_ID(tell)); if (posobj == NULL) goto fail; @@ -2975,7 +3054,7 @@ _io_TextIOWrapper_truncate_impl(textio *self, PyObject *pos) return NULL; } - return PyObject_CallMethodOneArg(self->buffer, &_Py_ID(truncate), pos); + return buffer_callmethod_onearg(self, &_Py_ID(truncate), pos); } static PyObject * @@ -3057,8 +3136,7 @@ static PyObject * _io_TextIOWrapper_fileno_impl(textio *self) /*[clinic end generated code: output=21490a4c3da13e6c input=515e1196aceb97ab]*/ { - CHECK_ATTACHED(self); - return PyObject_CallMethodNoArgs(self->buffer, &_Py_ID(fileno)); + return buffer_callmethod_noargs(self, &_Py_ID(fileno)); } /*[clinic input] @@ -3070,8 +3148,7 @@ static PyObject * _io_TextIOWrapper_seekable_impl(textio *self) /*[clinic end generated code: output=ab223dbbcffc0f00 input=71c4c092736c549b]*/ { - CHECK_ATTACHED(self); - return PyObject_CallMethodNoArgs(self->buffer, &_Py_ID(seekable)); + return buffer_callmethod_noargs(self, &_Py_ID(seekable)); } /*[clinic input] @@ -3083,8 +3160,7 @@ static PyObject * _io_TextIOWrapper_readable_impl(textio *self) /*[clinic end generated code: output=72ff7ba289a8a91b input=80438d1f01b0a89b]*/ { - CHECK_ATTACHED(self); - return PyObject_CallMethodNoArgs(self->buffer, &_Py_ID(readable)); + return buffer_callmethod_noargs(self, &_Py_ID(readable)); } /*[clinic input] @@ -3096,8 +3172,7 @@ static PyObject * _io_TextIOWrapper_writable_impl(textio *self) /*[clinic end generated code: output=a728c71790d03200 input=9d6c22befb0c340a]*/ { - CHECK_ATTACHED(self); - return PyObject_CallMethodNoArgs(self->buffer, &_Py_ID(writable)); + return buffer_callmethod_noargs(self, &_Py_ID(writable)); } /*[clinic input] @@ -3109,8 +3184,7 @@ static PyObject * _io_TextIOWrapper_isatty_impl(textio *self) /*[clinic end generated code: output=12be1a35bace882e input=7f83ff04d4d1733d]*/ { - CHECK_ATTACHED(self); - return PyObject_CallMethodNoArgs(self->buffer, &_Py_ID(isatty)); + return buffer_callmethod_noargs(self, &_Py_ID(isatty)); } /*[clinic input] @@ -3127,7 +3201,7 @@ _io_TextIOWrapper_flush_impl(textio *self) self->telling = self->seekable; if (_textiowrapper_writeflush(self) < 0) return NULL; - return PyObject_CallMethodNoArgs(self->buffer, &_Py_ID(flush)); + return buffer_callmethod_noargs(self, &_Py_ID(flush)); } /*[clinic input] @@ -3160,8 +3234,9 @@ _io_TextIOWrapper_close_impl(textio *self) else { PyObject *exc = NULL; if (self->finalizing) { - res = PyObject_CallMethodOneArg(self->buffer, &_Py_ID(_dealloc_warn), - (PyObject *)self); + res = buffer_callmethod_onearg(self, + &_Py_ID(_dealloc_warn), + (PyObject *)self); if (res) { Py_DECREF(res); } @@ -3173,7 +3248,7 @@ _io_TextIOWrapper_close_impl(textio *self) exc = PyErr_GetRaisedException(); } - res = PyObject_CallMethodNoArgs(self->buffer, &_Py_ID(close)); + res = buffer_callmethod_noargs(self, &_Py_ID(close)); if (exc != NULL) { _PyErr_ChainExceptions1(exc); Py_CLEAR(res); @@ -3241,8 +3316,7 @@ static PyObject * _io_TextIOWrapper_name_get_impl(textio *self) /*[clinic end generated code: output=8c2f1d6d8756af40 input=26ecec9b39e30e07]*/ { - CHECK_ATTACHED(self); - return PyObject_GetAttr(self->buffer, &_Py_ID(name)); + return buffer_getattr(self, &_Py_ID(name)); } /*[clinic input] @@ -3255,8 +3329,17 @@ static PyObject * _io_TextIOWrapper_closed_get_impl(textio *self) /*[clinic end generated code: output=b49b68f443a85e3c input=7dfcf43f63c7003d]*/ { - CHECK_ATTACHED(self); - return PyObject_GetAttr(self->buffer, &_Py_ID(closed)); + /* If partially constructed or deconstructed, return that the underlying + buffer is closed. + + The code managing the transition is responsible for closing. The closed + attribute is often called in re-initalization, as part of repr in error + cases, and when the I/O stack is garbage collected. */ + if (self->ok <= 0) { + Py_RETURN_TRUE; + } + + return buffer_getattr(self, &_Py_ID(closed)); } /*[clinic input] From e84e3112230bffecad68d0a6f92acd332a29fe02 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 9 Jun 2026 14:11:17 +0300 Subject: [PATCH 074/185] gh-151130: Add more tests for PyWeakref_* C API (GH-151131) --- Lib/test/test_capi/test_weakref.py | 136 ++++++++++++++++++ ...-06-09-11-52-52.gh-issue-151130.1vslPH.rst | 1 + Modules/Setup.stdlib.in | 4 +- Modules/_testcapi/parts.h | 1 + Modules/_testcapi/weakref.c | 46 ++++++ Modules/_testcapimodule.c | 3 + Modules/_testlimitedcapi.c | 3 + Modules/_testlimitedcapi/parts.h | 1 + Modules/_testlimitedcapi/weakref.c | 78 ++++++++++ PCbuild/_testcapi.vcxproj | 1 + PCbuild/_testcapi.vcxproj.filters | 3 + PCbuild/_testlimitedcapi.vcxproj | 1 + PCbuild/_testlimitedcapi.vcxproj.filters | 1 + 13 files changed, 277 insertions(+), 2 deletions(-) create mode 100644 Lib/test/test_capi/test_weakref.py create mode 100644 Misc/NEWS.d/next/Tests/2026-06-09-11-52-52.gh-issue-151130.1vslPH.rst create mode 100644 Modules/_testcapi/weakref.c create mode 100644 Modules/_testlimitedcapi/weakref.c diff --git a/Lib/test/test_capi/test_weakref.py b/Lib/test/test_capi/test_weakref.py new file mode 100644 index 000000000000000..86ebe92da8d95db --- /dev/null +++ b/Lib/test/test_capi/test_weakref.py @@ -0,0 +1,136 @@ +import weakref +import unittest +from test.support import import_helper + +_testcapi = import_helper.import_module('_testcapi') +_testlimitedcapi = import_helper.import_module('_testlimitedcapi') +NULL = None + +class Object: + pass + +class Ref(weakref.ReferenceType): + pass + + +class CAPIWeakrefTest(unittest.TestCase): + def test_pyweakref_check(self): + # Test PyWeakref_Check() + check = _testlimitedcapi.pyweakref_check + obj = Object() + self.assertEqual(check(obj), 0) + self.assertEqual(check(weakref.ref(obj)), 1) + self.assertEqual(check(Ref(obj)), 1) + self.assertEqual(check(weakref.proxy(obj)), 1) + + # CRASHES check(NULL) + + def test_pyweakref_checkref(self): + # Test PyWeakref_CheckRef() + checkref = _testlimitedcapi.pyweakref_checkref + obj = Object() + self.assertEqual(checkref(obj), 0) + self.assertEqual(checkref(weakref.ref(obj)), 1) + self.assertEqual(checkref(Ref(obj)), 1) + self.assertEqual(checkref(weakref.proxy(obj)), 0) + + # CRASHES checkref(NULL) + + def test_pyweakref_checkrefexact(self): + # Test PyWeakref_CheckRefExact() + checkrefexact = _testlimitedcapi.pyweakref_checkrefexact + obj = Object() + self.assertEqual(checkrefexact(obj), 0) + self.assertEqual(checkrefexact(weakref.ref(obj)), 1) + self.assertEqual(checkrefexact(Ref(obj)), 0) + self.assertEqual(checkrefexact(weakref.proxy(obj)), 0) + + # CRASHES checkrefexact(NULL) + + def test_pyweakref_checkproxy(self): + # Test PyWeakref_CheckProxy() + checkproxy = _testlimitedcapi.pyweakref_checkproxy + obj = Object() + self.assertEqual(checkproxy(obj), 0) + self.assertEqual(checkproxy(weakref.ref(obj)), 0) + self.assertEqual(checkproxy(Ref(obj)), 0) + self.assertEqual(checkproxy(weakref.proxy(obj)), 1) + + # CRASHES checkproxy(NULL) + + def test_pyweakref_getref(self): + # Test PyWeakref_GetRef() + getref = _testcapi.pyweakref_getref + obj = Object() + wr = weakref.ref(obj) + wp = weakref.proxy(obj) + self.assertEqual(getref(wr), (1, obj)) + self.assertEqual(getref(wp), (1, obj)) + del obj + self.assertEqual(getref(wr), 0) + self.assertEqual(getref(wp), 0) + + self.assertRaises(TypeError, getref, 42) + self.assertRaises(SystemError, getref, NULL) + + def test_pyweakref_isdead(self): + # Test PyWeakref_IsDead() + isdead = _testcapi.pyweakref_isdead + obj = Object() + wr = weakref.ref(obj) + wp = weakref.proxy(obj) + self.assertEqual(isdead(wr), 0) + self.assertEqual(isdead(wp), 0) + del obj + self.assertEqual(isdead(wr), 1) + self.assertEqual(isdead(wp), 1) + + self.assertRaises(TypeError, isdead, 42) + self.assertRaises(SystemError, isdead, NULL) + + def test_pyweakref_newref(self): + # Test PyWeakref_NewRef() + newref = _testlimitedcapi.pyweakref_newref + obj = Object() + wr = newref(obj) + self.assertIs(type(wr), weakref.ReferenceType) + # PyWeakref_NewRef() handles None callback as NULL callback + wr = newref(obj, None) + self.assertIs(type(wr), weakref.ReferenceType) + log = [] + wr = newref(obj, log.append) + self.assertIs(type(wr), weakref.ReferenceType) + self.assertEqual(log, []) + del obj + self.assertEqual(log, [wr]) + + self.assertRaises(TypeError, newref, []) + # CRASHES newref(NULL) + + def test_pyweakref_newproxy(self): + # Test PyWeakref_NewProxy() + newproxy = _testlimitedcapi.pyweakref_newproxy + obj = Object() + wp = newproxy(obj) + self.assertIs(type(wp), weakref.ProxyType) + # PyWeakref_NewProxy() handles None callback as NULL callback + wp = newproxy(obj, None) + self.assertIs(type(wp), weakref.ProxyType) + log = [] + wp = newproxy(obj, log.append) + self.assertIs(type(wp), weakref.ProxyType) + self.assertEqual(log, []) + del obj + self.assertEqual(log, [wp]) + + def func(): + pass + wp = newproxy(func) + self.assertIs(type(wp), weakref.CallableProxyType) + + self.assertRaises(TypeError, newproxy, []) + # CRASHES newproxy(NULL) + + +if __name__ == "__main__": + unittest.main() diff --git a/Misc/NEWS.d/next/Tests/2026-06-09-11-52-52.gh-issue-151130.1vslPH.rst b/Misc/NEWS.d/next/Tests/2026-06-09-11-52-52.gh-issue-151130.1vslPH.rst new file mode 100644 index 000000000000000..0333e66446ce161 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2026-06-09-11-52-52.gh-issue-151130.1vslPH.rst @@ -0,0 +1 @@ +Add more tests for ``PyWeakref_*`` C API. diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in index c3dd47a5e40a675..8efea27824f0e88 100644 --- a/Modules/Setup.stdlib.in +++ b/Modules/Setup.stdlib.in @@ -173,8 +173,8 @@ @MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c @MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c @MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c _testinternalcapi/test_lock.c _testinternalcapi/pytime.c _testinternalcapi/set.c _testinternalcapi/test_critical_sections.c _testinternalcapi/complex.c _testinternalcapi/interpreter.c _testinternalcapi/tuple.c -@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/modsupport.c _testcapi/monitoring.c _testcapi/config.c _testcapi/import.c _testcapi/frame.c _testcapi/type.c _testcapi/function.c _testcapi/module.c -@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/codec.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/import.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/slots.c _testlimitedcapi/sys.c _testlimitedcapi/threadstate.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c _testlimitedcapi/version.c _testlimitedcapi/file.c +@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/modsupport.c _testcapi/monitoring.c _testcapi/config.c _testcapi/import.c _testcapi/frame.c _testcapi/type.c _testcapi/function.c _testcapi/module.c _testcapi/weakref.c +@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/codec.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/import.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/slots.c _testlimitedcapi/sys.c _testlimitedcapi/threadstate.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c _testlimitedcapi/version.c _testlimitedcapi/file.c _testlimitedcapi/weakref.c @MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c @MODULE__TESTCLINIC_LIMITED_TRUE@_testclinic_limited _testclinic_limited.c diff --git a/Modules/_testcapi/parts.h b/Modules/_testcapi/parts.h index a7feca5bd960705..98b5dd47accde35 100644 --- a/Modules/_testcapi/parts.h +++ b/Modules/_testcapi/parts.h @@ -67,5 +67,6 @@ int _PyTestCapi_Init_Frame(PyObject *mod); int _PyTestCapi_Init_Type(PyObject *mod); int _PyTestCapi_Init_Function(PyObject *mod); int _PyTestCapi_Init_Module(PyObject *mod); +int _PyTestCapi_Init_Weakref(PyObject *mod); #endif // Py_TESTCAPI_PARTS_H diff --git a/Modules/_testcapi/weakref.c b/Modules/_testcapi/weakref.c new file mode 100644 index 000000000000000..7c3ad8565991b7e --- /dev/null +++ b/Modules/_testcapi/weakref.c @@ -0,0 +1,46 @@ +#include "parts.h" +#include "util.h" + + +static PyObject * +pyweakref_getref(PyObject *module, PyObject *ref) +{ + NULLABLE(ref); + PyObject *obj = UNINITIALIZED_PTR; + int rc = PyWeakref_GetRef(ref, &obj); + if (rc == -1 && PyErr_Occurred()) { + assert(obj == NULL); + return NULL; + } + if (obj == NULL) { + return Py_BuildValue("i", rc); + } + else { + assert(obj != UNINITIALIZED_PTR); + return Py_BuildValue("iN", rc, obj); + } +} + +static PyObject * +pyweakref_isdead(PyObject *module, PyObject *obj) +{ + NULLABLE(obj); + int rc = PyWeakref_IsDead(obj); + if (rc == -1 && PyErr_Occurred()) { + return NULL; + } + return PyLong_FromLong(rc); +} + + +static PyMethodDef test_methods[] = { + {"pyweakref_getref", pyweakref_getref, METH_O}, + {"pyweakref_isdead", pyweakref_isdead, METH_O}, + {NULL}, +}; + +int +_PyTestCapi_Init_Weakref(PyObject *m) +{ + return PyModule_AddFunctions(m, test_methods); +} diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index be5ad3e9efa1040..9c90d1fc36f398e 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3909,6 +3909,9 @@ _testcapi_exec(PyObject *m) if (_PyTestCapi_Init_Module(m) < 0) { return -1; } + if (_PyTestCapi_Init_Weakref(m) < 0) { + return -1; + } return 0; } diff --git a/Modules/_testlimitedcapi.c b/Modules/_testlimitedcapi.c index 5f2be0dd43954e3..9314fccc6c915a4 100644 --- a/Modules/_testlimitedcapi.c +++ b/Modules/_testlimitedcapi.c @@ -98,5 +98,8 @@ PyInit__testlimitedcapi(void) if (_PyTestLimitedCAPI_Init_File(mod) < 0) { return NULL; } + if (_PyTestLimitedCAPI_Init_Weakref(mod) < 0) { + return NULL; + } return mod; } diff --git a/Modules/_testlimitedcapi/parts.h b/Modules/_testlimitedcapi/parts.h index 1eea4f74d14416c..c51d285e19ab0d6 100644 --- a/Modules/_testlimitedcapi/parts.h +++ b/Modules/_testlimitedcapi/parts.h @@ -45,5 +45,6 @@ int _PyTestLimitedCAPI_Init_Unicode(PyObject *module); int _PyTestLimitedCAPI_Init_VectorcallLimited(PyObject *module); int _PyTestLimitedCAPI_Init_Version(PyObject *module); int _PyTestLimitedCAPI_Init_File(PyObject *module); +int _PyTestLimitedCAPI_Init_Weakref(PyObject *module); #endif // Py_TESTLIMITEDCAPI_PARTS_H diff --git a/Modules/_testlimitedcapi/weakref.c b/Modules/_testlimitedcapi/weakref.c new file mode 100644 index 000000000000000..e7f9d54d1a0d59e --- /dev/null +++ b/Modules/_testlimitedcapi/weakref.c @@ -0,0 +1,78 @@ +#include "pyconfig.h" // Py_GIL_DISABLED +#ifndef Py_GIL_DISABLED + // Need limited C API 3.5 for PyModule_AddFunctions() +# define Py_LIMITED_API 0x03050000 +#endif + +#include "parts.h" +#include "util.h" + + +static PyObject * +pyweakref_check(PyObject *module, PyObject *obj) +{ + NULLABLE(obj); + return PyLong_FromLong(PyWeakref_Check(obj)); +} + +static PyObject * +pyweakref_checkref(PyObject *module, PyObject *obj) +{ + NULLABLE(obj); + return PyLong_FromLong(PyWeakref_CheckRef(obj)); +} + +static PyObject * +pyweakref_checkrefexact(PyObject *module, PyObject *obj) +{ + NULLABLE(obj); + return PyLong_FromLong(PyWeakref_CheckRefExact(obj)); +} + +static PyObject * +pyweakref_checkproxy(PyObject *module, PyObject *obj) +{ + NULLABLE(obj); + return PyLong_FromLong(PyWeakref_CheckProxy(obj)); +} + +static PyObject * +pyweakref_newref(PyObject *module, PyObject *args) +{ + PyObject *obj; + PyObject *callback = NULL; + if (!PyArg_ParseTuple(args, "O|O", &obj, &callback)) { + return NULL; + } + NULLABLE(obj); + return PyWeakref_NewRef(obj, callback); +} + +static PyObject * +pyweakref_newproxy(PyObject *module, PyObject *args) +{ + PyObject *obj; + PyObject *callback = NULL; + if (!PyArg_ParseTuple(args, "O|O", &obj, &callback)) { + return NULL; + } + NULLABLE(obj); + return PyWeakref_NewProxy(obj, callback); +} + + +static PyMethodDef test_methods[] = { + {"pyweakref_check", pyweakref_check, METH_O}, + {"pyweakref_checkref", pyweakref_checkref, METH_O}, + {"pyweakref_checkrefexact", pyweakref_checkrefexact, METH_O}, + {"pyweakref_checkproxy", pyweakref_checkproxy, METH_O}, + {"pyweakref_newref", pyweakref_newref, METH_VARARGS}, + {"pyweakref_newproxy", pyweakref_newproxy, METH_VARARGS}, + {NULL}, +}; + +int +_PyTestLimitedCAPI_Init_Weakref(PyObject *m) +{ + return PyModule_AddFunctions(m, test_methods); +} diff --git a/PCbuild/_testcapi.vcxproj b/PCbuild/_testcapi.vcxproj index 62312acf248b918..64e50b67be46561 100644 --- a/PCbuild/_testcapi.vcxproj +++ b/PCbuild/_testcapi.vcxproj @@ -133,6 +133,7 @@ + diff --git a/PCbuild/_testcapi.vcxproj.filters b/PCbuild/_testcapi.vcxproj.filters index b0e75ce433ab14d..a3b62e1df663e00 100644 --- a/PCbuild/_testcapi.vcxproj.filters +++ b/PCbuild/_testcapi.vcxproj.filters @@ -132,6 +132,9 @@ Source Files + + Source Files + diff --git a/PCbuild/_testlimitedcapi.vcxproj b/PCbuild/_testlimitedcapi.vcxproj index 34841ff9780a011..69558d204dbb8e7 100644 --- a/PCbuild/_testlimitedcapi.vcxproj +++ b/PCbuild/_testlimitedcapi.vcxproj @@ -117,6 +117,7 @@ + diff --git a/PCbuild/_testlimitedcapi.vcxproj.filters b/PCbuild/_testlimitedcapi.vcxproj.filters index a29973786c9485d..2bcc3f6ff176bd9 100644 --- a/PCbuild/_testlimitedcapi.vcxproj.filters +++ b/PCbuild/_testlimitedcapi.vcxproj.filters @@ -33,6 +33,7 @@ + From 086589f1026b0fbc4e2303c0fb5bbfc5d977974f Mon Sep 17 00:00:00 2001 From: sobolevn Date: Tue, 9 Jun 2026 14:44:37 +0300 Subject: [PATCH 075/185] gh-151039: Fix a crash when `_datetime` types outlive `_datetime` module (#151044) --- Lib/test/datetimetester.py | 30 +++++++ ...-06-07-17-29-33.gh-issue-151039.AZ0qBn.rst | 1 + Modules/_datetimemodule.c | 81 +++++++++++++------ 3 files changed, 86 insertions(+), 26 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-06-07-17-29-33.gh-issue-151039.AZ0qBn.rst diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 5d5b8e415f3cd21..d26e41982deb811 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -7509,6 +7509,36 @@ def func(): self.assertEqual(out, b"a" * 8) self.assertEqual(err, b"") + @support.cpython_only + @support.subTests(("setup", "call"), [ + ("obj = _datetime.timedelta", "obj(seconds=2)"), + ("obj = _datetime.timedelta(seconds=2)", "obj.total_seconds()"), + ("obj = _datetime.date(2026, 6, 7)", "obj.isocalendar()"), + ]) + def test_static_datetime_types_outlive_collected_module(self, setup, call): + # gh-151039: This code used to crash + script = f"""if True: + import sys, gc + import _datetime + + {setup} # static C type, survives the module + del sys.modules['_datetime'] + del _datetime + sys.modules['_datetime'] = None # block re-import + gc.collect() # module object is collected + + try: + {call} # used to be a segmentation fault + except ImportError: + pass + else: + raise AssertionError("ImportError not raised") + """ + rc, out, err = script_helper.assert_python_ok("-c", script) + self.assertEqual(rc, 0) + self.assertEqual(out, b'') + self.assertEqual(err, b'') + def load_tests(loader, standard_tests, pattern): standard_tests.addTest(ZoneInfoCompleteTest()) diff --git a/Misc/NEWS.d/next/Library/2026-06-07-17-29-33.gh-issue-151039.AZ0qBn.rst b/Misc/NEWS.d/next/Library/2026-06-07-17-29-33.gh-issue-151039.AZ0qBn.rst new file mode 100644 index 000000000000000..1e99567f5550579 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-07-17-29-33.gh-issue-151039.AZ0qBn.rst @@ -0,0 +1 @@ +Fix a crash when static :mod:`datetime` types outlive the ``_datetime`` module. diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 30317ea823ff7ee..82b1f898a3c6741 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -126,8 +126,8 @@ get_module_state(PyObject *module) #define INTERP_KEY ((PyObject *)&_Py_ID(cached_datetime_module)) -static PyObject * -get_current_module(PyInterpreterState *interp) +static int +get_current_module(PyInterpreterState *interp, PyObject **p_mod) { PyObject *mod = NULL; @@ -139,20 +139,24 @@ get_current_module(PyInterpreterState *interp) if (PyDict_GetItemRef(dict, INTERP_KEY, &ref) < 0) { goto error; } - if (ref != NULL) { - if (ref != Py_None) { - (void)PyWeakref_GetRef(ref, &mod); - if (mod == Py_None) { - Py_CLEAR(mod); - } + if (ref != NULL && ref != Py_None) { + if (PyWeakref_GetRef(ref, &mod) < 0) { Py_DECREF(ref); + goto error; + } + if (mod == Py_None) { + Py_CLEAR(mod); } + Py_DECREF(ref); } - return mod; + assert(!PyErr_Occurred()); + *p_mod = mod; + return mod != NULL; error: assert(PyErr_Occurred()); - return NULL; + *p_mod = NULL; + return -1; } static PyModuleDef datetimemodule; @@ -161,22 +165,26 @@ static datetime_state * _get_current_state(PyObject **p_mod) { PyInterpreterState *interp = PyInterpreterState_Get(); - PyObject *mod = get_current_module(interp); + PyObject *mod; + if (get_current_module(interp, &mod) < 0) { + goto error; + } if (mod == NULL) { - assert(!PyErr_Occurred()); - if (PyErr_Occurred()) { - return NULL; - } /* The static types can outlive the module, * so we must re-import the module. */ mod = PyImport_ImportModule("_datetime"); if (mod == NULL) { - return NULL; + goto error; } } datetime_state *st = get_module_state(mod); *p_mod = mod; return st; + +error: + assert(PyErr_Occurred()); + *p_mod = NULL; + return NULL; } #define GET_CURRENT_STATE(MOD_VAR) \ @@ -2128,8 +2136,11 @@ delta_to_microseconds(PyDateTime_Delta *self) PyObject *x3 = NULL; PyObject *result = NULL; - PyObject *current_mod = NULL; + PyObject *current_mod; datetime_state *st = GET_CURRENT_STATE(current_mod); + if (st == NULL) { + return NULL; + } x1 = PyLong_FromLong(GET_TD_DAYS(self)); if (x1 == NULL) @@ -2207,8 +2218,11 @@ microseconds_to_delta_ex(PyObject *pyus, PyTypeObject *type) PyObject *num = NULL; PyObject *result = NULL; - PyObject *current_mod = NULL; + PyObject *current_mod; datetime_state *st = GET_CURRENT_STATE(current_mod); + if (st == NULL) { + return NULL; + } tuple = checked_divmod(pyus, CONST_US_PER_SECOND(st)); if (tuple == NULL) { @@ -2815,8 +2829,11 @@ delta_new_impl(PyTypeObject *type, PyObject *days, PyObject *seconds, { PyObject *self = NULL; - PyObject *current_mod = NULL; + PyObject *current_mod; datetime_state *st = GET_CURRENT_STATE(current_mod); + if (st == NULL) { + return NULL; + } PyObject *x = NULL; /* running sum of microseconds */ PyObject *y = NULL; /* temp sum of microseconds */ @@ -3014,8 +3031,12 @@ delta_total_seconds(PyObject *op, PyObject *Py_UNUSED(dummy)) if (total_microseconds == NULL) return NULL; - PyObject *current_mod = NULL; + PyObject *current_mod; datetime_state *st = GET_CURRENT_STATE(current_mod); + if (st == NULL) { + Py_DECREF(total_microseconds); + return NULL; + } total_seconds = PyNumber_TrueDivide(total_microseconds, CONST_US_PER_SECOND(st)); @@ -3867,8 +3888,11 @@ date_isocalendar(PyObject *self, PyObject *Py_UNUSED(dummy)) week = 0; } - PyObject *current_mod = NULL; + PyObject *current_mod; datetime_state *st = GET_CURRENT_STATE(current_mod); + if (st == NULL) { + return NULL; + } PyObject *v = iso_calendar_date_new_impl(ISOCALENDAR_DATE_TYPE(st), year, week + 1, day + 1); @@ -6800,8 +6824,11 @@ local_timezone(PyDateTime_DateTime *utc_time) PyObject *one_second; PyObject *seconds; - PyObject *current_mod = NULL; + PyObject *current_mod; datetime_state *st = GET_CURRENT_STATE(current_mod); + if (st == NULL) { + return NULL; + } delta = datetime_subtract((PyObject *)utc_time, CONST_EPOCH(st)); RELEASE_CURRENT_STATE(st, current_mod); @@ -7047,8 +7074,11 @@ datetime_timestamp(PyObject *op, PyObject *Py_UNUSED(dummy)) PyObject *result; if (HASTZINFO(self) && self->tzinfo != Py_None) { - PyObject *current_mod = NULL; + PyObject *current_mod; datetime_state *st = GET_CURRENT_STATE(current_mod); + if (st == NULL) { + return NULL; + } PyObject *delta; delta = datetime_subtract(op, CONST_EPOCH(st)); @@ -7581,9 +7611,8 @@ _datetime_exec(PyObject *module) datetime_state *st = get_module_state(module); PyInterpreterState *interp = PyInterpreterState_Get(); - PyObject *old_module = get_current_module(interp); - if (PyErr_Occurred()) { - assert(old_module == NULL); + PyObject *old_module; + if (get_current_module(interp, &old_module) < 0) { goto error; } /* We actually set the "current" module right before a successful return. */ From 15464d15e4e5ef07b401507d41ba696bf77cfda6 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Tue, 9 Jun 2026 14:16:03 +0100 Subject: [PATCH 076/185] Docs: Only add `profiling-sampling-visualization.{css,js}` to files when necessary (#151150) --- Doc/tools/extensions/profiling_trace.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Doc/tools/extensions/profiling_trace.py b/Doc/tools/extensions/profiling_trace.py index 7185ef351ddc7f2..183c6de48714a43 100644 --- a/Doc/tools/extensions/profiling_trace.py +++ b/Doc/tools/extensions/profiling_trace.py @@ -154,10 +154,15 @@ def inject_trace(app, exception): ) +def add_assets(app, pagename, templatename, context, doctree): + if pagename == 'library/profiling.sampling': + app.add_js_file('profiling-sampling-visualization.js') + app.add_css_file('profiling-sampling-visualization.css') + + def setup(app): app.connect('build-finished', inject_trace) - app.add_js_file('profiling-sampling-visualization.js') - app.add_css_file('profiling-sampling-visualization.css') + app.connect('html-page-context', add_assets) return { 'version': '1.0', From 71db66c60173a8157e7809c547643daa18518385 Mon Sep 17 00:00:00 2001 From: Robsdedude Date: Tue, 9 Jun 2026 16:37:38 +0200 Subject: [PATCH 077/185] gh-150898: Assume OpenSSL supports keylogging (#150870) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since version 3.10, CPython requires OpenSSL 1.1.1 or higher. Therefore, support for keylogging can be assumed. 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> Co-authored-by: Victor Stinner --- Doc/library/ssl.rst | 6 +++--- Lib/ssl.py | 14 ++++++-------- Lib/test/test_ssl.py | 8 +------- .../2026-06-04-06-50-06.gh-issue-150898.1LkLA3.rst | 1 + 4 files changed, 11 insertions(+), 18 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-06-04-06-50-06.gh-issue-150898.1LkLA3.rst diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index 41a101e84ac4d75..66fe6c7aee48626 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -146,9 +146,9 @@ purposes. *cadata* is given) or uses :meth:`SSLContext.load_default_certs` to load default CA certificates. - When :attr:`~SSLContext.keylog_filename` is supported and the environment - variable :envvar:`SSLKEYLOGFILE` is set, :func:`create_default_context` - enables key logging. + When the environment variable :envvar:`!SSLKEYLOGFILE` is set, + :func:`create_default_context` enables key logging by setting + :attr:`~SSLContext.keylog_filename` to the variable's value. The default settings for this context include :data:`VERIFY_X509_PARTIAL_CHAIN` and :data:`VERIFY_X509_STRICT`. diff --git a/Lib/ssl.py b/Lib/ssl.py index f23bcbe75e7201f..3c0361330d7e951 100644 --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -721,10 +721,9 @@ def create_default_context(purpose=Purpose.SERVER_AUTH, *, cafile=None, # root CA certificates for the given purpose. This may fail silently. context.load_default_certs(purpose) # OpenSSL 1.1.1 keylog file - if hasattr(context, 'keylog_filename'): - keylogfile = os.environ.get('SSLKEYLOGFILE') - if keylogfile and not sys.flags.ignore_environment: - context.keylog_filename = keylogfile + keylogfile = os.environ.get('SSLKEYLOGFILE') + if keylogfile and not sys.flags.ignore_environment: + context.keylog_filename = keylogfile return context def _create_unverified_context(protocol=None, *, cert_reqs=CERT_NONE, @@ -775,10 +774,9 @@ def _create_unverified_context(protocol=None, *, cert_reqs=CERT_NONE, # root CA certificates for the given purpose. This may fail silently. context.load_default_certs(purpose) # OpenSSL 1.1.1 keylog file - if hasattr(context, 'keylog_filename'): - keylogfile = os.environ.get('SSLKEYLOGFILE') - if keylogfile and not sys.flags.ignore_environment: - context.keylog_filename = keylogfile + keylogfile = os.environ.get('SSLKEYLOGFILE') + if keylogfile and not sys.flags.ignore_environment: + context.keylog_filename = keylogfile return context # Used by http.client if no context is explicitly passed. diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 4f998ef2b02a69a..40111d410077950 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -59,10 +59,7 @@ CAN_GET_SELECTED_OPENSSL_SIGALG = ssl.OPENSSL_VERSION_INFO >= (3, 5) PY_SSL_DEFAULT_CIPHERS = sysconfig.get_config_var('PY_SSL_DEFAULT_CIPHERS') -HAS_KEYLOG = hasattr(ssl.SSLContext, 'keylog_filename') -requires_keylog = unittest.skipUnless( - HAS_KEYLOG, 'test requires OpenSSL 1.1.1 with keylog callback') -CAN_SET_KEYLOG = HAS_KEYLOG and os.name != "nt" +CAN_SET_KEYLOG = (os.name != "nt") requires_keylog_setter = unittest.skipUnless( CAN_SET_KEYLOG, "cannot set 'keylog_filename' on Windows" @@ -5453,7 +5450,6 @@ def keylog_lines(self, fname=os_helper.TESTFN): with open(fname) as f: return len(list(f)) - @requires_keylog def test_keylog_defaults(self): self.addCleanup(os_helper.unlink, os_helper.TESTFN) ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) @@ -5481,7 +5477,6 @@ def test_keylog_defaults(self): with self.assertRaises(TypeError): ctx.keylog_filename = 1 - @requires_keylog def test_keylog_filename(self): self.addCleanup(os_helper.unlink, os_helper.TESTFN) client_context, server_context, hostname = testing_context() @@ -5522,7 +5517,6 @@ def test_keylog_filename(self): client_context.keylog_filename = None server_context.keylog_filename = None - @requires_keylog @unittest.skipIf(sys.flags.ignore_environment, "test is not compatible with ignore_environment") def test_keylog_env(self): diff --git a/Misc/NEWS.d/next/Library/2026-06-04-06-50-06.gh-issue-150898.1LkLA3.rst b/Misc/NEWS.d/next/Library/2026-06-04-06-50-06.gh-issue-150898.1LkLA3.rst new file mode 100644 index 000000000000000..85328c43368e198 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-04-06-50-06.gh-issue-150898.1LkLA3.rst @@ -0,0 +1 @@ +Unconditionally assume :attr:`ssl.SSLContext.keylog_filename` exists. From 058408dcebf5c84904e2adc6fb91a568cdb1e18e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20L=C3=B3pez=20G=C3=B3mez?= Date: Tue, 9 Jun 2026 17:36:05 +0200 Subject: [PATCH 078/185] Docs: add cross-references to improve navigation in `getpass.rst` (#151092) --- Doc/library/getpass.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Doc/library/getpass.rst b/Doc/library/getpass.rst index fd96f3bbf6a574f..f60cc63414f764d 100644 --- a/Doc/library/getpass.rst +++ b/Doc/library/getpass.rst @@ -18,7 +18,7 @@ The :mod:`!getpass` module provides two functions: the string *prompt*, which defaults to ``'Password: '``. On Unix, the prompt is written to the file-like object *stream* using the replace error handler if needed. *stream* defaults to the controlling terminal - (:file:`/dev/tty`) or if that is unavailable to ``sys.stderr`` (this + (:file:`/dev/tty`) or if that is unavailable to :data:`sys.stderr` (this argument is ignored on Windows). The *echo_char* argument controls how user input is displayed while typing. @@ -27,12 +27,12 @@ The :mod:`!getpass` module provides two functions: typed character is replaced by it. For example, ``echo_char='*'`` will display asterisks instead of the actual input. - If echo free input is unavailable getpass() falls back to printing - a warning message to *stream* and reading from ``sys.stdin`` and + If echo-free input is unavailable, :func:`getpass` falls back to printing + a warning message to *stream* and reading from :data:`sys.stdin` and issuing a :exc:`GetPassWarning`. .. note:: - If you call getpass from within IDLE, the input may be done in the + If you call :func:`getpass` from within IDLE, the input may be done in the terminal you launched IDLE from rather than the idle window itself. .. note:: From bd81be7d1102d8ac0c71775eb3934f9ade6d485f Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Tue, 9 Jun 2026 08:37:15 -0700 Subject: [PATCH 079/185] GH-54732: Tweak wording around empty lines in argument files (#150980) --- Doc/library/argparse.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Doc/library/argparse.rst b/Doc/library/argparse.rst index db5fae2006678a2..622f844a4a0b825 100644 --- a/Doc/library/argparse.rst +++ b/Doc/library/argparse.rst @@ -442,9 +442,8 @@ is considered equivalent to the expression ``['-f', 'foo', '-f', 'bar']``. .. note:: - Empty lines are treated as empty strings (``''``), which are allowed as values but - not as arguments. Empty lines that are read as arguments will result in an - "unrecognized arguments" error. + Each line is treated as a single argument, so an empty line is read as an + empty string (``''``). :class:`ArgumentParser` uses :term:`filesystem encoding and error handler` to read the file containing arguments. @@ -2232,6 +2231,9 @@ Customizing file parsing def convert_arg_line_to_args(self, arg_line): return arg_line.split() + Note that with this override an argument can no longer contain spaces, since + each space-separated word becomes a separate argument. + Exiting methods ^^^^^^^^^^^^^^^ From 0163eec720f65bff601cfa12dd7d747543875469 Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Tue, 9 Jun 2026 08:37:24 -0700 Subject: [PATCH 080/185] GH-61082: Clarify nargs='*' positional default behavior (#150989) --- Doc/library/argparse.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Doc/library/argparse.rst b/Doc/library/argparse.rst index 622f844a4a0b825..4c588d447a9a7cf 100644 --- a/Doc/library/argparse.rst +++ b/Doc/library/argparse.rst @@ -1051,6 +1051,10 @@ is used when no command-line argument was present:: >>> parser.parse_args([]) Namespace(foo=42) +Because ``nargs='*'`` gathers any supplied values into a list, an absent +positional argument yields an empty list (``[]``). Only a non-``None`` +*default* overrides this (so ``default=None`` still gives ``[]``). + For required_ arguments, the ``default`` value is ignored. For example, this applies to positional arguments with nargs_ values other than ``?`` or ``*``, or optional arguments marked as ``required=True``. From 2b95d298e179ab5436e7cabd2144912035b2187d Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 9 Jun 2026 19:13:34 +0300 Subject: [PATCH 081/185] gh-150285: Fix too long docstrings in the asyncio package (GH-151074) --- Lib/asyncio/base_events.py | 32 +++++++++---------- Lib/asyncio/events.py | 21 +++++++------ Lib/asyncio/graph.py | 14 ++++----- Lib/asyncio/locks.py | 18 +++++------ Lib/asyncio/queues.py | 36 +++++++++++---------- Lib/asyncio/runners.py | 3 +- Lib/asyncio/selector_events.py | 31 +++++++++--------- Lib/asyncio/streams.py | 14 ++++----- Lib/asyncio/taskgroups.py | 16 +++++----- Lib/asyncio/tasks.py | 57 ++++++++++++++++++---------------- Lib/asyncio/threads.py | 3 +- Lib/asyncio/timeouts.py | 3 +- Lib/asyncio/unix_events.py | 3 +- 13 files changed, 133 insertions(+), 118 deletions(-) diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index 3732294c5848f02..e6c72e3d5b5487e 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -488,10 +488,10 @@ def set_task_factory(self, factory): If factory is None the default task factory will be set. If factory is a callable, it should have a signature matching - '(loop, coro, **kwargs)', where 'loop' will be a reference to the active - event loop, 'coro' will be a coroutine object, and **kwargs will be - arbitrary keyword arguments that should be passed on to Task. - The callable must return a Task. + '(loop, coro, **kwargs)', where 'loop' will be a reference to the + active event loop, 'coro' will be a coroutine object, and **kwargs + will be arbitrary keyword arguments that should be passed on to + Task. The callable must return a Task. """ if factory is not None and not callable(factory): raise TypeError('task factory must be a callable or None') @@ -727,8 +727,8 @@ def run_until_complete(self, future): def stop(self): """Stop running the event loop. - Every callback already scheduled will still run. This simply informs - run_forever to stop looping after a complete iteration. + Every callback already scheduled will still run. This simply + informs run_forever to stop looping after a complete iteration. """ self._stopping = True @@ -1076,12 +1076,12 @@ async def create_connection( Create a streaming transport connection to a given internet host and port: socket family AF_INET or socket.AF_INET6 depending on host (or - family if specified), socket type SOCK_STREAM. protocol_factory must be - a callable returning a protocol instance. + family if specified), socket type SOCK_STREAM. protocol_factory must + be a callable returning a protocol instance. - This method is a coroutine which will try to establish the connection - in the background. When successful, the coroutine returns a - (transport, protocol) pair. + This method is a coroutine which will try to establish the + connection in the background. When successful, the coroutine + returns a (transport, protocol) pair. """ if server_hostname is not None and not ssl: raise ValueError('server_hostname is only meaningful with ssl') @@ -1550,11 +1550,11 @@ async def create_server( The host parameter can be a string, in that case the TCP server is bound to host and port. - The host parameter can also be a sequence of strings and in that case - the TCP server is bound to all hosts of the sequence. If a host - appears multiple times (possibly indirectly e.g. when hostnames - resolve to the same IP address), the server is only bound once to that - host. + The host parameter can also be a sequence of strings and in that + case the TCP server is bound to all hosts of the sequence. If + a host appears multiple times (possibly indirectly e.g. when + hostnames resolve to the same IP address), the server is only bound + once to that host. Return a Server object which can be used to stop the service. diff --git a/Lib/asyncio/events.py b/Lib/asyncio/events.py index a7fb55982abe9ca..42d8408a38dfd1f 100644 --- a/Lib/asyncio/events.py +++ b/Lib/asyncio/events.py @@ -374,8 +374,8 @@ async def create_server( If host is an empty string or None all interfaces are assumed and a list of multiple sockets will be returned (most likely - one for IPv4 and another one for IPv6). The host parameter can also be - a sequence (e.g. list) of hosts to bind to. + one for IPv4 and another one for IPv6). The host parameter can also + be a sequence (e.g. list) of hosts to bind to. family can be set to either AF_INET or AF_INET6 to force the socket to use IPv4 or IPv6. If not set it will be determined @@ -415,8 +415,9 @@ async def create_server( start_serving set to True (default) causes the created server to start accepting connections immediately. When set to False, - the user should await Server.start_serving() or Server.serve_forever() - to make the server to start accepting connections. + the user should await Server.start_serving() or + Server.serve_forever() to make the server to start accepting + connections. """ raise NotImplementedError @@ -479,8 +480,9 @@ async def create_unix_server( start_serving set to True (default) causes the created server to start accepting connections immediately. When set to False, - the user should await Server.start_serving() or Server.serve_forever() - to make the server to start accepting connections. + the user should await Server.start_serving() or + Server.serve_forever() to make the server to start accepting + connections. """ raise NotImplementedError @@ -511,8 +513,8 @@ async def create_datagram_endpoint(self, protocol_factory, protocol_factory must be a callable returning a protocol instance. - socket family AF_INET, socket.AF_INET6 or socket.AF_UNIX depending on - host (or family if specified), socket type SOCK_DGRAM. + socket family AF_INET, socket.AF_INET6 or socket.AF_UNIX depending + on host (or family if specified), socket type SOCK_DGRAM. reuse_address tells the kernel to reuse a local socket in TIME_WAIT state, without waiting for its natural timeout to @@ -552,7 +554,8 @@ async def connect_read_pipe(self, protocol_factory, pipe): async def connect_write_pipe(self, protocol_factory, pipe): """Register write pipe in event loop. - protocol_factory should instantiate object with BaseProtocol interface. + protocol_factory should instantiate object with BaseProtocol + interface. Pipe is file-like object already switched to nonblocking. Return pair (transport, protocol), where transport support WriteTransport interface.""" diff --git a/Lib/asyncio/graph.py b/Lib/asyncio/graph.py index b5bfeb1630a1590..35f7fa62140bd49 100644 --- a/Lib/asyncio/graph.py +++ b/Lib/asyncio/graph.py @@ -112,13 +112,13 @@ def capture_call_graph( optional keyword-only 'depth' argument can be used to skip the specified number of frames from top of the stack. - If the optional keyword-only 'limit' argument is provided, each call stack - in the resulting graph is truncated to include at most ``abs(limit)`` - entries. If 'limit' is positive, the entries left are the closest to - the invocation point. If 'limit' is negative, the topmost entries are - left. If 'limit' is omitted or None, all entries are present. - If 'limit' is 0, the call stack is not captured at all, only - "awaited by" information is present. + If the optional keyword-only 'limit' argument is provided, each call + stack in the resulting graph is truncated to include at most + ``abs(limit)`` entries. If 'limit' is positive, the entries left are + the closest to the invocation point. If 'limit' is negative, the + topmost entries are left. If 'limit' is omitted or None, all entries + are present. If 'limit' is 0, the call stack is not captured at all, + only "awaited by" information is present. """ loop = events._get_running_loop() diff --git a/Lib/asyncio/locks.py b/Lib/asyncio/locks.py index 0d5e362188ab21d..c59ea15111bef2b 100644 --- a/Lib/asyncio/locks.py +++ b/Lib/asyncio/locks.py @@ -155,10 +155,10 @@ def _wake_up_first(self): class Event(mixins._LoopBoundMixin): """Asynchronous equivalent to threading.Event. - Class implementing event objects. An event manages a flag that can be set - to true with the set() method and reset to false with the clear() method. - The wait() method blocks until the flag is true. The flag is initially - false. + Class implementing event objects. An event manages a flag that can be + set to true with the set() method and reset to false with the clear() + method. The wait() method blocks until the flag is true. The flag is + initially false. """ def __init__(self): @@ -350,9 +350,9 @@ class Semaphore(_ContextManagerMixin, mixins._LoopBoundMixin): """A Semaphore implementation. A semaphore manages an internal counter which is decremented by each - acquire() call and incremented by each release() call. The counter - can never go below zero; when acquire() finds that it is zero, it blocks, - waiting until some other thread calls release(). + acquire() call and incremented by each release() call. The counter + can never go below zero; when acquire() finds that it is zero, it + blocks, waiting until some other thread calls release(). Semaphores also support the context management protocol. @@ -508,8 +508,8 @@ async def __aexit__(self, *args): async def wait(self): """Wait for the barrier. - When the specified number of tasks have started waiting, they are all - simultaneously awoken. + When the specified number of tasks have started waiting, they are + all simultaneously awoken. Returns an unique and individual index number from 0 to 'parties-1'. """ async with self._cond: diff --git a/Lib/asyncio/queues.py b/Lib/asyncio/queues.py index 756216fac809329..30004f2bc9bacd2 100644 --- a/Lib/asyncio/queues.py +++ b/Lib/asyncio/queues.py @@ -33,9 +33,9 @@ class QueueShutDown(Exception): class Queue(mixins._LoopBoundMixin): """A queue, useful for coordinating producer and consumer coroutines. - If maxsize is less than or equal to zero, the queue size is infinite. If it - is an integer greater than 0, then "await put()" will block when the - queue reaches maxsize, until an item is removed by get(). + If maxsize is less than or equal to zero, the queue size is infinite. + If it is an integer greater than 0, then "await put()" will block when + the queue reaches maxsize, until an item is removed by get(). Unlike queue.Queue, you can reliably know this Queue's size with qsize(), since your single-threaded asyncio application won't be @@ -174,8 +174,8 @@ async def get(self): If queue is empty, wait until an item is available. - Raises QueueShutDown if the queue has been shut down and is empty, or - if the queue has been shut down immediately. + Raises QueueShutDown if the queue has been shut down and is empty, + or if the queue has been shut down immediately. """ while self.empty(): if self._is_shutdown and self.empty(): @@ -203,10 +203,11 @@ async def get(self): def get_nowait(self): """Remove and return an item from the queue. - Return an item if one is immediately available, else raise QueueEmpty. + Return an item if one is immediately available, else raise + QueueEmpty. - Raises QueueShutDown if the queue has been shut down and is empty, or - if the queue has been shut down immediately. + Raises QueueShutDown if the queue has been shut down and is empty, + or if the queue has been shut down immediately. """ if self.empty(): if self._is_shutdown: @@ -223,12 +224,12 @@ def task_done(self): a subsequent call to task_done() tells the queue that the processing on the task is complete. - If a join() is currently blocking, it will resume when all items have - been processed (meaning that a task_done() call was received for every - item that had been put() into the queue). + If a join() is currently blocking, it will resume when all items + have been processed (meaning that a task_done() call was received + for every item that had been put() into the queue). - Raises ValueError if called more times than there were items placed in - the queue. + Raises ValueError if called more times than there were items placed + in the queue. """ if self._unfinished_tasks <= 0: raise ValueError('task_done() called too many times') @@ -239,10 +240,11 @@ def task_done(self): async def join(self): """Block until all items in the queue have been gotten and processed. - The count of unfinished tasks goes up whenever an item is added to the - queue. The count goes down whenever a consumer calls task_done() to - indicate that the item was retrieved and all work on it is complete. - When the count of unfinished tasks drops to zero, join() unblocks. + The count of unfinished tasks goes up whenever an item is added to + the queue. The count goes down whenever a consumer calls + task_done() to indicate that the item was retrieved and all work on + it is complete. When the count of unfinished tasks drops to zero, + join() unblocks. """ if self._unfinished_tasks > 0: await self._finished.wait() diff --git a/Lib/asyncio/runners.py b/Lib/asyncio/runners.py index ba37e003a655c0a..774a317564df22c 100644 --- a/Lib/asyncio/runners.py +++ b/Lib/asyncio/runners.py @@ -35,7 +35,8 @@ class Runner: with asyncio.Runner(debug=True) as runner: runner.run(main()) - The run() method can be called multiple times within the runner's context. + The run() method can be called multiple times within the runner's + context. This can be useful for interactive console (e.g. IPython), unittest runners, console tools, -- everywhere when async code diff --git a/Lib/asyncio/selector_events.py b/Lib/asyncio/selector_events.py index fa04c1db36d33c0..4fa4dd5a885236e 100644 --- a/Lib/asyncio/selector_events.py +++ b/Lib/asyncio/selector_events.py @@ -533,11 +533,12 @@ def _sock_recvfrom_into(self, fut, sock, buf, bufsize): async def sock_sendall(self, sock, data): """Send data to the socket. - The socket must be connected to a remote socket. This method continues - to send data from data until either all data has been sent or an - error occurs. None is returned on success. On error, an exception is - raised, and there is no way to determine how much data, if any, was - successfully processed by the receiving end of the connection. + The socket must be connected to a remote socket. This method + continues to send data from data until either all data has been + sent or an error occurs. None is returned on success. On error, + an exception is raised, and there is no way to determine how much + data, if any, was successfully processed by the receiving end of + the connection. """ base_events._check_ssl_socket(sock) if self._debug and sock.gettimeout() != 0: @@ -586,11 +587,12 @@ def _sock_sendall(self, fut, sock, view, pos): async def sock_sendto(self, sock, data, address): """Send data to the socket. - The socket must be connected to a remote socket. This method continues - to send data from data until either all data has been sent or an - error occurs. None is returned on success. On error, an exception is - raised, and there is no way to determine how much data, if any, was - successfully processed by the receiving end of the connection. + The socket must be connected to a remote socket. This method + continues to send data from data until either all data has been + sent or an error occurs. None is returned on success. On error, + an exception is raised, and there is no way to determine how much + data, if any, was successfully processed by the receiving end of + the connection. """ base_events._check_ssl_socket(sock) if self._debug and sock.gettimeout() != 0: @@ -701,10 +703,11 @@ def _sock_connect_cb(self, fut, sock, address): async def sock_accept(self, sock): """Accept a connection. - The socket must be bound to an address and listening for connections. - The return value is a pair (conn, address) where conn is a new socket - object usable to send and receive data on the connection, and address - is the address bound to the socket on the other end of the connection. + The socket must be bound to an address and listening for + connections. The return value is a pair (conn, address) where + conn is a new socket object usable to send and receive data on the + connection, and address is the address bound to the socket on the + other end of the connection. """ base_events._check_ssl_socket(sock) if self._debug and sock.gettimeout() != 0: diff --git a/Lib/asyncio/streams.py b/Lib/asyncio/streams.py index d2db1a930c2ad2b..a28c11e928f806a 100644 --- a/Lib/asyncio/streams.py +++ b/Lib/asyncio/streams.py @@ -539,17 +539,17 @@ async def _wait_for_data(self, func_name): self._waiter = None async def readline(self): - """Read chunk of data from the stream until newline (b'\n') is found. + r"""Read chunk of data from the stream until newline (b'\n') is found. - On success, return chunk that ends with newline. If only partial + On success, return chunk that ends with newline. If only partial line can be read due to EOF, return incomplete line without - terminating newline. When EOF was reached while no bytes read, empty - bytes object is returned. + terminating newline. When EOF was reached while no bytes read, + empty bytes object is returned. - If limit is reached, ValueError will be raised. In that case, if + If limit is reached, ValueError will be raised. In that case, if newline was found, complete line including newline will be removed - from internal buffer. Else, internal buffer will be cleared. Limit is - compared against part of the line without newline. + from internal buffer. Else, internal buffer will be cleared. + Limit is compared against part of the line without newline. If stream was paused, this function will automatically resume it if needed. diff --git a/Lib/asyncio/taskgroups.py b/Lib/asyncio/taskgroups.py index 45dfebc65904fce..e1ec025791a52e6 100644 --- a/Lib/asyncio/taskgroups.py +++ b/Lib/asyncio/taskgroups.py @@ -289,14 +289,14 @@ def cancel(self): """Cancel the task group `cancel()` will be called on any tasks in the group that aren't yet - done, as well as the parent (body) of the group. This will cause the - task group context manager to exit *without* `asyncio.CancelledError` - being raised. - - If `cancel()` is called before entering the task group, the group will be - cancelled upon entry. This is useful for patterns where one piece of - code passes an unused TaskGroup instance to another in order to have - the ability to cancel anything run within the group. + done, as well as the parent (body) of the group. This will cause + the task group context manager to exit *without* + `asyncio.CancelledError` being raised. + + If `cancel()` is called before entering the task group, the group + will be cancelled upon entry. This is useful for patterns where + one piece of code passes an unused TaskGroup instance to another in + order to have the ability to cancel anything run within the group. `cancel()` is idempotent and may be called after the task group has already exited. diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py index fbd5c39a7c56ac4..14d3c1ceb58cacb 100644 --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -624,10 +624,11 @@ def as_completed(fs, *, timeout=None): Run the supplied awaitables concurrently. The returned object can be iterated to obtain the results of the awaitables as they finish. - The object returned can be iterated as an asynchronous iterator or a plain - iterator. When asynchronous iteration is used, the originally-supplied - awaitables are yielded if they are tasks or futures. This makes it easy to - correlate previously-scheduled tasks with their results: + The object returned can be iterated as an asynchronous iterator or + a plain iterator. When asynchronous iteration is used, the + originally-supplied awaitables are yielded if they are tasks or + futures. This makes it easy to correlate previously-scheduled tasks + with their results: ipv4_connect = create_task(open_connection("127.0.0.1", 80)) ipv6_connect = create_task(open_connection("::1", 80)) @@ -643,26 +644,27 @@ def as_completed(fs, *, timeout=None): else: print("IPv4 connection established.") - During asynchronous iteration, implicitly-created tasks will be yielded for - supplied awaitables that aren't tasks or futures. + During asynchronous iteration, implicitly-created tasks will be + yielded for supplied awaitables that aren't tasks or futures. - When used as a plain iterator, each iteration yields a new coroutine that - returns the result or raises the exception of the next completed awaitable. - This pattern is compatible with Python versions older than 3.13: + When used as a plain iterator, each iteration yields a new coroutine + that returns the result or raises the exception of the next completed + awaitable. This pattern is compatible with Python versions older than + 3.13: ipv4_connect = create_task(open_connection("127.0.0.1", 80)) ipv6_connect = create_task(open_connection("::1", 80)) tasks = [ipv4_connect, ipv6_connect] for next_connect in as_completed(tasks): - # next_connect is not one of the original task objects. It must be - # awaited to obtain the result value or raise the exception of the - # awaitable that finishes next. + # next_connect is not one of the original task objects. It must + # be awaited to obtain the result value or raise the exception + # of the awaitable that finishes next. reader, writer = await next_connect - A TimeoutError is raised if the timeout occurs before all awaitables are - done. This is raised by the async for loop during asynchronous iteration or - by the coroutines yielded during plain iteration. + A TimeoutError is raised if the timeout occurs before all awaitables + are done. This is raised by the async for loop during asynchronous + iteration or by the coroutines yielded during plain iteration. """ if inspect.isawaitable(fs): raise TypeError( @@ -1030,21 +1032,22 @@ def callback(): def create_eager_task_factory(custom_task_constructor): """Create a function suitable for use as a task factory on an event-loop. - Example usage: + Example usage: - loop.set_task_factory( - asyncio.create_eager_task_factory(my_task_constructor)) + loop.set_task_factory( + asyncio.create_eager_task_factory(my_task_constructor)) - Now, tasks created will be started immediately (rather than being first - scheduled to an event loop). The constructor argument can be any callable - that returns a Task-compatible object and has a signature compatible - with `Task.__init__`; it must have the `eager_start` keyword argument. + Now, tasks created will be started immediately (rather than being first + scheduled to an event loop). The constructor argument can be any + callable that returns a Task-compatible object and has a signature + compatible with `Task.__init__`; it must have the `eager_start` + keyword argument. - Most applications will use `Task` for `custom_task_constructor` and in - this case there's no need to call `create_eager_task_factory()` - directly. Instead the global `eager_task_factory` instance can be - used. E.g. `loop.set_task_factory(asyncio.eager_task_factory)`. - """ + Most applications will use `Task` for `custom_task_constructor` and in + this case there's no need to call `create_eager_task_factory()` + directly. Instead the global `eager_task_factory` instance can be + used. E.g. `loop.set_task_factory(asyncio.eager_task_factory)`. + """ def factory(loop, coro, *, eager_start=True, **kwargs): return custom_task_constructor( diff --git a/Lib/asyncio/threads.py b/Lib/asyncio/threads.py index db048a8231de166..5001351b0976ecd 100644 --- a/Lib/asyncio/threads.py +++ b/Lib/asyncio/threads.py @@ -17,7 +17,8 @@ async def to_thread(func, /, *args, **kwargs): allowing context variables from the main thread to be accessed in the separate thread. - Return a coroutine that can be awaited to get the eventual result of *func*. + Return a coroutine that can be awaited to get the eventual result of + *func*. """ loop = events.get_running_loop() ctx = contextvars.copy_context() diff --git a/Lib/asyncio/timeouts.py b/Lib/asyncio/timeouts.py index 09342dc7c1310b0..65ddc285abd971d 100644 --- a/Lib/asyncio/timeouts.py +++ b/Lib/asyncio/timeouts.py @@ -25,7 +25,8 @@ class _State(enum.Enum): class Timeout: """Asynchronous context manager for cancelling overdue coroutines. - Use `timeout()` or `timeout_at()` rather than instantiating this class directly. + Use `timeout()` or `timeout_at()` rather than instantiating this class + directly. """ def __init__(self, when: float | None) -> None: diff --git a/Lib/asyncio/unix_events.py b/Lib/asyncio/unix_events.py index 646ae71bbf5919e..93c5802cd44460d 100644 --- a/Lib/asyncio/unix_events.py +++ b/Lib/asyncio/unix_events.py @@ -55,7 +55,8 @@ def waitstatus_to_exitcode(status): class _UnixSelectorEventLoop(selector_events.BaseSelectorEventLoop): """Unix event loop. - Adds signal handling and UNIX Domain Socket support to SelectorEventLoop. + Adds signal handling and UNIX Domain Socket support to + SelectorEventLoop. """ def __init__(self, selector=None): From ae445aac4516039e250b24a124b34e41fd538543 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 9 Jun 2026 19:14:10 +0300 Subject: [PATCH 082/185] gh-150285: Fix too long docstrings in the concurrent package (GH-151076) --- Lib/concurrent/futures/_base.py | 112 +++++++++++++------------ Lib/concurrent/futures/interpreter.py | 4 +- Lib/concurrent/futures/process.py | 49 ++++++----- Lib/concurrent/interpreters/_queues.py | 3 +- 4 files changed, 89 insertions(+), 79 deletions(-) diff --git a/Lib/concurrent/futures/_base.py b/Lib/concurrent/futures/_base.py index f506ce68aea5b29..4e71331f9a0a594 100644 --- a/Lib/concurrent/futures/_base.py +++ b/Lib/concurrent/futures/_base.py @@ -194,15 +194,15 @@ def as_completed(fs, timeout=None): """An iterator over the given futures that yields each as it completes. Args: - fs: The sequence of Futures (possibly created by different Executors) to - iterate over. - timeout: The maximum number of seconds to wait. If None, then there - is no limit on the wait time. + fs: The sequence of Futures (possibly created by different + Executors) to iterate over. + timeout: The maximum number of seconds to wait. If None, then + there is no limit on the wait time. Returns: - An iterator that yields the given Futures as they complete (finished or - cancelled). If any given Futures are duplicated, they will be returned - once. + An iterator that yields the given Futures as they complete + (finished or cancelled). If any given Futures are duplicated, + they will be returned once. Raises: TimeoutError: If the entire result iterator could not be generated @@ -258,19 +258,20 @@ def wait(fs, timeout=None, return_when=ALL_COMPLETED): """Wait for the futures in the given sequence to complete. Args: - fs: The sequence of Futures (possibly created by different Executors) to - wait upon. - timeout: The maximum number of seconds to wait. If None, then there - is no limit on the wait time. - return_when: Indicates when this function should return. The options - are: + fs: The sequence of Futures (possibly created by different + Executors) to wait upon. + timeout: The maximum number of seconds to wait. If None, then + there is no limit on the wait time. + return_when: Indicates when this function should return. + The options are: FIRST_COMPLETED - Return when any future finishes or is cancelled. FIRST_EXCEPTION - Return when any future finishes by raising an - exception. If no future raises an exception + exception. If no future raises an exception then it is equivalent to ALL_COMPLETED. - ALL_COMPLETED - Return when all futures finish or are cancelled. + ALL_COMPLETED - Return when all futures finish or are + cancelled. Returns: A named 2-tuple of sets. The first set, named 'done', contains the @@ -404,11 +405,12 @@ def add_done_callback(self, fn): Args: fn: A callable that will be called with this future as its only - argument when the future completes or is cancelled. The callable - will always be called by a thread in the same process in which - it was added. If the future has already completed or been - cancelled then the callable will be called immediately. These - callables are called in the order that they were added. + argument when the future completes or is cancelled. The + callable will always be called by a thread in the same + process in which it was added. If the future has already + completed or been cancelled then the callable will be + called immediately. These callables are called in the + order that they were added. """ with self._condition: if self._state not in [CANCELLED, CANCELLED_AND_NOTIFIED, FINISHED]: @@ -423,17 +425,19 @@ def result(self, timeout=None): """Return the result of the call that the future represents. Args: - timeout: The number of seconds to wait for the result if the future - isn't done. If None, then there is no limit on the wait time. + timeout: The number of seconds to wait for the result if the + future isn't done. If None, then there is no limit on the + wait time. Returns: The result of the call that the future represents. Raises: CancelledError: If the future was cancelled. - TimeoutError: If the future didn't finish executing before the given - timeout. - Exception: If the call raised then that exception will be raised. + TimeoutError: If the future didn't finish executing before the + given timeout. + Exception: If the call raised then that exception will be + raised. """ try: with self._condition: @@ -459,17 +463,17 @@ def exception(self, timeout=None): Args: timeout: The number of seconds to wait for the exception if the - future isn't done. If None, then there is no limit on the wait - time. + future isn't done. If None, then there is no limit on the + wait time. Returns: - The exception raised by the call that the future represents or None - if the call completed without raising. + The exception raised by the call that the future represents or + None if the call completed without raising. Raises: CancelledError: If the future was cancelled. - TimeoutError: If the future didn't finish executing before the given - timeout. + TimeoutError: If the future didn't finish executing before the + given timeout. """ with self._condition: @@ -494,22 +498,23 @@ def set_running_or_notify_cancel(self): Should only be used by Executor implementations and unit tests. If the future has been cancelled (cancel() was called and returned - True) then any threads waiting on the future completing (though calls - to as_completed() or wait()) are notified and False is returned. + True) then any threads waiting on the future completing (though + calls to as_completed() or wait()) are notified and False is + returned. If the future was not cancelled then it is put in the running state (future calls to running() will return True) and True is returned. This method should be called by Executor implementations before - executing the work associated with this future. If this method returns - False then the work should not be executed. + executing the work associated with this future. If this method + returns False then the work should not be executed. Returns: False if the Future was cancelled, True otherwise. Raises: - RuntimeError: if this method was already called or if set_result() - or set_exception() was called. + RuntimeError: if this method was already called or if + set_result() or set_exception() was called. """ with self._condition: if self._state == CANCELLED: @@ -593,8 +598,9 @@ class Executor(object): def submit(self, fn, /, *args, **kwargs): """Submits a callable to be executed with the given arguments. - Schedules the callable to be executed as fn(*args, **kwargs) and returns - a Future instance representing the execution of the callable. + Schedules the callable to be executed as fn(*args, **kwargs) and + returns a Future instance representing the execution of the + callable. Returns: A Future representing the given call. @@ -607,25 +613,25 @@ def map(self, fn, *iterables, timeout=None, chunksize=1, buffersize=None): Args: fn: A callable that will take as many arguments as there are passed iterables. - timeout: The maximum number of seconds to wait. If None, then there - is no limit on the wait time. - chunksize: The size of the chunks the iterable will be broken into - before being passed to a child process. This argument is only - used by ProcessPoolExecutor; it is ignored by + timeout: The maximum number of seconds to wait. If None, then + there is no limit on the wait time. + chunksize: The size of the chunks the iterable will be broken + into before being passed to a child process. This argument + is only used by ProcessPoolExecutor; it is ignored by ThreadPoolExecutor. buffersize: The number of submitted tasks whose results have not - yet been yielded. If the buffer is full, iteration over the + yet been yielded. If the buffer is full, iteration over the iterables pauses until a result is yielded from the buffer. - If None, all input elements are eagerly collected, and a task is - submitted for each. + If None, all input elements are eagerly collected, and + a task is submitted for each. Returns: - An iterator equivalent to: map(func, *iterables) but the calls may - be evaluated out-of-order. + An iterator equivalent to: map(func, *iterables) but the calls + may be evaluated out-of-order. Raises: - TimeoutError: If the entire result iterator could not be generated - before the given timeout. + TimeoutError: If the entire result iterator could not be + generated before the given timeout. Exception: If fn(*args) raises for any values. """ if buffersize is not None and not isinstance(buffersize, int): @@ -679,8 +685,8 @@ def shutdown(self, wait=True, *, cancel_futures=False): Args: wait: If True then shutdown will not return until all running - futures have finished executing and the resources used by the - executor have been reclaimed. + futures have finished executing and the resources used by + the executor have been reclaimed. cancel_futures: If True then shutdown will cancel all pending futures. Futures that are completed or running will not be cancelled. diff --git a/Lib/concurrent/futures/interpreter.py b/Lib/concurrent/futures/interpreter.py index 85c1da2c7228943..fd3d45144b49a71 100644 --- a/Lib/concurrent/futures/interpreter.py +++ b/Lib/concurrent/futures/interpreter.py @@ -110,8 +110,8 @@ def __init__(self, max_workers=None, thread_name_prefix='', """Initializes a new InterpreterPoolExecutor instance. Args: - max_workers: The maximum number of interpreters that can be used to - execute the given calls. + max_workers: The maximum number of interpreters that can be used + to execute the given calls. thread_name_prefix: An optional name prefix to give our threads. initializer: A callable or script used to initialize each worker interpreter. diff --git a/Lib/concurrent/futures/process.py b/Lib/concurrent/futures/process.py index a42afa68efcb148..ed24cc250434130 100644 --- a/Lib/concurrent/futures/process.py +++ b/Lib/concurrent/futures/process.py @@ -656,19 +656,21 @@ def __init__(self, max_workers=None, mp_context=None, Args: max_workers: The maximum number of processes that can be used to - execute the given calls. If None or not given then as many - worker processes will be created as the machine has processors. - mp_context: A multiprocessing context to launch the workers created - using the multiprocessing.get_context('start method') API. This - object should provide SimpleQueue, Queue and Process. + execute the given calls. If None or not given then as many + worker processes will be created as the machine has + processors. + mp_context: A multiprocessing context to launch the workers + created using the multiprocessing.get_context('start method') + API. This object should provide SimpleQueue, Queue and + Process. initializer: A callable used to initialize worker processes. initargs: A tuple of arguments to pass to the initializer. - max_tasks_per_child: The maximum number of tasks a worker process - can complete before it will exit and be replaced with a fresh - worker process. The default of None means worker process will - live as long as the executor. Requires a non-'fork' mp_context - start method. When given, we default to using 'spawn' if no - mp_context is supplied. + max_tasks_per_child: The maximum number of tasks a worker + process can complete before it will exit and be replaced + with a fresh worker process. The default of None means + worker process will live as long as the executor. Requires + a non-'fork' mp_context start method. When given, we + default to using 'spawn' if no mp_context is supplied. """ _check_system_limits() @@ -838,24 +840,25 @@ def map(self, fn, *iterables, timeout=None, chunksize=1, buffersize=None): Args: fn: A callable that will take as many arguments as there are passed iterables. - timeout: The maximum number of seconds to wait. If None, then there - is no limit on the wait time. - chunksize: If greater than one, the iterables will be chopped into - chunks of size chunksize and submitted to the process pool. - If set to one, the items in the list will be sent one at a time. + timeout: The maximum number of seconds to wait. If None, then + there is no limit on the wait time. + chunksize: If greater than one, the iterables will be chopped + into chunks of size chunksize and submitted to the process + pool. If set to one, the items in the list will be sent + one at a time. buffersize: The number of submitted tasks whose results have not - yet been yielded. If the buffer is full, iteration over the + yet been yielded. If the buffer is full, iteration over the iterables pauses until a result is yielded from the buffer. - If None, all input elements are eagerly collected, and a task is - submitted for each. + If None, all input elements are eagerly collected, and + a task is submitted for each. Returns: - An iterator equivalent to: map(func, *iterables) but the calls may - be evaluated out-of-order. + An iterator equivalent to: map(func, *iterables) but the calls + may be evaluated out-of-order. Raises: - TimeoutError: If the entire result iterator could not be generated - before the given timeout. + TimeoutError: If the entire result iterator could not be + generated before the given timeout. Exception: If fn(*args) raises for any values. """ if chunksize < 1: diff --git a/Lib/concurrent/interpreters/_queues.py b/Lib/concurrent/interpreters/_queues.py index ee159d7de638272..5f3ee0934de59d6 100644 --- a/Lib/concurrent/interpreters/_queues.py +++ b/Lib/concurrent/interpreters/_queues.py @@ -185,7 +185,8 @@ def put(self, obj, block=True, timeout=None, *, underlying data is actually shared. Furthermore, some types can be sent through a queue more efficiently than others. This group includes various immutable types like int, str, bytes, and - tuple (if the items are likewise efficiently shareable). See interpreters.is_shareable(). + tuple (if the items are likewise efficiently shareable). + See interpreters.is_shareable(). "unbounditems" controls the behavior of Queue.get() for the given object if the current interpreter (calling put()) is later From 7e96037d11b60991fcdf585d53ba1c8469a2e36e Mon Sep 17 00:00:00 2001 From: sobolevn Date: Tue, 9 Jun 2026 19:42:08 +0300 Subject: [PATCH 083/185] gh-151126: Add missing `PyErr_NoMemory` in `_winapi` module (#151154) --- .../2026-06-09-10-28-30.gh-issue-151126.DKa6Sl.rst | 8 ++++++-- Modules/_winapi.c | 5 ++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-09-10-28-30.gh-issue-151126.DKa6Sl.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-09-10-28-30.gh-issue-151126.DKa6Sl.rst index 3f699a50d7a42bb..81e87e539865ce3 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-09-10-28-30.gh-issue-151126.DKa6Sl.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-09-10-28-30.gh-issue-151126.DKa6Sl.rst @@ -1,3 +1,7 @@ Fix a crash, when there's no memory left on a device, -which happened in code compilation. -Now it raises a proper :exc:`MemoryError`. +which happened in: + +- code compilation +- :func:`!_winapi.CreateProcess` + +Now these places raise proper :exc:`MemoryError` errors. diff --git a/Modules/_winapi.c b/Modules/_winapi.c index fc2c0890468a6b9..369a7400eb63b90 100644 --- a/Modules/_winapi.c +++ b/Modules/_winapi.c @@ -1194,8 +1194,10 @@ gethandlelist(PyObject *mapping, const char *name, Py_ssize_t *size) } ret = PyMem_Malloc(*size); - if (ret == NULL) + if (ret == NULL) { + PyErr_NoMemory(); goto cleanup; + } for (i = 0; i < PySequence_Fast_GET_SIZE(value_fast); i++) { ret[i] = PYNUM_TO_HANDLE(PySequence_Fast_GET_ITEM(value_fast, i)); @@ -1278,6 +1280,7 @@ getattributelist(PyObject *obj, const char *name, AttributeList *attribute_list) attribute_list->attribute_list = PyMem_Malloc(attribute_list_size); if (attribute_list->attribute_list == NULL) { ret = -1; + PyErr_NoMemory(); goto cleanup; } From 28df50dbe17d66b550e191f2b379ce7657a2e5ce Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Tue, 9 Jun 2026 10:07:03 -0700 Subject: [PATCH 084/185] GH-59633: Clarify dest collisions in argparse docs (#150987) --- Doc/library/argparse.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Doc/library/argparse.rst b/Doc/library/argparse.rst index 4c588d447a9a7cf..e4a5f4d109b4992 100644 --- a/Doc/library/argparse.rst +++ b/Doc/library/argparse.rst @@ -1372,6 +1372,11 @@ behavior:: >>> parser.parse_args('--foo XXX'.split()) Namespace(bar='XXX') +Multiple arguments may share the same ``dest``. By default, the value from the +last such argument given on the command line wins. Use ``action='append'`` to +collect values from all of them into a list instead. For conflicting *option +strings* rather than ``dest`` names, see conflict_handler_. + .. versionchanged:: 3.15 Single-dash long option now takes precedence over short options. @@ -1780,6 +1785,11 @@ Subcommands present, and when the ``b`` command is specified, only the ``foo`` and ``baz`` attributes are present. + If a subparser defines an argument with the same ``dest`` as the parent + parser, the two share a single namespace attribute, so the parent's value + won't be retained. Users should give them distinct ``dest`` values to + keep both. + Similarly, when a help message is requested from a subparser, only the help for that particular parser will be printed. The help message will not include parent parser or sibling parser messages. (A help message for each From 7ddea21635701aeb015b5de66cd7133e43edaaf0 Mon Sep 17 00:00:00 2001 From: Zachary Ware Date: Tue, 9 Jun 2026 12:09:19 -0500 Subject: [PATCH 085/185] gh-151159: Update Windows builds to use OpenSSL 3.5.7 (GH-151183) --- .../2026-06-09-11-40-48.gh-issue-151159.JKVfme.rst | 1 + Misc/externals.spdx.json | 8 ++++---- PCbuild/get_externals.bat | 4 ++-- PCbuild/python.props | 4 ++-- 4 files changed, 9 insertions(+), 8 deletions(-) create mode 100644 Misc/NEWS.d/next/Windows/2026-06-09-11-40-48.gh-issue-151159.JKVfme.rst diff --git a/Misc/NEWS.d/next/Windows/2026-06-09-11-40-48.gh-issue-151159.JKVfme.rst b/Misc/NEWS.d/next/Windows/2026-06-09-11-40-48.gh-issue-151159.JKVfme.rst new file mode 100644 index 000000000000000..ad1be115db5ce8f --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2026-06-09-11-40-48.gh-issue-151159.JKVfme.rst @@ -0,0 +1 @@ +Updated bundled version of OpenSSL to 3.5.7. diff --git a/Misc/externals.spdx.json b/Misc/externals.spdx.json index 080330c1cb75a53..dd49036a5360796 100644 --- a/Misc/externals.spdx.json +++ b/Misc/externals.spdx.json @@ -70,21 +70,21 @@ "checksums": [ { "algorithm": "SHA256", - "checksumValue": "cf01946f3a61ba45a08c1e35b223d41d23963e3df5ac98cbad6c8fa5a81070ca" + "checksumValue": "ca94e7c6c223d9caf77bb51aac5949186379608ea2a0cad3aa8bdf31856912e9" } ], - "downloadLocation": "https://github.com/python/cpython-source-deps/archive/refs/tags/openssl-3.5.6.tar.gz", + "downloadLocation": "https://github.com/python/cpython-source-deps/archive/refs/tags/openssl-3.5.7.tar.gz", "externalRefs": [ { "referenceCategory": "SECURITY", - "referenceLocator": "cpe:2.3:a:openssl:openssl:3.5.6:*:*:*:*:*:*:*", + "referenceLocator": "cpe:2.3:a:openssl:openssl:3.5.7:*:*:*:*:*:*:*", "referenceType": "cpe23Type" } ], "licenseConcluded": "NOASSERTION", "name": "openssl", "primaryPackagePurpose": "SOURCE", - "versionInfo": "3.5.6" + "versionInfo": "3.5.7" }, { "SPDXID": "SPDXRef-PACKAGE-sqlite", diff --git a/PCbuild/get_externals.bat b/PCbuild/get_externals.bat index f6ba3d0fef3a60b..f23a38ff6757d0f 100644 --- a/PCbuild/get_externals.bat +++ b/PCbuild/get_externals.bat @@ -54,7 +54,7 @@ echo.Fetching external libraries... set libraries= set libraries=%libraries% bzip2-1.0.8 if NOT "%IncludeLibffiSrc%"=="false" set libraries=%libraries% libffi-3.4.4 -if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-3.5.6 +if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-3.5.7 set libraries=%libraries% mpdecimal-4.0.0 set libraries=%libraries% sqlite-3.53.1.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tcl-9.0.3.0 @@ -79,7 +79,7 @@ echo.Fetching external binaries... set binaries= if NOT "%IncludeLibffi%"=="false" set binaries=%binaries% libffi-3.4.4 -if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-3.5.6 +if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-3.5.7 if NOT "%IncludeTkinter%"=="false" set binaries=%binaries% tcltk-9.0.3.0 if NOT "%IncludeSSLSrc%"=="false" set binaries=%binaries% nasm-2.11.06 if NOT "%IncludeLLVM%"=="false" set binaries=%binaries% llvm-21.1.4.0 diff --git a/PCbuild/python.props b/PCbuild/python.props index edcda8fd8fc55d9..86b88bf9ea75b4e 100644 --- a/PCbuild/python.props +++ b/PCbuild/python.props @@ -105,8 +105,8 @@ $(libffiDir)$(ArchName)\ $(libffiOutDir)include $(ExternalsDir)\mpdecimal-4.0.0\ - $(ExternalsDir)openssl-3.5.6\ - $(ExternalsDir)openssl-bin-3.5.6\$(ArchName)\ + $(ExternalsDir)openssl-3.5.7\ + $(ExternalsDir)openssl-bin-3.5.7\$(ArchName)\ $(opensslOutDir)include $(ExternalsDir)\nasm-2.11.06\ $(ExternalsDir)\zlib-1.3.1\ From bc0a1748a68ec57c188f67fc88319f2f36c855d0 Mon Sep 17 00:00:00 2001 From: Ivy Xu Date: Wed, 10 Jun 2026 01:12:36 +0800 Subject: [PATCH 086/185] gh-151159: Update macOS installer to use OpenSSL 3.5.7. (#151171) --- Mac/BuildScript/build-installer.py | 6 +++--- .../Security/2026-06-09-23-38-08.gh-issue-151159.ds-9f8.rst | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Security/2026-06-09-23-38-08.gh-issue-151159.ds-9f8.rst diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py index d4df8fbc42ddcba..9c54cfe75c12f72 100755 --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -246,9 +246,9 @@ def library_recipes(): result.extend([ dict( - name="OpenSSL 3.5.6", - url="https://github.com/openssl/openssl/releases/download/openssl-3.5.6/openssl-3.5.6.tar.gz", - checksum="deae7c80cba99c4b4f940ecadb3c3338b13cb77418409238e57d7f31f2a3b736", + name="OpenSSL 3.5.7", + url="https://github.com/openssl/openssl/releases/download/openssl-3.5.7/openssl-3.5.7.tar.gz", + checksum="a8c0d28a529ca480f9f36cf5792e2cd21984552a3c8e4aa11a24aa31aeac98e8", buildrecipe=build_universal_openssl, configure=None, install=None, diff --git a/Misc/NEWS.d/next/Security/2026-06-09-23-38-08.gh-issue-151159.ds-9f8.rst b/Misc/NEWS.d/next/Security/2026-06-09-23-38-08.gh-issue-151159.ds-9f8.rst new file mode 100644 index 000000000000000..d9251a93b40b2cc --- /dev/null +++ b/Misc/NEWS.d/next/Security/2026-06-09-23-38-08.gh-issue-151159.ds-9f8.rst @@ -0,0 +1 @@ +Update macOS installer to use OpenSSL 3.5.7. From b2f8b9d8d32cd043015a1347f4e28815cd04a539 Mon Sep 17 00:00:00 2001 From: Zachary Ware Date: Tue, 9 Jun 2026 12:26:13 -0500 Subject: [PATCH 087/185] gh-151159: Bump OpenSSL versions for iOS and Android (GH-151164) --- .../Security/2026-06-09-10-23-57.gh-issue-151159.91GpWQ.rst | 1 + Platforms/Android/__main__.py | 2 +- Platforms/Apple/__main__.py | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Security/2026-06-09-10-23-57.gh-issue-151159.91GpWQ.rst diff --git a/Misc/NEWS.d/next/Security/2026-06-09-10-23-57.gh-issue-151159.91GpWQ.rst b/Misc/NEWS.d/next/Security/2026-06-09-10-23-57.gh-issue-151159.91GpWQ.rst new file mode 100644 index 000000000000000..735164c1a65ec33 --- /dev/null +++ b/Misc/NEWS.d/next/Security/2026-06-09-10-23-57.gh-issue-151159.91GpWQ.rst @@ -0,0 +1 @@ +Update Android and iOS installers to use OpenSSL 3.5.7. diff --git a/Platforms/Android/__main__.py b/Platforms/Android/__main__.py index 5c41aaca6ebf0b4..f738a198e1a4121 100755 --- a/Platforms/Android/__main__.py +++ b/Platforms/Android/__main__.py @@ -219,7 +219,7 @@ def unpack_deps(host, prefix_dir, cache_dir): for name_ver in [ "bzip2-1.0.8-3", "libffi-3.4.4-3", - "openssl-3.5.6-0", + "openssl-3.5.7-0", "sqlite-3.53.1-0", "xz-5.4.6-1", "zstd-1.5.7-2" diff --git a/Platforms/Apple/__main__.py b/Platforms/Apple/__main__.py index d94198a309f9269..9f2d2afb0aa0f67 100644 --- a/Platforms/Apple/__main__.py +++ b/Platforms/Apple/__main__.py @@ -319,7 +319,7 @@ def unpack_deps( for name_ver in [ "BZip2-1.0.8-2", "libFFI-3.4.7-2", - "OpenSSL-3.5.6-1", + "OpenSSL-3.5.7-1", "XZ-5.6.4-2", "mpdecimal-4.0.0-2", "zstd-1.5.7-1", From eb510da416b06a39b83fac36cfec7cfbc9af6c43 Mon Sep 17 00:00:00 2001 From: Zachary Ware Date: Tue, 9 Jun 2026 12:29:36 -0500 Subject: [PATCH 088/185] gh-151159: Update CI to use latest SSL library versions (#151176) --- .github/workflows/build.yml | 17 ++++++++--------- .github/workflows/reusable-ubuntu.yml | 2 +- Tools/ssl/multissltests.py | 14 +++++++------- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 47ea859c5fefbb9..43f8e0c010ed1bc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -278,14 +278,13 @@ jobs: # unsupported as it most resembles other 1.1.1-work-a-like ssl APIs # supported by important vendors such as AWS-LC. - { name: openssl, version: 1.1.1w } - - { name: openssl, version: 3.0.20 } - - { name: openssl, version: 3.3.7 } - - { name: openssl, version: 3.4.5 } - - { name: openssl, version: 3.5.6 } - - { name: openssl, version: 3.6.2 } - - { name: openssl, version: 4.0.0 } + - { name: openssl, version: 3.0.21 } + - { name: openssl, version: 3.4.6 } + - { name: openssl, version: 3.5.7 } + - { name: openssl, version: 3.6.3 } + - { name: openssl, version: 4.0.1 } ## AWS-LC - - { name: aws-lc, version: 1.72.1 } + - { name: aws-lc, version: 5.0.0 } env: SSLLIB_VER: ${{ matrix.ssllib.version }} MULTISSL_DIR: ${{ github.workspace }}/multissl @@ -399,7 +398,7 @@ jobs: needs: build-context if: needs.build-context.outputs.run-ubuntu == 'true' env: - OPENSSL_VER: 3.5.6 + OPENSSL_VER: 3.5.7 PYTHONSTRICTEXTENSIONBUILD: 1 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -507,7 +506,7 @@ jobs: matrix: os: [ubuntu-24.04] env: - OPENSSL_VER: 3.5.6 + OPENSSL_VER: 3.5.7 PYTHONSTRICTEXTENSIONBUILD: 1 ASAN_OPTIONS: detect_leaks=0:allocator_may_return_null=1:handle_segv=0 steps: diff --git a/.github/workflows/reusable-ubuntu.yml b/.github/workflows/reusable-ubuntu.yml index a7e307848af670c..f4321cefa1b5985 100644 --- a/.github/workflows/reusable-ubuntu.yml +++ b/.github/workflows/reusable-ubuntu.yml @@ -35,7 +35,7 @@ jobs: runs-on: ${{ inputs.os }} timeout-minutes: 60 env: - OPENSSL_VER: 3.5.6 + OPENSSL_VER: 3.5.7 PYTHONSTRICTEXTENSIONBUILD: 1 TERM: linux steps: diff --git a/Tools/ssl/multissltests.py b/Tools/ssl/multissltests.py index ad3e4b6596ed7ae..1a213187b897d1d 100755 --- a/Tools/ssl/multissltests.py +++ b/Tools/ssl/multissltests.py @@ -46,15 +46,15 @@ "1.1.1w", "3.1.8", "3.2.6", + "3.3.7", ] OPENSSL_RECENT_VERSIONS = [ - "3.0.20", - "3.3.7", - "3.4.5", - "3.5.6", - "3.6.2", - "4.0.0", + "3.0.21", + "3.4.6", + "3.5.7", + "3.6.3", + "4.0.1", # See make_ssl_data.py for notes on adding a new version. ] @@ -65,7 +65,7 @@ ] AWSLC_RECENT_VERSIONS = [ - "1.68.0", + "5.0.0", ] # store files in ../multissl From 53ccdd025079c0c88f04c3a4eacdfc07af229a4e Mon Sep 17 00:00:00 2001 From: Peter Date: Wed, 10 Jun 2026 02:00:23 +0800 Subject: [PATCH 089/185] =?UTF-8?q?gh-139819:=20rlcompleter=20=E2=80=93=20?= =?UTF-8?q?avoid=20suggesting=20attributes=20not=20accessible=20on=20insta?= =?UTF-8?q?nces=20(GH-139820)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ethan Furman --- Lib/rlcompleter.py | 11 ++++--- Lib/test/test_rlcompleter.py | 29 +++++++++++++++---- ...-10-09-12-13-29.gh-issue-139819.YxUDyH.rst | 3 ++ 3 files changed, 33 insertions(+), 10 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-10-09-12-13-29.gh-issue-139819.YxUDyH.rst diff --git a/Lib/rlcompleter.py b/Lib/rlcompleter.py index 6c6d9bb6b34244e..271b77a322fdd2b 100644 --- a/Lib/rlcompleter.py +++ b/Lib/rlcompleter.py @@ -40,6 +40,9 @@ __all__ = ["Completer"] +# Sentinel object to distinguish "missing" from "present but None" +_SENTINEL = object() + class Completer: def __init__(self, namespace = None): """Create a new completer for the command line. @@ -194,14 +197,14 @@ def attr_matches(self, text): and isinstance(thisobject.__dict__.get(word), types.LazyImportType) - ): + ): value = thisobject.__dict__.get(word) else: - value = getattr(thisobject, word, None) + value = getattr(thisobject, word, _SENTINEL) - if value is not None: + if value is not _SENTINEL: matches.append(self._callable_postfix(value, match)) - else: + elif word in getattr(type(thisobject), '__slots__', ()): matches.append(match) if matches or not noprefix: break diff --git a/Lib/test/test_rlcompleter.py b/Lib/test/test_rlcompleter.py index e6d727d417b2985..c0b5a4da8cb2569 100644 --- a/Lib/test/test_rlcompleter.py +++ b/Lib/test/test_rlcompleter.py @@ -107,19 +107,35 @@ def test_excessive_getattr(self): # we use __dir__ and __getattr__ in class Foo to create a "magic" # class attribute 'bar'. This forces `getattr` to call __getattr__ # (which is doesn't necessarily do). - class Foo: + # Test 1: Attribute returns None + class FooReturnsNone: calls = 0 - bar = '' + bar = None def __getattribute__(self, name): if name == 'bar': self.calls += 1 return None return super().__getattribute__(name) - f = Foo() - completer = rlcompleter.Completer(dict(f=f)) - self.assertEqual(completer.complete('f.b', 0), 'f.bar') - self.assertEqual(f.calls, 1) + f1 = FooReturnsNone() + completer1 = rlcompleter.Completer(dict(f=f1)) + self.assertEqual(completer1.complete('f.b', 0), 'f.bar') + self.assertEqual(f1.calls, 1) + + # Test 2: Attribute returns non-None value + class FooReturnsValue: + calls = 0 + bar = '' + def __getattribute__(self, name): + if name == 'bar': + self.calls += 1 + return '' + return super().__getattribute__(name) + + f2 = FooReturnsValue() + completer2 = rlcompleter.Completer(dict(f=f2)) + self.assertEqual(completer2.complete('f.b', 0), 'f.bar') + self.assertEqual(f2.calls, 1) def test_property_method_not_called(self): class Foo: @@ -196,6 +212,7 @@ class Foo: completer = rlcompleter.Completer(dict(f=Foo())) self.assertEqual(completer.complete('f.', 0), 'f.bar') + @unittest.mock.patch('rlcompleter._readline_available', False) def test_complete(self): completer = rlcompleter.Completer() diff --git a/Misc/NEWS.d/next/Library/2025-10-09-12-13-29.gh-issue-139819.YxUDyH.rst b/Misc/NEWS.d/next/Library/2025-10-09-12-13-29.gh-issue-139819.YxUDyH.rst new file mode 100644 index 000000000000000..ab3a8c6ed16ce4a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-10-09-12-13-29.gh-issue-139819.YxUDyH.rst @@ -0,0 +1,3 @@ +:mod:`rlcompleter`: Avoid suggesting attributes that are not accessible on +instances (e.g., Enum members showing ``__name__``). Patch by Peter +(ttw225). From bbd2b5f4dd05c670cd3413be65c42a2265f861f1 Mon Sep 17 00:00:00 2001 From: Zachary Ware Date: Tue, 9 Jun 2026 13:27:26 -0500 Subject: [PATCH 090/185] gh-151163: Update Android, macOS installer, and Windows builds to SQLite 3.53.2 (GH-151187) --- Mac/BuildScript/build-installer.py | 6 +++--- .../Build/2026-06-09-11-54-13.gh-issue-151163.vFAtjv.rst | 1 + .../2026-04-26-23-14-45.gh-issue-149029.oPTXP4.rst | 1 - .../2026-06-09-11-55-41.gh-issue-151163.oizZYV.rst | 1 + .../macOS/2026-04-26-23-15-09.gh-issue-149029.Lsx--T.rst | 1 - .../macOS/2026-06-09-11-52-35.gh-issue-151163.RlPXHq.rst | 1 + Misc/externals.spdx.json | 8 ++++---- PCbuild/get_externals.bat | 2 +- PCbuild/python.props | 2 +- PCbuild/readme.txt | 2 +- Platforms/Android/__main__.py | 2 +- 11 files changed, 14 insertions(+), 13 deletions(-) create mode 100644 Misc/NEWS.d/next/Build/2026-06-09-11-54-13.gh-issue-151163.vFAtjv.rst delete mode 100644 Misc/NEWS.d/next/Windows/2026-04-26-23-14-45.gh-issue-149029.oPTXP4.rst create mode 100644 Misc/NEWS.d/next/Windows/2026-06-09-11-55-41.gh-issue-151163.oizZYV.rst delete mode 100644 Misc/NEWS.d/next/macOS/2026-04-26-23-15-09.gh-issue-149029.Lsx--T.rst create mode 100644 Misc/NEWS.d/next/macOS/2026-06-09-11-52-35.gh-issue-151163.RlPXHq.rst diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py index 9c54cfe75c12f72..394e42439f7710e 100755 --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -359,9 +359,9 @@ def library_recipes(): ), ), dict( - name="SQLite 3.53.1", - url="https://www.sqlite.org/2026/sqlite-autoconf-3530100.tar.gz", - checksum="83e6b2020a034e9a7ad4a72feea59e1ad52f162e09cbd26735a3ffb98359fc4f", + name="SQLite 3.53.2", + url="https://www.sqlite.org/2026/sqlite-autoconf-3530200.tar.gz", + checksum="588ad51949419a56ebe81fe56193d510c559eb94c9a57748387860b5d3069316", extra_cflags=('-Os ' '-DSQLITE_ENABLE_FTS5 ' '-DSQLITE_ENABLE_FTS4 ' diff --git a/Misc/NEWS.d/next/Build/2026-06-09-11-54-13.gh-issue-151163.vFAtjv.rst b/Misc/NEWS.d/next/Build/2026-06-09-11-54-13.gh-issue-151163.vFAtjv.rst new file mode 100644 index 000000000000000..e4f3a044c81c6f5 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2026-06-09-11-54-13.gh-issue-151163.vFAtjv.rst @@ -0,0 +1 @@ +Updated Android build to include SQLite version 3.53.2. diff --git a/Misc/NEWS.d/next/Windows/2026-04-26-23-14-45.gh-issue-149029.oPTXP4.rst b/Misc/NEWS.d/next/Windows/2026-04-26-23-14-45.gh-issue-149029.oPTXP4.rst deleted file mode 100644 index 6c4c6403b989847..000000000000000 --- a/Misc/NEWS.d/next/Windows/2026-04-26-23-14-45.gh-issue-149029.oPTXP4.rst +++ /dev/null @@ -1 +0,0 @@ -Update Windows installer to ship with SQLite 3.53.1. diff --git a/Misc/NEWS.d/next/Windows/2026-06-09-11-55-41.gh-issue-151163.oizZYV.rst b/Misc/NEWS.d/next/Windows/2026-06-09-11-55-41.gh-issue-151163.oizZYV.rst new file mode 100644 index 000000000000000..580a87400862c52 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2026-06-09-11-55-41.gh-issue-151163.oizZYV.rst @@ -0,0 +1 @@ +Updated Windows builds to include SQLite version 3.53.2. diff --git a/Misc/NEWS.d/next/macOS/2026-04-26-23-15-09.gh-issue-149029.Lsx--T.rst b/Misc/NEWS.d/next/macOS/2026-04-26-23-15-09.gh-issue-149029.Lsx--T.rst deleted file mode 100644 index 157a70f5e3cefc9..000000000000000 --- a/Misc/NEWS.d/next/macOS/2026-04-26-23-15-09.gh-issue-149029.Lsx--T.rst +++ /dev/null @@ -1 +0,0 @@ -Update macOS installer to ship with SQLite version 3.53.1. diff --git a/Misc/NEWS.d/next/macOS/2026-06-09-11-52-35.gh-issue-151163.RlPXHq.rst b/Misc/NEWS.d/next/macOS/2026-06-09-11-52-35.gh-issue-151163.RlPXHq.rst new file mode 100644 index 000000000000000..7e9bf6f4587974e --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2026-06-09-11-52-35.gh-issue-151163.RlPXHq.rst @@ -0,0 +1 @@ +Updated macOS installer to include SQLite version 3.53.2. diff --git a/Misc/externals.spdx.json b/Misc/externals.spdx.json index dd49036a5360796..523d20259adaaa9 100644 --- a/Misc/externals.spdx.json +++ b/Misc/externals.spdx.json @@ -91,21 +91,21 @@ "checksums": [ { "algorithm": "SHA256", - "checksumValue": "15e8fc7dc059f7b156e53629540951c2691acd71e027f6f8f66dacab5c66c884" + "checksumValue": "53f8711811090cc4d9ffc624c360f81e7b409763b145ab2e948998f1a0d6a612" } ], - "downloadLocation": "https://github.com/python/cpython-source-deps/archive/refs/tags/sqlite-3.53.1.0.tar.gz", + "downloadLocation": "https://github.com/python/cpython-source-deps/archive/refs/tags/sqlite-3.53.2.0.tar.gz", "externalRefs": [ { "referenceCategory": "SECURITY", - "referenceLocator": "cpe:2.3:a:sqlite:sqlite:3.53.1.0:*:*:*:*:*:*:*", + "referenceLocator": "cpe:2.3:a:sqlite:sqlite:3.53.2.0:*:*:*:*:*:*:*", "referenceType": "cpe23Type" } ], "licenseConcluded": "NOASSERTION", "name": "sqlite", "primaryPackagePurpose": "SOURCE", - "versionInfo": "3.53.1.0" + "versionInfo": "3.53.2.0" }, { "SPDXID": "SPDXRef-PACKAGE-tcl", diff --git a/PCbuild/get_externals.bat b/PCbuild/get_externals.bat index f23a38ff6757d0f..47399fe65d1e542 100644 --- a/PCbuild/get_externals.bat +++ b/PCbuild/get_externals.bat @@ -56,7 +56,7 @@ set libraries=%libraries% bzip2-1.0.8 if NOT "%IncludeLibffiSrc%"=="false" set libraries=%libraries% libffi-3.4.4 if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-3.5.7 set libraries=%libraries% mpdecimal-4.0.0 -set libraries=%libraries% sqlite-3.53.1.0 +set libraries=%libraries% sqlite-3.53.2.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tcl-9.0.3.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tk-9.0.3.1 set libraries=%libraries% xz-5.8.1.1 diff --git a/PCbuild/python.props b/PCbuild/python.props index 86b88bf9ea75b4e..8d931bba28a389a 100644 --- a/PCbuild/python.props +++ b/PCbuild/python.props @@ -98,7 +98,7 @@ - $(ExternalsDir)sqlite-3.53.1.0\ + $(ExternalsDir)sqlite-3.53.2.0\ $(ExternalsDir)bzip2-1.0.8\ $(ExternalsDir)xz-5.8.1.1\ $(ExternalsDir)libffi-3.4.4\ diff --git a/PCbuild/readme.txt b/PCbuild/readme.txt index ea8adf21c279a68..7c5eab2eb75a1de 100644 --- a/PCbuild/readme.txt +++ b/PCbuild/readme.txt @@ -242,7 +242,7 @@ _ssl again when building. _sqlite3 - Wraps SQLite 3.53.1, which is itself built by sqlite3.vcxproj + Wraps SQLite 3.53.2, which is itself built by sqlite3.vcxproj Homepage: https://www.sqlite.org/ diff --git a/Platforms/Android/__main__.py b/Platforms/Android/__main__.py index f738a198e1a4121..063e1f316031443 100755 --- a/Platforms/Android/__main__.py +++ b/Platforms/Android/__main__.py @@ -220,7 +220,7 @@ def unpack_deps(host, prefix_dir, cache_dir): "bzip2-1.0.8-3", "libffi-3.4.4-3", "openssl-3.5.7-0", - "sqlite-3.53.1-0", + "sqlite-3.53.2-0", "xz-5.4.6-1", "zstd-1.5.7-2" ]: From dbd40b97c3efb913c9f26880d05bd4b350384640 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Tue, 9 Jun 2026 20:45:54 +0100 Subject: [PATCH 091/185] gh-151112: Fix double free in `assemble_init` when out of memory (#151142) --- .../2026-06-09-12-24-35.gh-issue-151112.4RKCkD.rst | 1 + Python/assemble.c | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-06-09-12-24-35.gh-issue-151112.4RKCkD.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-09-12-24-35.gh-issue-151112.4RKCkD.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-09-12-24-35.gh-issue-151112.4RKCkD.rst new file mode 100644 index 000000000000000..93ee5c8cf1914b4 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-09-12-24-35.gh-issue-151112.4RKCkD.rst @@ -0,0 +1 @@ +Fix a crash in the compiler that could occur when running out of memory. diff --git a/Python/assemble.c b/Python/assemble.c index 3df959c36341951..8bcb4c7bf3a9aa4 100644 --- a/Python/assemble.c +++ b/Python/assemble.c @@ -80,9 +80,9 @@ assemble_init(struct assembler *a, int firstlineno) } return SUCCESS; error: - Py_XDECREF(a->a_bytecode); - Py_XDECREF(a->a_linetable); - Py_XDECREF(a->a_except_table); + Py_CLEAR(a->a_bytecode); + Py_CLEAR(a->a_linetable); + Py_CLEAR(a->a_except_table); return ERROR; } From ff09eb973125e99fe84d2f8de7d215fd86063cd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20S=C5=82awecki?= Date: Wed, 10 Jun 2026 00:22:13 +0200 Subject: [PATCH 092/185] gh-150700: Fix class-scope inline comprehensions when nested scopes reference `__class__` and friends (#150735) * Fix class-scope inline comprehensions when nested scopes reference `__class__` and friends In `inline_comprehension()`, when `__class__` / `__classdict__` / `__conditional_annotations__` appears as `FREE` in a comprehension's symbol table because a nested scope captured it (e.g. nested lambdas), this name is still discarded from `comp_free` unconditionally. This prevents `drop_class_free()` from seeing it, so the appropriate `ste_needs_(...)` flag is never set on the enclosing class. That leads to `codegen_make_closure()` throwing `SystemError` when it couldn't find `__class__` / `__classdict__` / `__conditional_annotations__` in the class's cellvars. From now on we just discard from `comp_free` when no child scope (e.g. a lambda) still needs the name as `FREE`. When a child scope does need it, keep it in `comp_free` so `drop_class_free()` can set the appropriate flag and the class creates the implicit cell. * Fix tests * Fix typo * Fix formatting * Add test checking validity of `__class__` returned * Prefer 'used' to 'deferred' --- Lib/test/test_listcomps.py | 31 +++++++++++++++++++ ...-06-01-19-00-00.gh-issue-150700.W8CzVR.rst | 3 ++ Python/symtable.c | 13 +++++--- 3 files changed, 43 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-06-01-19-00-00.gh-issue-150700.W8CzVR.rst diff --git a/Lib/test/test_listcomps.py b/Lib/test/test_listcomps.py index cee528722b85aa0..cf3796d94808015 100644 --- a/Lib/test/test_listcomps.py +++ b/Lib/test/test_listcomps.py @@ -171,6 +171,17 @@ def test_references___class__(self): """ self._check_in_scopes(code, raises=NameError) + def test_references___class___nested(self): + code = """ + res = [(lambda: __class__)() for _ in [1]] + """ + self._check_in_scopes(code, raises=NameError) + + def test_references___class___nested_used(self): + class _C: + res = [lambda: __class__ for _ in [1]] + self.assertIs(_C.res[0](), _C) + def test_references___class___defined(self): code = """ __class__ = 2 @@ -180,18 +191,38 @@ def test_references___class___defined(self): code, outputs={"res": [2]}, scopes=["module", "function"]) self._check_in_scopes(code, raises=NameError, scopes=["class"]) + def test_references___class___defined_nested(self): + code = """ + __class__ = 2 + res = [(lambda: __class__)() for x in [1]] + """ + self._check_in_scopes( + code, outputs={"res": [2]}, scopes=["module", "function"]) + self._check_in_scopes(code, raises=NameError, scopes=["class"]) + def test_references___classdict__(self): code = """ class i: [__classdict__ for x in y] """ self._check_in_scopes(code, raises=NameError) + def test_references___classdict___nested(self): + class _C: + res = [(lambda: __classdict__)() for _ in [1]] + self.assertIn("res", _C.res[0]) + def test_references___conditional_annotations__(self): code = """ class i: [__conditional_annotations__ for x in y] """ self._check_in_scopes(code, raises=NameError) + def test_references___conditional_annotations___nested(self): + code = """ + class i: [lambda: __conditional_annotations__ for x in y] + """ + self._check_in_scopes(code, raises=NameError) + def test_references___class___enclosing(self): code = """ __class__ = 2 diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-01-19-00-00.gh-issue-150700.W8CzVR.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-01-19-00-00.gh-issue-150700.W8CzVR.rst new file mode 100644 index 000000000000000..e7734034ff5c814 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-01-19-00-00.gh-issue-150700.W8CzVR.rst @@ -0,0 +1,3 @@ +Fix a :exc:`SystemError` when compiling a class-scope comprehension containing +a ``lambda`` that references ``__class__``, ``__classdict__``, or +``__conditional_annotations__``. Patch by Bartosz Sławecki. diff --git a/Python/symtable.c b/Python/symtable.c index 070e374101b5cd5..00ac510fe76b972 100644 --- a/Python/symtable.c +++ b/Python/symtable.c @@ -834,17 +834,22 @@ inline_comprehension(PySTEntryObject *ste, PySTEntryObject *comp, return 0; } // __class__, __classdict__ and __conditional_annotations__ are - // never allowed to be free through a class scope (see - // drop_class_free) + // not allowed to be free through a class scope (see + // drop_class_free) unless children scopes need it if (scope == FREE && ste->ste_type == ClassBlock && (_PyUnicode_EqualToASCIIString(k, "__class__") || _PyUnicode_EqualToASCIIString(k, "__classdict__") || _PyUnicode_EqualToASCIIString(k, "__conditional_annotations__"))) { scope = GLOBAL_IMPLICIT; - if (PySet_Discard(comp_free, k) < 0) { + int child_needs_free = is_free_in_any_child(comp, k); + if (child_needs_free < 0) { return 0; } - + if (!child_needs_free) { + if (PySet_Discard(comp_free, k) < 0) { + return 0; + } + } if (_PyUnicode_EqualToASCIIString(k, "__class__")) { remove_dunder_class = 1; } From 513e89c2f547692fc8519dea709ca2743c4edc82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20S=C5=82awecki?= Date: Wed, 10 Jun 2026 08:36:50 +0200 Subject: [PATCH 093/185] gh-145239: Colorize `case +` in the REPL (#150979) --- Lib/_pyrepl/utils.py | 2 +- Lib/test/test_pyrepl/test_utils.py | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Lib/_pyrepl/utils.py b/Lib/_pyrepl/utils.py index b50426c31ead53a..f2837a1b8eb95e7 100644 --- a/Lib/_pyrepl/utils.py +++ b/Lib/_pyrepl/utils.py @@ -274,7 +274,7 @@ def is_soft_keyword_used(*tokens: TI | None) -> bool: None | TI(T.NEWLINE) | TI(T.INDENT) | TI(T.DEDENT) | TI(string=":"), TI(string="case"), TI(T.NUMBER | T.STRING | T.FSTRING_START | T.TSTRING_START) - | TI(T.OP, string="(" | "*" | "-" | "[" | "{") + | TI(T.OP, string="(" | "*" | "-" | "+" | "[" | "{") ): return True case ( diff --git a/Lib/test/test_pyrepl/test_utils.py b/Lib/test/test_pyrepl/test_utils.py index 3c55b6bdaeee9e8..0b873d32b62b5d2 100644 --- a/Lib/test/test_pyrepl/test_utils.py +++ b/Lib/test/test_pyrepl/test_utils.py @@ -125,6 +125,14 @@ def test_gen_colors_keyword_highlighting(self): ("import", "keyword"), ], ), + ( + "case +1", + [ + ("case", "soft_keyword"), + ("+", "op"), + ("1", "number"), + ], + ), ] for code, expected_highlights in cases: with self.subTest(code=code): From 813aff67501489a49c48d26c3dc9970d3f163e0e Mon Sep 17 00:00:00 2001 From: Harjoth Khara Date: Wed, 10 Jun 2026 01:48:16 -0700 Subject: [PATCH 094/185] gh-109940: Respect VIRTUAL_ENV_DISABLE_PROMPT in activate.bat (GH-151215) --- Lib/test/test_venv.py | 45 +++++++++++++++++++ Lib/venv/scripts/nt/activate.bat | 4 +- ...-06-10-00-00-02.gh-issue-109940.Cx1099.rst | 2 + 3 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-06-10-00-00-02.gh-issue-109940.Cx1099.rst diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py index 1ff5d7cf0c51dd8..0c6cd1b196ac812 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -593,6 +593,51 @@ def test_unicode_in_batch_file(self): ) self.assertEqual(out.strip(), '0') + @unittest.skipUnless(os.name == 'nt', 'only relevant on Windows') + def test_activate_bat_respects_disable_prompt(self): + rmtree(self.env_dir) + env_dir = os.path.join(os.path.realpath(self.env_dir), 'venv') + builder = venv.EnvBuilder(clear=True) + builder.create(env_dir) + activate = os.path.join(env_dir, self.bindir, 'activate.bat') + test_batch = os.path.join(self.env_dir, 'test_disable_prompt.bat') + with open(test_batch, "w") as f: + f.write('@echo off\n' + 'set "PROMPT=base$G"\n' + 'set "VIRTUAL_ENV_DISABLE_PROMPT=1"\n' + f'call "{activate}"\n' + 'echo ACTIVE_PROMPT:%PROMPT%\n' + 'echo VIRTUAL_ENV:%VIRTUAL_ENV%\n' + 'set "PROMPT=changed$G"\n' + 'call deactivate\n' + 'echo FINAL_PROMPT:%PROMPT%\n') + out, err = check_output([test_batch]) + lines = out.splitlines() + self.assertEqual(lines[0], b'ACTIVE_PROMPT:base$G') + self.assertEndsWith(lines[1], os.fsencode(env_dir)) + self.assertEqual(lines[2], b'FINAL_PROMPT:changed$G') + + @unittest.skipUnless(os.name == 'nt', 'only relevant on Windows') + def test_activate_bat_prefixes_prompt_by_default(self): + rmtree(self.env_dir) + env_dir = os.path.join(os.path.realpath(self.env_dir), 'venv') + builder = venv.EnvBuilder(clear=True) + builder.create(env_dir) + activate = os.path.join(env_dir, self.bindir, 'activate.bat') + test_batch = os.path.join(self.env_dir, 'test_enable_prompt.bat') + with open(test_batch, "w") as f: + f.write('@echo off\n' + 'set "PROMPT=base) $G"\n' + 'set "VIRTUAL_ENV_DISABLE_PROMPT="\n' + f'call "{activate}"\n' + 'echo ACTIVE_PROMPT:%PROMPT%\n' + 'call deactivate\n' + 'echo FINAL_PROMPT:%PROMPT%\n') + out, err = check_output([test_batch]) + lines = out.splitlines() + self.assertEqual(lines[0], b'ACTIVE_PROMPT:(venv) base) $G') + self.assertEqual(lines[1], b'FINAL_PROMPT:base) $G') + @unittest.skipUnless(os.name == 'nt' and can_symlink(), 'symlinks on Windows') def test_failed_symlink(self): diff --git a/Lib/venv/scripts/nt/activate.bat b/Lib/venv/scripts/nt/activate.bat index 06f4753d73b7aed..4a3e791abb86bd2 100644 --- a/Lib/venv/scripts/nt/activate.bat +++ b/Lib/venv/scripts/nt/activate.bat @@ -13,8 +13,8 @@ @if defined _OLD_VIRTUAL_PROMPT @set PROMPT=%_OLD_VIRTUAL_PROMPT% @if defined _OLD_VIRTUAL_PYTHONHOME @set PYTHONHOME=%_OLD_VIRTUAL_PYTHONHOME% -@set "_OLD_VIRTUAL_PROMPT=%PROMPT%" -@set "PROMPT=(__VENV_PROMPT__) %PROMPT%" +@if not defined VIRTUAL_ENV_DISABLE_PROMPT @set "_OLD_VIRTUAL_PROMPT=%PROMPT%" +@if not defined VIRTUAL_ENV_DISABLE_PROMPT @set "PROMPT=(__VENV_PROMPT__) %PROMPT%" @if defined PYTHONHOME @set _OLD_VIRTUAL_PYTHONHOME=%PYTHONHOME% @set PYTHONHOME= diff --git a/Misc/NEWS.d/next/Library/2026-06-10-00-00-02.gh-issue-109940.Cx1099.rst b/Misc/NEWS.d/next/Library/2026-06-10-00-00-02.gh-issue-109940.Cx1099.rst new file mode 100644 index 000000000000000..130dc780b612864 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-10-00-00-02.gh-issue-109940.Cx1099.rst @@ -0,0 +1,2 @@ +Fix Windows :mod:`venv` activation in ``cmd.exe`` to respect +``VIRTUAL_ENV_DISABLE_PROMPT``. From c6f8635e9ceabb755b4c66d1a64a3672a44a8247 Mon Sep 17 00:00:00 2001 From: Taeknology <20297177+Taeknology@users.noreply.github.com> Date: Wed, 10 Jun 2026 19:10:15 +0900 Subject: [PATCH 095/185] gh-149716: Document PySlot_DATA for Py_mod_gil and Py_mod_multiple_interpreters (GH-150053) Add short code examples mirroring the existing Py_mod_abi example, so it is clear which slot definition macro (PySlot_DATA, PySlot_INT64, or PySlot_UINT64) to use for these two slots. --- Doc/c-api/module.rst | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Doc/c-api/module.rst b/Doc/c-api/module.rst index 20fd66e35a0d4d6..9f68abba66bc5d6 100644 --- a/Doc/c-api/module.rst +++ b/Doc/c-api/module.rst @@ -247,6 +247,15 @@ Feature slots If ``Py_mod_multiple_interpreters`` is not specified, the import machinery defaults to ``Py_MOD_MULTIPLE_INTERPRETERS_SUPPORTED``. + For historical reasons, the values are declared as pointers (``void *``). + When using :c:type:`PySlot` arrays, use :c:macro:`PySlot_DATA` for + :c:macro:`!Py_mod_multiple_interpreters`: + + .. code-block:: c + + PySlot_DATA(Py_mod_multiple_interpreters, + Py_MOD_PER_INTERPRETER_GIL_SUPPORTED) + .. versionadded:: 3.12 .. c:macro:: Py_mod_gil @@ -272,6 +281,14 @@ Feature slots If ``Py_mod_gil`` is not specified, the import machinery defaults to ``Py_MOD_GIL_USED``. + For historical reasons, the values are declared as pointers (``void *``). + When using :c:type:`PySlot` arrays, use :c:macro:`PySlot_DATA` for + :c:macro:`!Py_mod_gil`: + + .. code-block:: c + + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED) + .. versionadded:: 3.13 From b6cd57ead30d67e95e88c084e16ce9f18b4cd800 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 10 Jun 2026 13:34:55 +0300 Subject: [PATCH 096/185] gh-80384: Check that callback is callable at weak reference creation (GH-151145) * Python functions weakref.ref() and weakref.proxy() now raise TypeError if the callback argument is not callable or None. * C functions PyWeakref_NewRef() and PyWeakref_NewProxy() now raise TypeError if the callback argument is not callable, None, or NULL. Co-authored-by: Maxwell Bernstein --- Doc/c-api/weakref.rst | 12 ++++++++++-- Doc/library/weakref.rst | 6 ++++++ Lib/test/test_capi/test_weakref.py | 2 ++ Lib/test/test_traceback.py | 2 +- Lib/test/test_weakref.py | 5 +++++ .../2026-06-09-14-50-35.gh-issue-80384.UEGyvB.rst | 3 +++ .../2026-06-09-14-49-17.gh-issue-80384.ttRAja.rst | 2 ++ Objects/weakrefobject.c | 9 ++++++++- 8 files changed, 37 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/C_API/2026-06-09-14-50-35.gh-issue-80384.UEGyvB.rst create mode 100644 Misc/NEWS.d/next/Library/2026-06-09-14-49-17.gh-issue-80384.ttRAja.rst diff --git a/Doc/c-api/weakref.rst b/Doc/c-api/weakref.rst index db6ae0a9d4ea3d7..8762a003c5218d3 100644 --- a/Doc/c-api/weakref.rst +++ b/Doc/c-api/weakref.rst @@ -43,7 +43,11 @@ as much as it can. should accept a single parameter, which will be the weak reference object itself. *callback* may also be ``None`` or ``NULL``. If *ob* is not a weakly referenceable object, or if *callback* is not callable, ``None``, or - ``NULL``, this will return ``NULL`` and raise :exc:`TypeError`. + ``NULL``, this will raise :exc:`TypeError` and return ``NULL``. + + .. versionchanged:: next + Raise :exc:`!TypeError` if *callback* is not callable, ``None``, or + ``NULL``. .. seealso:: :c:func:`PyType_SUPPORTS_WEAKREFS` for checking if *ob* is weakly @@ -59,7 +63,11 @@ as much as it can. collected; it should accept a single parameter, which will be the weak reference object itself. *callback* may also be ``None`` or ``NULL``. If *ob* is not a weakly referenceable object, or if *callback* is not callable, - ``None``, or ``NULL``, this will return ``NULL`` and raise :exc:`TypeError`. + ``NULL``, this will raise :exc:`TypeError` and return ``NULL``. + + .. versionchanged:: next + Raise :exc:`!TypeError` if *callback* is not callable, ``None``, or + ``NULL``. .. seealso:: :c:func:`PyType_SUPPORTS_WEAKREFS` for checking if *ob* is weakly diff --git a/Doc/library/weakref.rst b/Doc/library/weakref.rst index fcb9e0199fad69e..952609c0e700e5e 100644 --- a/Doc/library/weakref.rst +++ b/Doc/library/weakref.rst @@ -132,6 +132,9 @@ See :ref:`__slots__ documentation ` for details. .. versionchanged:: 3.4 Added the :attr:`__callback__` attribute. + .. versionchanged:: next + Raise :exc:`!TypeError` if *callback* is not callable or ``None``. + .. function:: proxy(object[, callback]) @@ -151,6 +154,9 @@ See :ref:`__slots__ documentation ` for details. Extended the operator support on proxy objects to include the matrix multiplication operators ``@`` and ``@=``. + .. versionchanged:: next + Raise :exc:`!TypeError` if *callback* is not callable or ``None``. + .. function:: getweakrefcount(object) diff --git a/Lib/test/test_capi/test_weakref.py b/Lib/test/test_capi/test_weakref.py index 86ebe92da8d95db..7c9e97f571979dc 100644 --- a/Lib/test/test_capi/test_weakref.py +++ b/Lib/test/test_capi/test_weakref.py @@ -97,6 +97,7 @@ def test_pyweakref_newref(self): # PyWeakref_NewRef() handles None callback as NULL callback wr = newref(obj, None) self.assertIs(type(wr), weakref.ReferenceType) + self.assertRaises(TypeError, newref, obj, 42) log = [] wr = newref(obj, log.append) self.assertIs(type(wr), weakref.ReferenceType) @@ -116,6 +117,7 @@ def test_pyweakref_newproxy(self): # PyWeakref_NewProxy() handles None callback as NULL callback wp = newproxy(obj, None) self.assertIs(type(wp), weakref.ProxyType) + self.assertRaises(TypeError, newproxy, obj, 42) log = [] wp = newproxy(obj, log.append) self.assertIs(type(wp), weakref.ProxyType) diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 7dc3364561d8a11..2c6324a14a8e2fc 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -206,7 +206,7 @@ def test_recursion_error_during_traceback(self): sys.setrecursionlimit(15) def f(): - ref(lambda: 0, []) + ref(lambda: 0, ord) f() try: diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py index b187643e84521cc..0c4717521f16f67 100644 --- a/Lib/test/test_weakref.py +++ b/Lib/test/test_weakref.py @@ -167,6 +167,11 @@ def test_basic_callback(self): self.check_basic_callback(create_function) self.check_basic_callback(create_bound_method) + def test_non_callable_callback(self): + c = C() + self.assertRaises(TypeError, weakref.ref, c, 42) + self.assertRaises(TypeError, weakref.proxy, c, 42) + @support.cpython_only def test_cfunction(self): _testcapi = import_helper.import_module("_testcapi") diff --git a/Misc/NEWS.d/next/C_API/2026-06-09-14-50-35.gh-issue-80384.UEGyvB.rst b/Misc/NEWS.d/next/C_API/2026-06-09-14-50-35.gh-issue-80384.UEGyvB.rst new file mode 100644 index 000000000000000..c4f5e8f3f9bbc2d --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2026-06-09-14-50-35.gh-issue-80384.UEGyvB.rst @@ -0,0 +1,3 @@ +:c:func:`PyWeakref_NewRef` and :c:func:`PyWeakref_NewProxy` now raise +:exc:`TypeError` if the *callback* argument is not callable, ``None``, or +``NULL``. diff --git a/Misc/NEWS.d/next/Library/2026-06-09-14-49-17.gh-issue-80384.ttRAja.rst b/Misc/NEWS.d/next/Library/2026-06-09-14-49-17.gh-issue-80384.ttRAja.rst new file mode 100644 index 000000000000000..53d4d513ba757de --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-09-14-49-17.gh-issue-80384.ttRAja.rst @@ -0,0 +1,2 @@ +:func:`weakref.ref` and :func:`weakref.proxy` now raise :exc:`TypeError` if +the *callback* argument is not callable or ``None``. diff --git a/Objects/weakrefobject.c b/Objects/weakrefobject.c index 8446a2dbcf75593..daeba5368b21660 100644 --- a/Objects/weakrefobject.c +++ b/Objects/weakrefobject.c @@ -416,8 +416,15 @@ get_or_create_weakref(PyTypeObject *type, PyObject *obj, PyObject *callback) Py_TYPE(obj)->tp_name); return NULL; } - if (callback == Py_None) + if (callback == Py_None) { callback = NULL; + } + if (callback != NULL && !PyCallable_Check(callback)) { + PyErr_Format(PyExc_TypeError, + "callback must be callable or None, not '%T'", + callback); + return NULL; + } PyWeakReference **list = GET_WEAKREFS_LISTPTR(obj); if ((type == &_PyWeakref_RefType) || From 1ae23a6261ceb15e8e3002b5f355c71b0180286d Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 10 Jun 2026 13:44:35 +0300 Subject: [PATCH 097/185] gh-150285: Wrap long single-line summary in text output in pydoc (GH-151081) --- Lib/pydoc.py | 17 ++++- Lib/test/test_pydoc/longsummary.py | 29 +++++++++ Lib/test/test_pydoc/test_pydoc.py | 65 +++++++++++++++++++ ...-06-08-15-04-35.gh-issue-150285.-Tj94n.rst | 1 + 4 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 Lib/test/test_pydoc/longsummary.py create mode 100644 Misc/NEWS.d/next/Library/2026-06-08-15-04-35.gh-issue-150285.-Tj94n.rst diff --git a/Lib/pydoc.py b/Lib/pydoc.py index 497cc7d90a42456..ca4eb1001981681 100644 --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -1240,6 +1240,17 @@ def indent(self, text, prefix=' '): lines = [(prefix + line).rstrip() for line in text.split('\n')] return '\n'.join(lines) + def _format_doc(self, text, width=68): + """Wraps the single-line summary if it is too long.""" + if not text: return '' + lines = text.split('\n', 2) + if len(lines) > 1 and lines[1]: + return text + lines[:1] = textwrap.wrap(lines[0], width, + break_long_words=False, + break_on_hyphens=False) + return '\n'.join(lines) + def section(self, title, contents): """Format a section with a given heading.""" clean_contents = self.indent(contents).rstrip() @@ -1390,6 +1401,7 @@ def makename(c, m=object.__module__): doc = getdoc(object) if doc: + doc = self._format_doc(doc) push(doc + '\n') # List the mro, if non-trivial. @@ -1590,6 +1602,7 @@ def docroutine(self, object, name=None, mod=None, cl=None, homecls=None): return decl + '\n' else: doc = getdoc(object) or '' + doc = self._format_doc(doc) return decl + '\n' + (doc and self.indent(doc).rstrip() + '\n') def docdata(self, object, name=None, mod=None, cl=None, *ignored): @@ -1602,6 +1615,7 @@ def docdata(self, object, name=None, mod=None, cl=None, *ignored): push('\n') doc = getdoc(object) or '' if doc: + doc = self._format_doc(doc) push(self.indent(doc)) push('\n') return ''.join(results) @@ -1620,7 +1634,8 @@ def docother(self, object, name=None, mod=None, parent=None, *ignored, if not doc: doc = getdoc(object) if doc: - line += '\n' + self.indent(str(doc)) + '\n' + doc = self._format_doc(str(doc)) + line += '\n' + self.indent(doc) + '\n' return line class _PlainTextDoc(TextDoc): diff --git a/Lib/test/test_pydoc/longsummary.py b/Lib/test/test_pydoc/longsummary.py new file mode 100644 index 000000000000000..0e566d33454119b --- /dev/null +++ b/Lib/test/test_pydoc/longsummary.py @@ -0,0 +1,29 @@ +class C: + """This is a class summary that consists of a very long single line, exceeding the recommended PEP 8 limit. + + The rest of the docstring body, separated from the summary by a blank line, can also contain very long lines. + """ + def meth(self): + """This is a method summary that consists of a very long single line, exceeding the recommended PEP 8 limit. + + The rest of the docstring body, separated from the summary by a blank line, can also contain very long lines. + """ + + @property + def prop(self): + """This is a property summary that consists of a very long single line, exceeding the recommended PEP 8 limit. + + The rest of the docstring body, separated from the summary by a blank line, can also contain very long lines. + """ + +def func(self): + """This is a function summary that consists of a very long single line, exceeding the recommended PEP 8 limit. + + The rest of the docstring body, separated from the summary by a blank line, can also contain very long lines. + """ + +data = C() +data.__doc__ = """This is a data summary that consists of a very long single line, exceeding the recommended PEP 8 limit. + +The rest of the docstring body, separated from the summary by a blank line, can also contain very long lines. +""" diff --git a/Lib/test/test_pydoc/test_pydoc.py b/Lib/test/test_pydoc/test_pydoc.py index 5cd26923f75c311..5543c664528e6c8 100644 --- a/Lib/test/test_pydoc/test_pydoc.py +++ b/Lib/test/test_pydoc/test_pydoc.py @@ -1245,6 +1245,71 @@ def function_with_really_long_name_so_annotations_can_be_rather_small( lambda very_long_parameter_name_that_should_not_fit_into_a_single_line, second_very_long_parameter_name ''' % __name__) + @requires_docstrings + def test_long_summaries(self): + from . import longsummary + doc = pydoc.render_doc(longsummary) + doc = clean_text(doc) + self.assertEqual(doc, '''Python Library Documentation: module test.test_pydoc.longsummary in test.test_pydoc + +NAME + test.test_pydoc.longsummary + +CLASSES + builtins.object + C + + class C(builtins.object) + | This is a class summary that consists of a very long single line, + | exceeding the recommended PEP 8 limit. + | + | The rest of the docstring body, separated from the summary by a blank line, can also contain very long lines. + | + | Methods defined here: + | + | meth(self) + | This is a method summary that consists of a very long single line, + | exceeding the recommended PEP 8 limit. + | + | The rest of the docstring body, separated from the summary by a blank line, can also contain very long lines. + | + | ---------------------------------------------------------------------- + | Readonly properties defined here: + | + | prop + | This is a property summary that consists of a very long single line, + | exceeding the recommended PEP 8 limit. + | + | The rest of the docstring body, separated from the summary by a blank line, can also contain very long lines. + | + | ---------------------------------------------------------------------- + | Data descriptors defined here: + | + | __dict__ + | dictionary for instance variables + | + | __weakref__ + | list of weak references to the object + +FUNCTIONS + func(self) + This is a function summary that consists of a very long single line, + exceeding the recommended PEP 8 limit. + + The rest of the docstring body, separated from the summary by a blank line, can also contain very long lines. + +DATA + data = + This is a data summary that consists of a very long single line, + exceeding the recommended PEP 8 limit. + + The rest of the docstring body, separated from the summary by a blank line, can also contain very long lines. + +FILE + %s + +''' % inspect.getabsfile(longsummary)) + def test__future__imports(self): # __future__ features are excluded from module help, # except when it's the __future__ module itself diff --git a/Misc/NEWS.d/next/Library/2026-06-08-15-04-35.gh-issue-150285.-Tj94n.rst b/Misc/NEWS.d/next/Library/2026-06-08-15-04-35.gh-issue-150285.-Tj94n.rst new file mode 100644 index 000000000000000..60b1f3e0a71f5bb --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-08-15-04-35.gh-issue-150285.-Tj94n.rst @@ -0,0 +1 @@ +:mod:`pydoc` now wraps long single-line summary in text output. From 3e7abf7bd585fa472fc21a39bce79e57577f5d7b Mon Sep 17 00:00:00 2001 From: stratakis Date: Wed, 10 Jun 2026 13:08:25 +0200 Subject: [PATCH 098/185] gh-98894: Fix DTrace test_check_probes for shared builds (#151122) When building with --enable-shared, the SystemTap/DTrace notes live in libpython. Add detection logic to be used by readelf. Force the C locale on readelf output. --- Lib/test/test_dtrace.py | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_dtrace.py b/Lib/test/test_dtrace.py index 61320a472f3e02c..6286b6d21b572e3 100644 --- a/Lib/test/test_dtrace.py +++ b/Lib/test/test_dtrace.py @@ -378,11 +378,14 @@ def setUpClass(cls): def get_readelf_version(): try: cmd = ["readelf", "--version"] + # Force the C locale to disable localization. + env = dict(os.environ, LC_ALL="C") proc = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, + env=env, ) with proc: version, stderr = proc.communicate() @@ -405,12 +408,36 @@ def get_readelf_version(): return int(match.group(1)), int(match.group(2)) def get_readelf_output(self): - command = ["readelf", "-n", sys.executable] + binary = sys.executable + if sysconfig.get_config_var("Py_ENABLE_SHARED"): + lib_dir = sysconfig.get_config_var("LIBDIR") + if not lib_dir or sysconfig.is_python_build(): + lib_dir = os.path.abspath(os.path.dirname(sys.executable)) + + lib_names = [] + for name in ( + sysconfig.get_config_var("INSTSONAME"), + sysconfig.get_config_var("LDLIBRARY"), + ): + if name and name not in lib_names: + lib_names.append(name) + + if lib_dir: + for name in lib_names: + libpython_path = os.path.join(lib_dir, name) + if os.path.exists(libpython_path): + binary = libpython_path + break + + command = ["readelf", "-n", binary] + # Force the C locale to disable localization. + env = dict(os.environ, LC_ALL="C") stdout, _ = subprocess.Popen( command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, + env=env, ).communicate() return stdout From 055db9decb0428a358ea2ac5a0769cc56d9dd7dd Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 10 Jun 2026 14:36:14 +0300 Subject: [PATCH 099/185] Add yet one assertion in test_set_text_charset_cp949 (GH-151224) Check bytes(m), not only str(m). --- Lib/test/test_email/test_contentmanager.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_email/test_contentmanager.py b/Lib/test/test_email/test_contentmanager.py index bc0e5d356181591..0b1b6e89f8c9922 100644 --- a/Lib/test/test_email/test_contentmanager.py +++ b/Lib/test/test_email/test_contentmanager.py @@ -352,7 +352,14 @@ def test_set_text_charset_cp949(self): x9Gxub7uCoFBCg== """)) - self.assertEqual(m.get_payload(decode=True).decode('ks_c_5601-1987'), content) + self.assertEqual(bytes(m), textwrap.dedent("""\ + Content-Type: text/plain; charset="ks_c_5601-1987" + Content-Transfer-Encoding: 8bit + + \ud55c\uad6d\uc5b4 + \uac02 + """).encode('ks_c_5601-1987')) + self.assertEqual(m.get_payload(decode=True), content.encode('ks_c_5601-1987')) self.assertEqual(m.get_content(), content) def test_set_text_plain_long_line_heuristics(self): From 8921cbb994c4ff3607ae6b5dce7aae6604439e62 Mon Sep 17 00:00:00 2001 From: Lukas Geiger Date: Wed, 10 Jun 2026 13:27:11 +0100 Subject: [PATCH 100/185] gh-150988: Fix refleak in `OSError` when attrs are set before `super().__init__()` (#150990) Co-authored-by: Victor Stinner --- Lib/test/test_exceptions.py | 14 ++++++++++++++ .../2026-06-05-22-52-41.gh-issue-150988.fDKfMJ.rst | 2 ++ Objects/exceptions.c | 10 +++++----- 3 files changed, 21 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-06-05-22-52-41.gh-issue-150988.fDKfMJ.rst diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 3f5fcb29b442dec..df7a04273b9b41c 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -1714,6 +1714,20 @@ def inner(): gc_collect() # For PyPy or other GCs. self.assertEqual(wr(), None) + def test_oserror_reinit_leak(self): + # gh-150988: Check for memory leak when re-initializing OSError. + # Previously, setting OSError attributes in a subclass + # before calling super().__init__() leaked memory. + class LeakingOSError(OSError): + def __init__(self, code, message, filename, filename2): + self.strerror = message + self.filename = filename + self.filename2 = filename2 + super().__init__(code, message, filename, None, filename2) + + exc = LeakingOSError(1, "some message", "filename.py", "filename2.py") + exc.__init__(2, "another message", "filename3.py", "filename4.py") + def test_errno_ENOTDIR(self): # Issue #12802: "not a directory" errors are ENOTDIR even on Windows with self.assertRaises(OSError) as cm: diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-05-22-52-41.gh-issue-150988.fDKfMJ.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-05-22-52-41.gh-issue-150988.fDKfMJ.rst new file mode 100644 index 000000000000000..6fb70a1ce2685c8 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-05-22-52-41.gh-issue-150988.fDKfMJ.rst @@ -0,0 +1,2 @@ +Fix a reference leak in :exc:`OSError` when attributes are set before +``super().__init__()``. diff --git a/Objects/exceptions.c b/Objects/exceptions.c index 34a7844c857732e..149595e64cec144 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -2140,10 +2140,10 @@ oserror_init(PyOSErrorObject *self, PyObject **p_args, return -1; } else { - self->filename = Py_NewRef(filename); + Py_XSETREF(self->filename, Py_NewRef(filename)); if (filename2 && filename2 != Py_None) { - self->filename2 = Py_NewRef(filename2); + Py_XSETREF(self->filename2, Py_NewRef(filename2)); } if (nargs >= 2 && nargs <= 5) { @@ -2158,10 +2158,10 @@ oserror_init(PyOSErrorObject *self, PyObject **p_args, } } } - self->myerrno = Py_XNewRef(myerrno); - self->strerror = Py_XNewRef(strerror); + Py_XSETREF(self->myerrno, Py_XNewRef(myerrno)); + Py_XSETREF(self->strerror, Py_XNewRef(strerror)); #ifdef MS_WINDOWS - self->winerror = Py_XNewRef(winerror); + Py_XSETREF(self->winerror, Py_XNewRef(winerror)); #endif /* Steals the reference to args */ From 263bc546efddf558a5a6a39bb42f2129e7cb5103 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Wed, 10 Jun 2026 15:57:09 +0300 Subject: [PATCH 101/185] gh-150671: Deprecate `PyAsyncGen_New`, `PyCoro_New`, `PyGen_New`, `PyGen_NewWithQualName` functions (#150672) Co-authored-by: Peter Bierma Co-authored-by: Victor Stinner --- Doc/c-api/coro.rst | 6 ++++++ Doc/c-api/gen.rst | 18 ++++++++++++++++++ .../c-api-pending-removal-in-3.18.rst | 4 ++++ Doc/whatsnew/3.16.rst | 16 +++++++++++++++- Include/cpython/genobject.h | 8 ++++---- ...6-05-31-15-39-58.gh-issue-150671.bTkDVC.rst | 4 ++++ 6 files changed, 51 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/C_API/2026-05-31-15-39-58.gh-issue-150671.bTkDVC.rst diff --git a/Doc/c-api/coro.rst b/Doc/c-api/coro.rst index caa855a10d20ca9..06422fb63f34f1a 100644 --- a/Doc/c-api/coro.rst +++ b/Doc/c-api/coro.rst @@ -33,3 +33,9 @@ return. with ``__name__`` and ``__qualname__`` set to *name* and *qualname*. A reference to *frame* is stolen by this function. The *frame* argument must not be ``NULL``. + + .. deprecated-removed:: 3.16 3.18 + + This function has not been used since 3.10. + It is also impossible to construct a proper *frame* + object to call this function. diff --git a/Doc/c-api/gen.rst b/Doc/c-api/gen.rst index ed121726b89620c..7713ba2ee4f8047 100644 --- a/Doc/c-api/gen.rst +++ b/Doc/c-api/gen.rst @@ -38,6 +38,12 @@ than explicitly calling :c:func:`PyGen_New` or :c:func:`PyGen_NewWithQualName`. A reference to *frame* is stolen by this function. The argument must not be ``NULL``. + .. deprecated-removed:: 3.16 3.18 + + This function has not been used since 3.10. + It is also impossible to construct a proper *frame* + object to call this function. + .. c:function:: PyObject* PyGen_NewWithQualName(PyFrameObject *frame, PyObject *name, PyObject *qualname) Create and return a new generator object based on the *frame* object, @@ -45,6 +51,12 @@ than explicitly calling :c:func:`PyGen_New` or :c:func:`PyGen_NewWithQualName`. A reference to *frame* is stolen by this function. The *frame* argument must not be ``NULL``. + .. deprecated-removed:: 3.16 3.18 + + This function has not been used since 3.10. + It is also impossible to construct a proper *frame* + object to call this function. + .. c:function:: PyCodeObject* PyGen_GetCode(PyGenObject *gen) @@ -77,6 +89,12 @@ Asynchronous Generator Objects .. versionadded:: 3.6 + .. deprecated-removed:: 3.16 3.18 + + This function has not been used since 3.10. + It is also impossible to construct a proper *frame* + object to call this function. + .. c:function:: int PyAsyncGen_CheckExact(PyObject *op) Return true if *op* is an asynchronous generator object, false otherwise. diff --git a/Doc/deprecations/c-api-pending-removal-in-3.18.rst b/Doc/deprecations/c-api-pending-removal-in-3.18.rst index 022aee93aa70c47..820334ee43c82e4 100644 --- a/Doc/deprecations/c-api-pending-removal-in-3.18.rst +++ b/Doc/deprecations/c-api-pending-removal-in-3.18.rst @@ -40,6 +40,10 @@ Pending removal in Python 3.18 * :c:func:`!_PyUnicodeWriter_PrepareKind`: (no replacement). * :c:func:`!_Py_HashPointer`: use :c:func:`Py_HashPointer`. * :c:func:`!_Py_fopen_obj`: use :c:func:`Py_fopen`. + * :c:func:`PyGen_New`: (no replacement). + * :c:func:`PyGen_NewWithQualName`: (no replacement). + * :c:func:`PyCoro_New`: (no replacement). + * :c:func:`PyAsyncGen_New`: (no replacement). The `pythoncapi-compat project `__ can be used to get diff --git a/Doc/whatsnew/3.16.rst b/Doc/whatsnew/3.16.rst index 6c35124ba7b4865..8e4c4a1e9b1de02 100644 --- a/Doc/whatsnew/3.16.rst +++ b/Doc/whatsnew/3.16.rst @@ -275,10 +275,14 @@ Deprecated .. include:: ../deprecations/pending-removal-in-3.17.rst +.. include:: ../deprecations/pending-removal-in-3.18.rst + .. include:: ../deprecations/pending-removal-in-3.19.rst .. include:: ../deprecations/pending-removal-in-3.20.rst +.. include:: ../deprecations/pending-removal-in-3.21.rst + .. include:: ../deprecations/pending-removal-in-future.rst @@ -326,9 +330,19 @@ Porting to Python 3.16 Deprecated C APIs ----------------- -* TODO +* :c:func:`PyGen_New`, :c:func:`PyGen_NewWithQualName`, :c:func:`PyCoro_New`, + and :c:func:`PyAsyncGen_New` are deprecated. + They are scheduled for removal in 3.18. .. Add C API deprecations above alphabetically, not here at the end. +.. include:: ../deprecations/c-api-pending-removal-in-3.18.rst + +.. include:: ../deprecations/c-api-pending-removal-in-3.19.rst + +.. include:: ../deprecations/c-api-pending-removal-in-3.20.rst + +.. include:: ../deprecations/c-api-pending-removal-in-future.rst + Removed C APIs -------------- diff --git a/Include/cpython/genobject.h b/Include/cpython/genobject.h index f75884e597e2c24..e14facd77edec53 100644 --- a/Include/cpython/genobject.h +++ b/Include/cpython/genobject.h @@ -16,8 +16,8 @@ PyAPI_DATA(PyTypeObject) PyGen_Type; #define PyGen_Check(op) PyObject_TypeCheck((op), &PyGen_Type) #define PyGen_CheckExact(op) Py_IS_TYPE((op), &PyGen_Type) -PyAPI_FUNC(PyObject *) PyGen_New(PyFrameObject *); -PyAPI_FUNC(PyObject *) PyGen_NewWithQualName(PyFrameObject *, +Py_DEPRECATED(3.16) PyAPI_FUNC(PyObject *) PyGen_New(PyFrameObject *); +Py_DEPRECATED(3.16) PyAPI_FUNC(PyObject *) PyGen_NewWithQualName(PyFrameObject *, PyObject *name, PyObject *qualname); PyAPI_FUNC(PyCodeObject *) PyGen_GetCode(PyGenObject *gen); @@ -29,7 +29,7 @@ typedef struct _PyCoroObject PyCoroObject; PyAPI_DATA(PyTypeObject) PyCoro_Type; #define PyCoro_CheckExact(op) Py_IS_TYPE((op), &PyCoro_Type) -PyAPI_FUNC(PyObject *) PyCoro_New(PyFrameObject *, +Py_DEPRECATED(3.16) PyAPI_FUNC(PyObject *) PyCoro_New(PyFrameObject *, PyObject *name, PyObject *qualname); @@ -40,7 +40,7 @@ typedef struct _PyAsyncGenObject PyAsyncGenObject; PyAPI_DATA(PyTypeObject) PyAsyncGen_Type; PyAPI_DATA(PyTypeObject) _PyAsyncGenASend_Type; -PyAPI_FUNC(PyObject *) PyAsyncGen_New(PyFrameObject *, +Py_DEPRECATED(3.16) PyAPI_FUNC(PyObject *) PyAsyncGen_New(PyFrameObject *, PyObject *name, PyObject *qualname); #define PyAsyncGen_CheckExact(op) Py_IS_TYPE((op), &PyAsyncGen_Type) diff --git a/Misc/NEWS.d/next/C_API/2026-05-31-15-39-58.gh-issue-150671.bTkDVC.rst b/Misc/NEWS.d/next/C_API/2026-05-31-15-39-58.gh-issue-150671.bTkDVC.rst new file mode 100644 index 000000000000000..a67a0f85db1a506 --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2026-05-31-15-39-58.gh-issue-150671.bTkDVC.rst @@ -0,0 +1,4 @@ +Deprecate these C-API functions: :c:func:`PyGen_New`, +:c:func:`PyGen_NewWithQualName`, :c:func:`PyCoro_New`, and +:c:func:`PyAsyncGen_New`. +Schedule them for removal in 3.18 From 610f4dd073720d344d3d1aa4db7a95fef5c9414a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bern=C3=A1t=20G=C3=A1bor?= Date: Wed, 10 Jun 2026 06:01:01 -0700 Subject: [PATCH 102/185] gh-89554: Document socket.SocketType as a class (#150683) socket.SocketType is a class (re-exported from _socket as an alias of _socket.socket, the base class of socket.socket), but was documented with the ".. data::" directive, so ":class:" cross-references to it cannot resolve against a py:class target. Switch the entry to ".. class::", correct the misleading description (SocketType is the base class of the socket type, not "type(socket(...))" which is socket.socket; addresses gh-88427), move it into the Socket Objects section, and document the socket object methods and attributes nested under the socket class, dropping the redundant "socket." prefix. --- Doc/library/socket.rst | 1050 ++++++++++++++++++++-------------------- 1 file changed, 527 insertions(+), 523 deletions(-) diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index 96bc9e7a0d61e3d..836aa91bb0885b1 100644 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -842,65 +842,8 @@ Creating sockets The following functions all create :ref:`socket objects `. -.. class:: socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None) - - Create a new socket using the given address family, socket type and protocol - number. The address family should be :const:`AF_INET` (the default), - :const:`AF_INET6`, :const:`AF_UNIX`, :const:`AF_CAN`, :const:`AF_PACKET`, - or :const:`AF_RDS`. The socket type should be :const:`SOCK_STREAM` (the - default), :const:`SOCK_DGRAM`, :const:`SOCK_RAW` or perhaps one of the other - ``SOCK_`` constants. The protocol number is usually zero and may be omitted - or in the case where the address family is :const:`AF_CAN` the protocol - should be one of :const:`CAN_RAW`, :const:`CAN_BCM`, :const:`CAN_ISOTP` or - :const:`CAN_J1939`. - - If *fileno* is specified, the values for *family*, *type*, and *proto* are - auto-detected from the specified file descriptor. Auto-detection can be - overruled by calling the function with explicit *family*, *type*, or *proto* - arguments. This only affects how Python represents e.g. the return value - of :meth:`socket.getpeername` but not the actual OS resource. Unlike - :func:`socket.fromfd`, *fileno* will return the same socket and not a - duplicate. This may help close a detached socket using - :meth:`socket.close`. - - The newly created socket is :ref:`non-inheritable `. - - .. audit-event:: socket.__new__ self,family,type,protocol socket.socket - - .. versionchanged:: 3.3 - The AF_CAN family was added. - The AF_RDS family was added. - - .. versionchanged:: 3.4 - The CAN_BCM protocol was added. - - .. versionchanged:: 3.4 - The returned socket is now non-inheritable. - - .. versionchanged:: 3.7 - The CAN_ISOTP protocol was added. - - .. versionchanged:: 3.7 - When :const:`SOCK_NONBLOCK` or :const:`SOCK_CLOEXEC` - bit flags are applied to *type* they are cleared, and - :attr:`socket.type` will not reflect them. They are still passed - to the underlying system ``socket()`` call. Therefore, - - :: - - sock = socket.socket( - socket.AF_INET, - socket.SOCK_STREAM | socket.SOCK_NONBLOCK) - - will still create a non-blocking socket on OSes that support - ``SOCK_NONBLOCK``, but ``sock.type`` will be set to - ``socket.SOCK_STREAM``. - - .. versionchanged:: 3.9 - The CAN_J1939 protocol was added. - - .. versionchanged:: 3.10 - The IPPROTO_MPTCP protocol was added. +The :class:`socket ` class constructor creates a new socket +directly; see :ref:`socket-objects` for its parameters and full description. .. function:: socketpair([family[, type[, proto]]]) @@ -1025,12 +968,6 @@ The following functions all create :ref:`socket objects `. .. versionadded:: 3.3 -.. data:: SocketType - - This is a Python type object that represents the socket object type. It is the - same as ``type(socket(...))``. - - Other functions ''''''''''''''' @@ -1538,642 +1475,709 @@ The :mod:`!socket` module also offers various network-related services: Socket Objects -------------- -Socket objects have the following methods. Except for -:meth:`~socket.makefile`, these correspond to Unix system calls applicable -to sockets. +.. class:: socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None) -.. versionchanged:: 3.2 - Support for the :term:`context manager` protocol was added. Exiting the - context manager is equivalent to calling :meth:`~socket.close`. + Create a new socket using the given address family, socket type and protocol + number. The address family should be :const:`AF_INET` (the default), + :const:`AF_INET6`, :const:`AF_UNIX`, :const:`AF_CAN`, :const:`AF_PACKET`, + or :const:`AF_RDS`. The socket type should be :const:`SOCK_STREAM` (the + default), :const:`SOCK_DGRAM`, :const:`SOCK_RAW` or perhaps one of the other + ``SOCK_`` constants. The protocol number is usually zero and may be omitted + or in the case where the address family is :const:`AF_CAN` the protocol + should be one of :const:`CAN_RAW`, :const:`CAN_BCM`, :const:`CAN_ISOTP` or + :const:`CAN_J1939`. + If *fileno* is specified, the values for *family*, *type*, and *proto* are + auto-detected from the specified file descriptor. Auto-detection can be + overruled by calling the function with explicit *family*, *type*, or *proto* + arguments. This only affects how Python represents e.g. the return value + of :meth:`socket.getpeername` but not the actual OS resource. Unlike + :func:`socket.fromfd`, *fileno* will return the same socket and not a + duplicate. This may help close a detached socket using + :meth:`socket.close`. -.. method:: socket.accept() + The newly created socket is :ref:`non-inheritable `. - Accept a connection. The socket must be bound to an address and listening for - connections. The return value is a pair ``(conn, address)`` where *conn* is a - *new* socket object usable to send and receive data on the connection, and - *address* is the address bound to the socket on the other end of the connection. + .. audit-event:: socket.__new__ self,family,type,protocol socket.socket - The newly created socket is :ref:`non-inheritable `. + .. versionchanged:: 3.3 + The AF_CAN family was added. + The AF_RDS family was added. .. versionchanged:: 3.4 - The socket is now non-inheritable. + The CAN_BCM protocol was added. - .. versionchanged:: 3.5 - If the system call is interrupted and the signal handler does not raise - an exception, the method now retries the system call instead of raising - an :exc:`InterruptedError` exception (see :pep:`475` for the rationale). + .. versionchanged:: 3.4 + The returned socket is now non-inheritable. + .. versionchanged:: 3.7 + The CAN_ISOTP protocol was added. -.. method:: socket.bind(address) + .. versionchanged:: 3.7 + When :const:`SOCK_NONBLOCK` or :const:`SOCK_CLOEXEC` + bit flags are applied to *type* they are cleared, and + :attr:`socket.type` will not reflect them. They are still passed + to the underlying system ``socket()`` call. Therefore, - Bind the socket to *address*. The socket must not already be bound. The format - of *address* depends on the address family --- see :ref:`socket-addresses`. + :: - .. audit-event:: socket.bind self,address socket.socket.bind + sock = socket.socket( + socket.AF_INET, + socket.SOCK_STREAM | socket.SOCK_NONBLOCK) - .. availability:: not WASI. + will still create a non-blocking socket on OSes that support + ``SOCK_NONBLOCK``, but ``sock.type`` will be set to + ``socket.SOCK_STREAM``. + .. versionchanged:: 3.9 + The CAN_J1939 protocol was added. -.. method:: socket.close() + .. versionchanged:: 3.10 + The IPPROTO_MPTCP protocol was added. - Mark the socket closed. The underlying system resource (e.g. a file - descriptor) is also closed when all file objects from :meth:`makefile` - are closed. Once that happens, all future operations on the socket - object will fail. The remote end will receive no more data (after - queued data is flushed). + Socket objects have the following methods. Except for + :meth:`~socket.makefile`, these correspond to Unix system calls applicable + to sockets. - Sockets are automatically closed when they are garbage-collected, but - it is recommended to :meth:`close` them explicitly, or to use a - :keyword:`with` statement around them. + .. versionchanged:: 3.2 + Support for the :term:`context manager` protocol was added. Exiting the + context manager is equivalent to calling :meth:`~socket.close`. - .. versionchanged:: 3.6 - :exc:`OSError` is now raised if an error occurs when the underlying - :c:func:`close` call is made. - .. note:: + .. method:: accept() - :meth:`close` releases the resource associated with a connection but - does not necessarily close the connection immediately. If you want - to close the connection in a timely fashion, call :meth:`shutdown` - before :meth:`close`. + Accept a connection. The socket must be bound to an address and listening for + connections. The return value is a pair ``(conn, address)`` where *conn* is a + *new* socket object usable to send and receive data on the connection, and + *address* is the address bound to the socket on the other end of the connection. + The newly created socket is :ref:`non-inheritable `. -.. method:: socket.connect(address) + .. versionchanged:: 3.4 + The socket is now non-inheritable. - Connect to a remote socket at *address*. The format of *address* depends on the - address family --- see :ref:`socket-addresses`. + .. versionchanged:: 3.5 + If the system call is interrupted and the signal handler does not raise + an exception, the method now retries the system call instead of raising + an :exc:`InterruptedError` exception (see :pep:`475` for the rationale). - If the connection is interrupted by a signal, the method waits until the - connection completes, or raises a :exc:`TimeoutError` on timeout, if the - signal handler doesn't raise an exception and the socket is blocking or has - a timeout. For non-blocking sockets, the method raises an - :exc:`InterruptedError` exception if the connection is interrupted by a - signal (or the exception raised by the signal handler). - .. audit-event:: socket.connect self,address socket.socket.connect + .. method:: bind(address) - .. versionchanged:: 3.5 - The method now waits until the connection completes instead of raising an + Bind the socket to *address*. The socket must not already be bound. The format + of *address* depends on the address family --- see :ref:`socket-addresses`. + + .. audit-event:: socket.bind self,address socket.socket.bind + + .. availability:: not WASI. + + + .. method:: close() + + Mark the socket closed. The underlying system resource (e.g. a file + descriptor) is also closed when all file objects from :meth:`makefile` + are closed. Once that happens, all future operations on the socket + object will fail. The remote end will receive no more data (after + queued data is flushed). + + Sockets are automatically closed when they are garbage-collected, but + it is recommended to :meth:`close` them explicitly, or to use a + :keyword:`with` statement around them. + + .. versionchanged:: 3.6 + :exc:`OSError` is now raised if an error occurs when the underlying + :c:func:`!close` call is made. + + .. note:: + + :meth:`close` releases the resource associated with a connection but + does not necessarily close the connection immediately. If you want + to close the connection in a timely fashion, call :meth:`shutdown` + before :meth:`close`. + + + .. method:: connect(address) + + Connect to a remote socket at *address*. The format of *address* depends on the + address family --- see :ref:`socket-addresses`. + + If the connection is interrupted by a signal, the method waits until the + connection completes, or raises a :exc:`TimeoutError` on timeout, if the + signal handler doesn't raise an exception and the socket is blocking or has + a timeout. For non-blocking sockets, the method raises an :exc:`InterruptedError` exception if the connection is interrupted by a - signal, the signal handler doesn't raise an exception and the socket is - blocking or has a timeout (see the :pep:`475` for the rationale). + signal (or the exception raised by the signal handler). - .. availability:: not WASI. + .. audit-event:: socket.connect self,address socket.socket.connect + .. versionchanged:: 3.5 + The method now waits until the connection completes instead of raising an + :exc:`InterruptedError` exception if the connection is interrupted by a + signal, the signal handler doesn't raise an exception and the socket is + blocking or has a timeout (see the :pep:`475` for the rationale). -.. method:: socket.connect_ex(address) + .. availability:: not WASI. - Like ``connect(address)``, but return an error indicator instead of raising an - exception for errors returned by the C-level :c:func:`connect` call (other - problems, such as "host not found," can still raise exceptions). The error - indicator is ``0`` if the operation succeeded, otherwise the value of the - :c:data:`errno` variable. This is useful to support, for example, asynchronous - connects. - .. audit-event:: socket.connect self,address socket.socket.connect_ex + .. method:: connect_ex(address) - .. availability:: not WASI. + Like ``connect(address)``, but return an error indicator instead of raising an + exception for errors returned by the C-level :c:func:`!connect` call (other + problems, such as "host not found," can still raise exceptions). The error + indicator is ``0`` if the operation succeeded, otherwise the value of the + :c:data:`errno` variable. This is useful to support, for example, asynchronous + connects. -.. method:: socket.detach() + .. audit-event:: socket.connect self,address socket.socket.connect_ex - Put the socket object into closed state without actually closing the - underlying file descriptor. The file descriptor is returned, and can - be reused for other purposes. + .. availability:: not WASI. - .. versionadded:: 3.2 + .. method:: detach() + Put the socket object into closed state without actually closing the + underlying file descriptor. The file descriptor is returned, and can + be reused for other purposes. -.. method:: socket.dup() + .. versionadded:: 3.2 - Duplicate the socket. - The newly created socket is :ref:`non-inheritable `. + .. method:: dup() - .. versionchanged:: 3.4 - The socket is now non-inheritable. + Duplicate the socket. - .. availability:: not WASI. + The newly created socket is :ref:`non-inheritable `. + .. versionchanged:: 3.4 + The socket is now non-inheritable. -.. method:: socket.fileno() + .. availability:: not WASI. - Return the socket's file descriptor (a small integer), or -1 on failure. This - is useful with :func:`select.select`. - Under Windows the small integer returned by this method cannot be used where a - file descriptor can be used (such as :func:`os.fdopen`). Unix does not have - this limitation. + .. method:: fileno() -.. method:: socket.get_inheritable() + Return the socket's file descriptor (a small integer), or -1 on failure. This + is useful with :func:`select.select`. - Get the :ref:`inheritable flag ` of the socket's file - descriptor or socket's handle: ``True`` if the socket can be inherited in - child processes, ``False`` if it cannot. + Under Windows the small integer returned by this method cannot be used where a + file descriptor can be used (such as :func:`os.fdopen`). Unix does not have + this limitation. - .. versionadded:: 3.4 + .. method:: get_inheritable() + Get the :ref:`inheritable flag ` of the socket's file + descriptor or socket's handle: ``True`` if the socket can be inherited in + child processes, ``False`` if it cannot. -.. method:: socket.getpeername() + .. versionadded:: 3.4 - Return the remote address to which the socket is connected. This is useful to - find out the port number of a remote IPv4/v6 socket, for instance. The format - of the address returned depends on the address family --- see :ref:`socket-addresses`. - On some systems this function is not supported. + .. method:: getpeername() -.. method:: socket.getsockname() + Return the remote address to which the socket is connected. This is useful to + find out the port number of a remote IPv4/v6 socket, for instance. The format + of the address returned depends on the address family --- see :ref:`socket-addresses`. + On some systems this function is not supported. - Return the socket's own address. This is useful to find out the port number of - an IPv4/v6 socket, for instance. The format of the address returned depends on - the address family --- see :ref:`socket-addresses`. + .. method:: getsockname() -.. method:: socket.getsockopt(level, optname[, buflen]) + Return the socket's own address. This is useful to find out the port number of + an IPv4/v6 socket, for instance. The format of the address returned depends on + the address family --- see :ref:`socket-addresses`. - Return the value of the given socket option (see the Unix man page - :manpage:`getsockopt(2)`). The needed symbolic constants (:ref:`SO_\* etc. `) - are defined in this module. If *buflen* is absent, an integer option is assumed - and its integer value is returned by the function. If *buflen* is present, it - specifies the maximum length of the buffer used to receive the option in, and - this buffer is returned as a bytes object. It is up to the caller to decode the - contents of the buffer (see the optional built-in module :mod:`struct` for a way - to decode C structures encoded as byte strings). - .. availability:: not WASI. + .. method:: getsockopt(level, optname[, buflen]) + Return the value of the given socket option (see the Unix man page + :manpage:`getsockopt(2)`). The needed symbolic constants (:ref:`SO_\* etc. `) + are defined in this module. If *buflen* is absent, an integer option is assumed + and its integer value is returned by the function. If *buflen* is present, it + specifies the maximum length of the buffer used to receive the option in, and + this buffer is returned as a bytes object. It is up to the caller to decode the + contents of the buffer (see the optional built-in module :mod:`struct` for a way + to decode C structures encoded as byte strings). -.. method:: socket.getblocking() + .. availability:: not WASI. - Return ``True`` if socket is in blocking mode, ``False`` if in - non-blocking. - This is equivalent to checking ``socket.gettimeout() != 0``. + .. method:: getblocking() - .. versionadded:: 3.7 + Return ``True`` if socket is in blocking mode, ``False`` if in + non-blocking. + This is equivalent to checking ``socket.gettimeout() != 0``. -.. method:: socket.gettimeout() + .. versionadded:: 3.7 - Return the timeout in seconds (float) associated with socket operations, - or ``None`` if no timeout is set. This reflects the last call to - :meth:`setblocking` or :meth:`settimeout`. + .. method:: gettimeout() -.. method:: socket.ioctl(control, option) + Return the timeout in seconds (float) associated with socket operations, + or ``None`` if no timeout is set. This reflects the last call to + :meth:`setblocking` or :meth:`settimeout`. - The :meth:`ioctl` method is a limited interface to the WSAIoctl system - interface. Please refer to the `Win32 documentation - `_ for more - information. - On other platforms, the generic :func:`fcntl.fcntl` and :func:`fcntl.ioctl` - functions may be used; they accept a socket object as their first argument. + .. method:: ioctl(control, option) - Currently only the following control codes are supported: - ``SIO_RCVALL``, ``SIO_KEEPALIVE_VALS``, and ``SIO_LOOPBACK_FAST_PATH``. + The :meth:`ioctl` method is a limited interface to the WSAIoctl system + interface. Please refer to the `Win32 documentation + `_ for more + information. - .. availability:: Windows + On other platforms, the generic :func:`fcntl.fcntl` and :func:`fcntl.ioctl` + functions may be used; they accept a socket object as their first argument. - .. versionchanged:: 3.6 - ``SIO_LOOPBACK_FAST_PATH`` was added. + Currently only the following control codes are supported: + ``SIO_RCVALL``, ``SIO_KEEPALIVE_VALS``, and ``SIO_LOOPBACK_FAST_PATH``. + .. availability:: Windows -.. method:: socket.listen([backlog]) + .. versionchanged:: 3.6 + ``SIO_LOOPBACK_FAST_PATH`` was added. - Enable a server to accept connections. If *backlog* is specified, it must - be at least 0 (if it is lower, it is set to 0); it specifies the number of - unaccepted connections that the system will allow before refusing new - connections. If not specified, a default reasonable value is chosen. - .. availability:: not WASI. + .. method:: listen([backlog]) - .. versionchanged:: 3.5 - The *backlog* parameter is now optional. + Enable a server to accept connections. If *backlog* is specified, it must + be at least 0 (if it is lower, it is set to 0); it specifies the number of + unaccepted connections that the system will allow before refusing new + connections. If not specified, a default reasonable value is chosen. + .. availability:: not WASI. -.. method:: socket.makefile(mode='r', buffering=None, *, encoding=None, \ - errors=None, newline=None) + .. versionchanged:: 3.5 + The *backlog* parameter is now optional. - .. index:: single: I/O control; buffering - Return a :term:`file object` associated with the socket. The exact returned - type depends on the arguments given to :meth:`makefile`. These arguments are - interpreted the same way as by the built-in :func:`open` function, except - the only supported *mode* values are ``'r'`` (default), ``'w'``, ``'b'``, or - a combination of those. + .. method:: makefile(mode='r', buffering=None, *, encoding=None, \ + errors=None, newline=None) - The socket must be in blocking mode; it can have a timeout, but the file - object's internal buffer may end up in an inconsistent state if a timeout - occurs. + .. index:: single: I/O control; buffering - Closing the file object returned by :meth:`makefile` won't close the - original socket unless all other file objects have been closed and - :meth:`socket.close` has been called on the socket object. + Return a :term:`file object` associated with the socket. The exact returned + type depends on the arguments given to :meth:`makefile`. These arguments are + interpreted the same way as by the built-in :func:`open` function, except + the only supported *mode* values are ``'r'`` (default), ``'w'``, ``'b'``, or + a combination of those. - .. note:: + The socket must be in blocking mode; it can have a timeout, but the file + object's internal buffer may end up in an inconsistent state if a timeout + occurs. - On Windows, the file-like object created by :meth:`makefile` cannot be - used where a file object with a file descriptor is expected, such as the - stream arguments of :meth:`subprocess.Popen`. + Closing the file object returned by :meth:`makefile` won't close the + original socket unless all other file objects have been closed and + :meth:`socket.close` has been called on the socket object. + .. note:: -.. method:: socket.recv(bufsize[, flags]) + On Windows, the file-like object created by :meth:`makefile` cannot be + used where a file object with a file descriptor is expected, such as the + stream arguments of :meth:`subprocess.Popen`. - Receive data from the socket. The return value is a bytes object representing the - data received. The maximum amount of data to be received at once is specified - by *bufsize*. A returned empty bytes object indicates that the client has disconnected. - See the Unix manual page :manpage:`recv(2)` for the meaning of the optional argument - *flags*; it defaults to zero. - .. versionchanged:: 3.5 - If the system call is interrupted and the signal handler does not raise - an exception, the method now retries the system call instead of raising - an :exc:`InterruptedError` exception (see :pep:`475` for the rationale). + .. method:: recv(bufsize[, flags]) + Receive data from the socket. The return value is a bytes object representing the + data received. The maximum amount of data to be received at once is specified + by *bufsize*. A returned empty bytes object indicates that the client has disconnected. + See the Unix manual page :manpage:`recv(2)` for the meaning of the optional argument + *flags*; it defaults to zero. -.. method:: socket.recvfrom(bufsize[, flags]) + .. versionchanged:: 3.5 + If the system call is interrupted and the signal handler does not raise + an exception, the method now retries the system call instead of raising + an :exc:`InterruptedError` exception (see :pep:`475` for the rationale). - Receive data from the socket. The return value is a pair ``(bytes, address)`` - where *bytes* is a bytes object representing the data received and *address* is the - address of the socket sending the data. See the Unix manual page - :manpage:`recv(2)` for the meaning of the optional argument *flags*; it defaults - to zero. The format of *address* depends on the address family --- see - :ref:`socket-addresses`. - .. versionchanged:: 3.5 - If the system call is interrupted and the signal handler does not raise - an exception, the method now retries the system call instead of raising - an :exc:`InterruptedError` exception (see :pep:`475` for the rationale). + .. method:: recvfrom(bufsize[, flags]) - .. versionchanged:: 3.7 - For multicast IPv6 address, first item of *address* does not contain - ``%scope_id`` part anymore. In order to get full IPv6 address use - :func:`getnameinfo`. - -.. method:: socket.recvmsg(bufsize[, ancbufsize[, flags]]) - - Receive normal data (up to *bufsize* bytes) and ancillary data from - the socket. The *ancbufsize* argument sets the size in bytes of - the internal buffer used to receive the ancillary data; it defaults - to 0, meaning that no ancillary data will be received. Appropriate - buffer sizes for ancillary data can be calculated using - :func:`CMSG_SPACE` or :func:`CMSG_LEN`, and items which do not fit - into the buffer might be truncated or discarded. The *flags* - argument defaults to 0 and has the same meaning as for - :meth:`recv`. - - The return value is a 4-tuple: ``(data, ancdata, msg_flags, - address)``. The *data* item is a :class:`bytes` object holding the - non-ancillary data received. The *ancdata* item is a list of zero - or more tuples ``(cmsg_level, cmsg_type, cmsg_data)`` representing - the ancillary data (control messages) received: *cmsg_level* and - *cmsg_type* are integers specifying the protocol level and - protocol-specific type respectively, and *cmsg_data* is a - :class:`bytes` object holding the associated data. The *msg_flags* - item is the bitwise OR of various flags indicating conditions on - the received message; see your system documentation for details. - If the receiving socket is unconnected, *address* is the address of - the sending socket, if available; otherwise, its value is - unspecified. - - On some systems, :meth:`sendmsg` and :meth:`recvmsg` can be used to - pass file descriptors between processes over an :const:`AF_UNIX` - socket. When this facility is used (it is often restricted to - :const:`SOCK_STREAM` sockets), :meth:`recvmsg` will return, in its - ancillary data, items of the form ``(socket.SOL_SOCKET, - socket.SCM_RIGHTS, fds)``, where *fds* is a :class:`bytes` object - representing the new file descriptors as a binary array of the - native C :c:expr:`int` type. If :meth:`recvmsg` raises an - exception after the system call returns, it will first attempt to - close any file descriptors received via this mechanism. - - Some systems do not indicate the truncated length of ancillary data - items which have been only partially received. If an item appears - to extend beyond the end of the buffer, :meth:`recvmsg` will issue - a :exc:`RuntimeWarning`, and will return the part of it which is - inside the buffer provided it has not been truncated before the - start of its associated data. - - On systems which support the :const:`SCM_RIGHTS` mechanism, the - following function will receive up to *maxfds* file descriptors, - returning the message data and a list containing the descriptors - (while ignoring unexpected conditions such as unrelated control - messages being received). See also :meth:`sendmsg`. :: - - import socket, array - - def recv_fds(sock, msglen, maxfds): - fds = array.array("i") # Array of ints - msg, ancdata, flags, addr = sock.recvmsg(msglen, socket.CMSG_LEN(maxfds * fds.itemsize)) - for cmsg_level, cmsg_type, cmsg_data in ancdata: - if cmsg_level == socket.SOL_SOCKET and cmsg_type == socket.SCM_RIGHTS: - # Append data, ignoring any truncated integers at the end. - fds.frombytes(cmsg_data[:len(cmsg_data) - (len(cmsg_data) % fds.itemsize)]) - return msg, list(fds) - - .. availability:: Unix. + Receive data from the socket. The return value is a pair ``(bytes, address)`` + where *bytes* is a bytes object representing the data received and *address* is the + address of the socket sending the data. See the Unix manual page + :manpage:`recv(2)` for the meaning of the optional argument *flags*; it defaults + to zero. The format of *address* depends on the address family --- see + :ref:`socket-addresses`. - Most Unix platforms. + .. versionchanged:: 3.5 + If the system call is interrupted and the signal handler does not raise + an exception, the method now retries the system call instead of raising + an :exc:`InterruptedError` exception (see :pep:`475` for the rationale). - .. versionadded:: 3.3 + .. versionchanged:: 3.7 + For multicast IPv6 address, first item of *address* does not contain + ``%scope_id`` part anymore. In order to get full IPv6 address use + :func:`getnameinfo`. - .. versionchanged:: 3.5 - If the system call is interrupted and the signal handler does not raise - an exception, the method now retries the system call instead of raising - an :exc:`InterruptedError` exception (see :pep:`475` for the rationale). - - -.. method:: socket.recvmsg_into(buffers[, ancbufsize[, flags]]) - - Receive normal data and ancillary data from the socket, behaving as - :meth:`recvmsg` would, but scatter the non-ancillary data into a - series of buffers instead of returning a new bytes object. The - *buffers* argument must be an iterable of objects that export - writable buffers (e.g. :class:`bytearray` objects); these will be - filled with successive chunks of the non-ancillary data until it - has all been written or there are no more buffers. The operating - system may set a limit (:func:`~os.sysconf` value ``SC_IOV_MAX``) - on the number of buffers that can be used. The *ancbufsize* and - *flags* arguments have the same meaning as for :meth:`recvmsg`. - - The return value is a 4-tuple: ``(nbytes, ancdata, msg_flags, - address)``, where *nbytes* is the total number of bytes of - non-ancillary data written into the buffers, and *ancdata*, - *msg_flags* and *address* are the same as for :meth:`recvmsg`. - - Example:: - - >>> import socket - >>> s1, s2 = socket.socketpair() - >>> b1 = bytearray(b'----') - >>> b2 = bytearray(b'0123456789') - >>> b3 = bytearray(b'--------------') - >>> s1.send(b'Mary had a little lamb') - 22 - >>> s2.recvmsg_into([b1, memoryview(b2)[2:9], b3]) - (22, [], 0, None) - >>> [b1, b2, b3] - [bytearray(b'Mary'), bytearray(b'01 had a 9'), bytearray(b'little lamb---')] - - .. availability:: Unix. + .. method:: recvmsg(bufsize[, ancbufsize[, flags]]) + + Receive normal data (up to *bufsize* bytes) and ancillary data from + the socket. The *ancbufsize* argument sets the size in bytes of + the internal buffer used to receive the ancillary data; it defaults + to 0, meaning that no ancillary data will be received. Appropriate + buffer sizes for ancillary data can be calculated using + :func:`CMSG_SPACE` or :func:`CMSG_LEN`, and items which do not fit + into the buffer might be truncated or discarded. The *flags* + argument defaults to 0 and has the same meaning as for + :meth:`recv`. + + The return value is a 4-tuple: ``(data, ancdata, msg_flags, + address)``. The *data* item is a :class:`bytes` object holding the + non-ancillary data received. The *ancdata* item is a list of zero + or more tuples ``(cmsg_level, cmsg_type, cmsg_data)`` representing + the ancillary data (control messages) received: *cmsg_level* and + *cmsg_type* are integers specifying the protocol level and + protocol-specific type respectively, and *cmsg_data* is a + :class:`bytes` object holding the associated data. The *msg_flags* + item is the bitwise OR of various flags indicating conditions on + the received message; see your system documentation for details. + If the receiving socket is unconnected, *address* is the address of + the sending socket, if available; otherwise, its value is + unspecified. + + On some systems, :meth:`sendmsg` and :meth:`recvmsg` can be used to + pass file descriptors between processes over an :const:`AF_UNIX` + socket. When this facility is used (it is often restricted to + :const:`SOCK_STREAM` sockets), :meth:`recvmsg` will return, in its + ancillary data, items of the form ``(socket.SOL_SOCKET, + socket.SCM_RIGHTS, fds)``, where *fds* is a :class:`bytes` object + representing the new file descriptors as a binary array of the + native C :c:expr:`int` type. If :meth:`recvmsg` raises an + exception after the system call returns, it will first attempt to + close any file descriptors received via this mechanism. + + Some systems do not indicate the truncated length of ancillary data + items which have been only partially received. If an item appears + to extend beyond the end of the buffer, :meth:`recvmsg` will issue + a :exc:`RuntimeWarning`, and will return the part of it which is + inside the buffer provided it has not been truncated before the + start of its associated data. + + On systems which support the :const:`!SCM_RIGHTS` mechanism, the + following function will receive up to *maxfds* file descriptors, + returning the message data and a list containing the descriptors + (while ignoring unexpected conditions such as unrelated control + messages being received). See also :meth:`sendmsg`. :: + + import socket, array + + def recv_fds(sock, msglen, maxfds): + fds = array.array("i") # Array of ints + msg, ancdata, flags, addr = sock.recvmsg(msglen, socket.CMSG_LEN(maxfds * fds.itemsize)) + for cmsg_level, cmsg_type, cmsg_data in ancdata: + if cmsg_level == socket.SOL_SOCKET and cmsg_type == socket.SCM_RIGHTS: + # Append data, ignoring any truncated integers at the end. + fds.frombytes(cmsg_data[:len(cmsg_data) - (len(cmsg_data) % fds.itemsize)]) + return msg, list(fds) + + .. availability:: Unix. + + Most Unix platforms. + + .. versionadded:: 3.3 + + .. versionchanged:: 3.5 + If the system call is interrupted and the signal handler does not raise + an exception, the method now retries the system call instead of raising + an :exc:`InterruptedError` exception (see :pep:`475` for the rationale). + + + .. method:: recvmsg_into(buffers[, ancbufsize[, flags]]) + + Receive normal data and ancillary data from the socket, behaving as + :meth:`recvmsg` would, but scatter the non-ancillary data into a + series of buffers instead of returning a new bytes object. The + *buffers* argument must be an iterable of objects that export + writable buffers (e.g. :class:`bytearray` objects); these will be + filled with successive chunks of the non-ancillary data until it + has all been written or there are no more buffers. The operating + system may set a limit (:func:`~os.sysconf` value ``SC_IOV_MAX``) + on the number of buffers that can be used. The *ancbufsize* and + *flags* arguments have the same meaning as for :meth:`recvmsg`. + + The return value is a 4-tuple: ``(nbytes, ancdata, msg_flags, + address)``, where *nbytes* is the total number of bytes of + non-ancillary data written into the buffers, and *ancdata*, + *msg_flags* and *address* are the same as for :meth:`recvmsg`. + + Example:: + + >>> import socket + >>> s1, s2 = socket.socketpair() + >>> b1 = bytearray(b'----') + >>> b2 = bytearray(b'0123456789') + >>> b3 = bytearray(b'--------------') + >>> s1.send(b'Mary had a little lamb') + 22 + >>> s2.recvmsg_into([b1, memoryview(b2)[2:9], b3]) + (22, [], 0, None) + >>> [b1, b2, b3] + [bytearray(b'Mary'), bytearray(b'01 had a 9'), bytearray(b'little lamb---')] + + .. availability:: Unix. + + Most Unix platforms. + + .. versionadded:: 3.3 + + + .. method:: recvfrom_into(buffer[, nbytes[, flags]]) + + Receive data from the socket, writing it into *buffer* instead of creating a + new bytestring. The return value is a pair ``(nbytes, address)`` where *nbytes* is + the number of bytes received and *address* is the address of the socket sending + the data. See the Unix manual page :manpage:`recv(2)` for the meaning of the + optional argument *flags*; it defaults to zero. The format of *address* + depends on the address family --- see :ref:`socket-addresses`. + + + .. method:: recv_into(buffer[, nbytes[, flags]]) + + Receive up to *nbytes* bytes from the socket, storing the data into a buffer + rather than creating a new bytestring. If *nbytes* is not specified (or 0), + receive up to the size available in the given buffer. Returns the number of + bytes received. See the Unix manual page :manpage:`recv(2)` for the meaning + of the optional argument *flags*; it defaults to zero. - Most Unix platforms. - .. versionadded:: 3.3 + .. method:: send(bytes[, flags]) + Send data to the socket. The socket must be connected to a remote socket. The + optional *flags* argument has the same meaning as for :meth:`recv`. + Returns the number of bytes sent. Applications are responsible for checking that + all data has been sent; if only some of the data was transmitted, the + application needs to attempt delivery of the remaining data. For further + information on this topic, consult the :ref:`socket-howto`. -.. method:: socket.recvfrom_into(buffer[, nbytes[, flags]]) + .. versionchanged:: 3.5 + If the system call is interrupted and the signal handler does not raise + an exception, the method now retries the system call instead of raising + an :exc:`InterruptedError` exception (see :pep:`475` for the rationale). - Receive data from the socket, writing it into *buffer* instead of creating a - new bytestring. The return value is a pair ``(nbytes, address)`` where *nbytes* is - the number of bytes received and *address* is the address of the socket sending - the data. See the Unix manual page :manpage:`recv(2)` for the meaning of the - optional argument *flags*; it defaults to zero. The format of *address* - depends on the address family --- see :ref:`socket-addresses`. + .. method:: sendall(bytes[, flags]) -.. method:: socket.recv_into(buffer[, nbytes[, flags]]) + Send data to the socket. The socket must be connected to a remote socket. The + optional *flags* argument has the same meaning as for :meth:`recv`. + Unlike :meth:`send`, this method continues to send data from *bytes* until + either all data has been sent or an error occurs. ``None`` is returned on + success. On error, an exception is raised, and there is no way to determine how + much data, if any, was successfully sent. - Receive up to *nbytes* bytes from the socket, storing the data into a buffer - rather than creating a new bytestring. If *nbytes* is not specified (or 0), - receive up to the size available in the given buffer. Returns the number of - bytes received. See the Unix manual page :manpage:`recv(2)` for the meaning - of the optional argument *flags*; it defaults to zero. + .. versionchanged:: 3.5 + The socket timeout is no longer reset each time data is sent successfully. + The socket timeout is now the maximum total duration to send all data. + .. versionchanged:: 3.5 + If the system call is interrupted and the signal handler does not raise + an exception, the method now retries the system call instead of raising + an :exc:`InterruptedError` exception (see :pep:`475` for the rationale). -.. method:: socket.send(bytes[, flags]) - Send data to the socket. The socket must be connected to a remote socket. The - optional *flags* argument has the same meaning as for :meth:`recv`. - Returns the number of bytes sent. Applications are responsible for checking that - all data has been sent; if only some of the data was transmitted, the - application needs to attempt delivery of the remaining data. For further - information on this topic, consult the :ref:`socket-howto`. + .. method:: sendto(bytes, address) + sendto(bytes, flags, address) - .. versionchanged:: 3.5 - If the system call is interrupted and the signal handler does not raise - an exception, the method now retries the system call instead of raising - an :exc:`InterruptedError` exception (see :pep:`475` for the rationale). + Send data to the socket. The socket should not be connected to a remote socket, + since the destination socket is specified by *address*. The optional *flags* + argument has the same meaning as for :meth:`recv`. Return the number of + bytes sent. The format of *address* depends on the address family --- see + :ref:`socket-addresses`. + .. audit-event:: socket.sendto self,address socket.socket.sendto -.. method:: socket.sendall(bytes[, flags]) + .. versionchanged:: 3.5 + If the system call is interrupted and the signal handler does not raise + an exception, the method now retries the system call instead of raising + an :exc:`InterruptedError` exception (see :pep:`475` for the rationale). - Send data to the socket. The socket must be connected to a remote socket. The - optional *flags* argument has the same meaning as for :meth:`recv`. - Unlike :meth:`send`, this method continues to send data from *bytes* until - either all data has been sent or an error occurs. ``None`` is returned on - success. On error, an exception is raised, and there is no way to determine how - much data, if any, was successfully sent. - .. versionchanged:: 3.5 - The socket timeout is no longer reset each time data is sent successfully. - The socket timeout is now the maximum total duration to send all data. + .. method:: sendmsg(buffers[, ancdata[, flags[, address]]]) - .. versionchanged:: 3.5 - If the system call is interrupted and the signal handler does not raise - an exception, the method now retries the system call instead of raising - an :exc:`InterruptedError` exception (see :pep:`475` for the rationale). + Send normal and ancillary data to the socket, gathering the + non-ancillary data from a series of buffers and concatenating it + into a single message. The *buffers* argument specifies the + non-ancillary data as an iterable of + :term:`bytes-like objects ` + (e.g. :class:`bytes` objects); the operating system may set a limit + (:func:`~os.sysconf` value ``SC_IOV_MAX``) on the number of buffers + that can be used. The *ancdata* argument specifies the ancillary + data (control messages) as an iterable of zero or more tuples + ``(cmsg_level, cmsg_type, cmsg_data)``, where *cmsg_level* and + *cmsg_type* are integers specifying the protocol level and + protocol-specific type respectively, and *cmsg_data* is a + bytes-like object holding the associated data. Note that + some systems (in particular, systems without :func:`CMSG_SPACE`) + might support sending only one control message per call. The + *flags* argument defaults to 0 and has the same meaning as for + :meth:`send`. If *address* is supplied and not ``None``, it sets a + destination address for the message. The return value is the + number of bytes of non-ancillary data sent. + The following function sends the list of file descriptors *fds* + over an :const:`AF_UNIX` socket, on systems which support the + :const:`!SCM_RIGHTS` mechanism. See also :meth:`recvmsg`. :: -.. method:: socket.sendto(bytes, address) - socket.sendto(bytes, flags, address) + import socket, array - Send data to the socket. The socket should not be connected to a remote socket, - since the destination socket is specified by *address*. The optional *flags* - argument has the same meaning as for :meth:`recv`. Return the number of - bytes sent. The format of *address* depends on the address family --- see - :ref:`socket-addresses`. + def send_fds(sock, msg, fds): + return sock.sendmsg([msg], [(socket.SOL_SOCKET, socket.SCM_RIGHTS, array.array("i", fds))]) - .. audit-event:: socket.sendto self,address socket.socket.sendto + .. availability:: Unix, not WASI. - .. versionchanged:: 3.5 - If the system call is interrupted and the signal handler does not raise - an exception, the method now retries the system call instead of raising - an :exc:`InterruptedError` exception (see :pep:`475` for the rationale). - - -.. method:: socket.sendmsg(buffers[, ancdata[, flags[, address]]]) - - Send normal and ancillary data to the socket, gathering the - non-ancillary data from a series of buffers and concatenating it - into a single message. The *buffers* argument specifies the - non-ancillary data as an iterable of - :term:`bytes-like objects ` - (e.g. :class:`bytes` objects); the operating system may set a limit - (:func:`~os.sysconf` value ``SC_IOV_MAX``) on the number of buffers - that can be used. The *ancdata* argument specifies the ancillary - data (control messages) as an iterable of zero or more tuples - ``(cmsg_level, cmsg_type, cmsg_data)``, where *cmsg_level* and - *cmsg_type* are integers specifying the protocol level and - protocol-specific type respectively, and *cmsg_data* is a - bytes-like object holding the associated data. Note that - some systems (in particular, systems without :func:`CMSG_SPACE`) - might support sending only one control message per call. The - *flags* argument defaults to 0 and has the same meaning as for - :meth:`send`. If *address* is supplied and not ``None``, it sets a - destination address for the message. The return value is the - number of bytes of non-ancillary data sent. - - The following function sends the list of file descriptors *fds* - over an :const:`AF_UNIX` socket, on systems which support the - :const:`SCM_RIGHTS` mechanism. See also :meth:`recvmsg`. :: - - import socket, array - - def send_fds(sock, msg, fds): - return sock.sendmsg([msg], [(socket.SOL_SOCKET, socket.SCM_RIGHTS, array.array("i", fds))]) + Most Unix platforms. - .. availability:: Unix, not WASI. + .. audit-event:: socket.sendmsg self,address socket.socket.sendmsg - Most Unix platforms. + .. versionadded:: 3.3 - .. audit-event:: socket.sendmsg self,address socket.socket.sendmsg + .. versionchanged:: 3.5 + If the system call is interrupted and the signal handler does not raise + an exception, the method now retries the system call instead of raising + an :exc:`InterruptedError` exception (see :pep:`475` for the rationale). - .. versionadded:: 3.3 + .. method:: sendmsg_afalg([msg], *, op[, iv[, assoclen[, flags]]]) - .. versionchanged:: 3.5 - If the system call is interrupted and the signal handler does not raise - an exception, the method now retries the system call instead of raising - an :exc:`InterruptedError` exception (see :pep:`475` for the rationale). + Specialized version of :meth:`~socket.sendmsg` for :const:`AF_ALG` socket. + Set mode, IV, AEAD associated data length and flags for :const:`AF_ALG` socket. -.. method:: socket.sendmsg_afalg([msg], *, op[, iv[, assoclen[, flags]]]) + .. availability:: Linux >= 2.6.38. - Specialized version of :meth:`~socket.sendmsg` for :const:`AF_ALG` socket. - Set mode, IV, AEAD associated data length and flags for :const:`AF_ALG` socket. + .. versionadded:: 3.6 - .. availability:: Linux >= 2.6.38. + .. method:: sendfile(file, offset=0, count=None) - .. versionadded:: 3.6 + Send a file until EOF is reached by using high-performance + :mod:`os.sendfile` and return the total number of bytes which were sent. + *file* must be a regular file object opened in binary mode. If + :mod:`os.sendfile` is not available (e.g. Windows) or *file* is not a + regular file :meth:`send` will be used instead. *offset* tells from where to + start reading the file. If specified, *count* is the total number of bytes + to transmit as opposed to sending the file until EOF is reached. File + position is updated on return or also in case of error in which case + :meth:`file.tell() ` can be used to figure out the number of + bytes which were sent. The socket must be of :const:`SOCK_STREAM` type. + Non-blocking sockets are not supported. -.. method:: socket.sendfile(file, offset=0, count=None) + .. versionadded:: 3.5 - Send a file until EOF is reached by using high-performance - :mod:`os.sendfile` and return the total number of bytes which were sent. - *file* must be a regular file object opened in binary mode. If - :mod:`os.sendfile` is not available (e.g. Windows) or *file* is not a - regular file :meth:`send` will be used instead. *offset* tells from where to - start reading the file. If specified, *count* is the total number of bytes - to transmit as opposed to sending the file until EOF is reached. File - position is updated on return or also in case of error in which case - :meth:`file.tell() ` can be used to figure out the number of - bytes which were sent. The socket must be of :const:`SOCK_STREAM` type. - Non-blocking sockets are not supported. + .. method:: set_inheritable(inheritable) - .. versionadded:: 3.5 + Set the :ref:`inheritable flag ` of the socket's file + descriptor or socket's handle. -.. method:: socket.set_inheritable(inheritable) + .. versionadded:: 3.4 - Set the :ref:`inheritable flag ` of the socket's file - descriptor or socket's handle. - .. versionadded:: 3.4 + .. method:: setblocking(flag) + Set blocking or non-blocking mode of the socket: if *flag* is false, the + socket is set to non-blocking, else to blocking mode. -.. method:: socket.setblocking(flag) + This method is a shorthand for certain :meth:`~socket.settimeout` calls: - Set blocking or non-blocking mode of the socket: if *flag* is false, the - socket is set to non-blocking, else to blocking mode. + * ``sock.setblocking(True)`` is equivalent to ``sock.settimeout(None)`` - This method is a shorthand for certain :meth:`~socket.settimeout` calls: + * ``sock.setblocking(False)`` is equivalent to ``sock.settimeout(0.0)`` - * ``sock.setblocking(True)`` is equivalent to ``sock.settimeout(None)`` + .. versionchanged:: 3.7 + The method no longer applies :const:`SOCK_NONBLOCK` flag on + :attr:`socket.type`. - * ``sock.setblocking(False)`` is equivalent to ``sock.settimeout(0.0)`` - .. versionchanged:: 3.7 - The method no longer applies :const:`SOCK_NONBLOCK` flag on - :attr:`socket.type`. + .. method:: settimeout(value) + Set a timeout on blocking socket operations. The *value* argument can be a + nonnegative real number expressing seconds, or ``None``. + If a non-zero value is given, subsequent socket operations will raise a + :exc:`timeout` exception if the timeout period *value* has elapsed before + the operation has completed. If zero is given, the socket is put in + non-blocking mode. If ``None`` is given, the socket is put in blocking mode. -.. method:: socket.settimeout(value) + For further information, please consult the :ref:`notes on socket timeouts `. - Set a timeout on blocking socket operations. The *value* argument can be a - nonnegative real number expressing seconds, or ``None``. - If a non-zero value is given, subsequent socket operations will raise a - :exc:`timeout` exception if the timeout period *value* has elapsed before - the operation has completed. If zero is given, the socket is put in - non-blocking mode. If ``None`` is given, the socket is put in blocking mode. + .. versionchanged:: 3.7 + The method no longer toggles :const:`SOCK_NONBLOCK` flag on + :attr:`socket.type`. - For further information, please consult the :ref:`notes on socket timeouts `. + .. versionchanged:: 3.15 + Accepts any real number, not only integer or float. - .. versionchanged:: 3.7 - The method no longer toggles :const:`SOCK_NONBLOCK` flag on - :attr:`socket.type`. - .. versionchanged:: 3.15 - Accepts any real number, not only integer or float. + .. method:: setsockopt(level, optname, value: int | Buffer) + setsockopt(level, optname, None, optlen: int) + .. index:: pair: module; struct -.. method:: socket.setsockopt(level, optname, value: int | Buffer) - socket.setsockopt(level, optname, None, optlen: int) + Set the value of the given socket option (see the Unix manual page + :manpage:`setsockopt(2)`). The needed symbolic constants are defined in this + module (:ref:`!SO_\* etc. `). The value can be an integer, + ``None`` or a :term:`bytes-like object` representing a buffer. In the latter + case it is up to the caller to ensure that the bytestring contains the + proper bits (see the optional built-in module :mod:`struct` for a way to + encode C structures as bytestrings). When *value* is set to ``None``, + *optlen* argument is required. It's equivalent to calling :c:func:`!setsockopt` C + function with ``optval=NULL`` and ``optlen=optlen``. - .. index:: pair: module; struct + .. versionchanged:: 3.5 + Writable :term:`bytes-like object` is now accepted. - Set the value of the given socket option (see the Unix manual page - :manpage:`setsockopt(2)`). The needed symbolic constants are defined in this - module (:ref:`!SO_\* etc. `). The value can be an integer, - ``None`` or a :term:`bytes-like object` representing a buffer. In the latter - case it is up to the caller to ensure that the bytestring contains the - proper bits (see the optional built-in module :mod:`struct` for a way to - encode C structures as bytestrings). When *value* is set to ``None``, - *optlen* argument is required. It's equivalent to calling :c:func:`setsockopt` C - function with ``optval=NULL`` and ``optlen=optlen``. + .. versionchanged:: 3.6 + setsockopt(level, optname, None, optlen: int) form added. - .. versionchanged:: 3.5 - Writable :term:`bytes-like object` is now accepted. + .. availability:: not WASI. - .. versionchanged:: 3.6 - setsockopt(level, optname, None, optlen: int) form added. - .. availability:: not WASI. + .. method:: shutdown(how) + Shut down one or both halves of the connection. If *how* is :const:`SHUT_RD`, + further receives are disallowed. If *how* is :const:`SHUT_WR`, further sends + are disallowed. If *how* is :const:`SHUT_RDWR`, further sends and receives are + disallowed. -.. method:: socket.shutdown(how) + .. availability:: not WASI. - Shut down one or both halves of the connection. If *how* is :const:`SHUT_RD`, - further receives are disallowed. If *how* is :const:`SHUT_WR`, further sends - are disallowed. If *how* is :const:`SHUT_RDWR`, further sends and receives are - disallowed. - .. availability:: not WASI. + .. method:: share(process_id) + Duplicate a socket and prepare it for sharing with a target process. The + target process must be provided with *process_id*. The resulting bytes object + can then be passed to the target process using some form of interprocess + communication and the socket can be recreated there using :func:`fromshare`. + Once this method has been called, it is safe to close the socket since + the operating system has already duplicated it for the target process. -.. method:: socket.share(process_id) + .. availability:: Windows. - Duplicate a socket and prepare it for sharing with a target process. The - target process must be provided with *process_id*. The resulting bytes object - can then be passed to the target process using some form of interprocess - communication and the socket can be recreated there using :func:`fromshare`. - Once this method has been called, it is safe to close the socket since - the operating system has already duplicated it for the target process. + .. versionadded:: 3.3 - .. availability:: Windows. - .. versionadded:: 3.3 + Note that there are no methods :meth:`!read` or :meth:`!write`; use + :meth:`~socket.recv` and :meth:`~socket.send` without *flags* argument instead. + Socket objects also have these (read-only) attributes that correspond to the + values given to the :class:`~socket.socket` constructor. -Note that there are no methods :meth:`read` or :meth:`write`; use -:meth:`~socket.recv` and :meth:`~socket.send` without *flags* argument instead. -Socket objects also have these (read-only) attributes that correspond to the -values given to the :class:`~socket.socket` constructor. + .. attribute:: family + The socket family. -.. attribute:: socket.family - The socket family. + .. attribute:: type + The socket type. -.. attribute:: socket.type - The socket type. + .. attribute:: proto + The socket protocol. -.. attribute:: socket.proto - The socket protocol. +.. class:: SocketType + The base class of the :class:`~socket.socket` type, re-exported from + :mod:`!_socket`. An instance check such as + ``isinstance(socket(...), SocketType)`` is true, but ``SocketType`` is not + the same as ``type(socket(...))``, which is :class:`~socket.socket` itself. .. _socket-timeouts: From c1cc2377e26c80206c606cc9498f66d3e1343b2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?tonghuaroot=20=28=E7=AB=A5=E8=AF=9D=29?= Date: Wed, 10 Jun 2026 21:03:49 +0800 Subject: [PATCH 103/185] gh-143988: Fix re-entrant mutation crashes in socket sendmsg/recvmsg_into (#143987) Fix crashes in socket.sendmsg() and socket.recvmsg_into() that could occur if buffer sequences are mutated re-entrantly during argument parsing via __buffer__ protocol callbacks. The bug occurs because: 1. PySequence_Fast() returns the original list object when the input is already a list (not a copy). 2. During iteration, PyObject_GetBuffer() triggers __buffer__ callbacks which may clear the list. 3. Subsequent iterations access invalid memory (heap OOB read). The fix replaces PySequence_Fast() with PySequence_Tuple() which always creates a new tuple, ensuring the sequence cannot be mutated during iteration. Co-authored-by: tonghuaroot <23011166+tonghuaroot@users.noreply.github.com> --- Lib/test/test_socket.py | 56 +++++++++++++++++++ ...-01-18-06-42-47.gh-issue-143988.MtLtCP.rst | 2 + Modules/socketmodule.c | 26 +++++---- 3 files changed, 72 insertions(+), 12 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-01-18-06-42-47.gh-issue-143988.MtLtCP.rst diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 94325f5a8003d97..63c465e3bfea16d 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -7529,6 +7529,62 @@ def detach(): pass +class ReentrantMutationTests(unittest.TestCase): + """Regression tests for re-entrant mutation in sendmsg/recvmsg_into. + + These tests verify that mutating sequences during argument parsing + via __buffer__ protocol does not cause crashes. + + See: https://github.com/python/cpython/issues/143988 + """ + + @unittest.skipUnless(hasattr(socket.socket, "sendmsg"), + "sendmsg not supported") + def test_sendmsg_reentrant_data_mutation(self): + seq = [] + + class MutBuffer: + def __init__(self): + self.tripped = False + + def __buffer__(self, flags): + if not self.tripped: + self.tripped = True + seq.clear() + return memoryview(b'Hello') + + seq = [MutBuffer(), b'World', b'Test'] + + left, right = socket.socketpair() + with left, right: + left.sendmsg(seq) + self.assertEqual(right.recv(1024), b'HelloWorldTest') + + @unittest.skipUnless(hasattr(socket.socket, "recvmsg_into"), + "recvmsg_into not supported") + def test_recvmsg_into_reentrant_buffer_mutation(self): + seq = [] + buf1 = bytearray(100) + + class MutBuffer: + def __init__(self): + self.tripped = False + + def __buffer__(self, flags): + if not self.tripped: + self.tripped = True + seq.clear() + return memoryview(buf1) + + seq = [MutBuffer(), bytearray(100), bytearray(100)] + + left, right = socket.socketpair() + with left, right: + left.send(b'Hello World!') + right.recvmsg_into(seq) + self.assertEqual(buf1, b'Hello World!'.ljust(100, b'\x00')) + + def setUpModule(): thread_info = threading_helper.threading_setup() unittest.addModuleCleanup(threading_helper.threading_cleanup, *thread_info) diff --git a/Misc/NEWS.d/next/Library/2026-01-18-06-42-47.gh-issue-143988.MtLtCP.rst b/Misc/NEWS.d/next/Library/2026-01-18-06-42-47.gh-issue-143988.MtLtCP.rst new file mode 100644 index 000000000000000..fcc0cb54934b90e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-01-18-06-42-47.gh-issue-143988.MtLtCP.rst @@ -0,0 +1,2 @@ +Fixed crashes in :meth:`socket.socket.sendmsg` and :meth:`socket.socket.recvmsg_into` +that could occur if buffer sequences are concurrently mutated. diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 3eaa8cf75482397..3e82af3194d053a 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -4526,17 +4526,19 @@ sock_recvmsg_into(PyObject *self, PyObject *args) struct iovec *iovs = NULL; Py_ssize_t i, nitems, nbufs = 0; Py_buffer *bufs = NULL; - PyObject *buffers_arg, *fast, *retval = NULL; + PyObject *buffers_arg, *buffers_tuple, *retval = NULL; if (!PyArg_ParseTuple(args, "O|ni:recvmsg_into", &buffers_arg, &ancbufsize, &flags)) return NULL; - if ((fast = PySequence_Fast(buffers_arg, - "recvmsg_into() argument 1 must be an " - "iterable")) == NULL) + buffers_tuple = PySequence_Tuple(buffers_arg); + if (buffers_tuple == NULL) { + PyErr_SetString(PyExc_TypeError, + "recvmsg_into() argument 1 must be an iterable"); return NULL; - nitems = PySequence_Fast_GET_SIZE(fast); + } + nitems = PyTuple_GET_SIZE(buffers_tuple); if (nitems > INT_MAX) { PyErr_SetString(PyExc_OSError, "recvmsg_into() argument 1 is too long"); goto finally; @@ -4550,7 +4552,7 @@ sock_recvmsg_into(PyObject *self, PyObject *args) goto finally; } for (; nbufs < nitems; nbufs++) { - if (!PyArg_Parse(PySequence_Fast_GET_ITEM(fast, nbufs), + if (!PyArg_Parse(PyTuple_GET_ITEM(buffers_tuple, nbufs), "w*;recvmsg_into() argument 1 must be an iterable " "of single-segment read-write buffers", &bufs[nbufs])) @@ -4566,7 +4568,7 @@ sock_recvmsg_into(PyObject *self, PyObject *args) PyBuffer_Release(&bufs[i]); PyMem_Free(bufs); PyMem_Free(iovs); - Py_DECREF(fast); + Py_DECREF(buffers_tuple); return retval; } @@ -4861,14 +4863,14 @@ sock_sendmsg_iovec(PySocketSockObject *s, PyObject *data_arg, /* Fill in an iovec for each message part, and save the Py_buffer structs to release afterwards. */ - data_fast = PySequence_Fast(data_arg, - "sendmsg() argument 1 must be an " - "iterable"); + data_fast = PySequence_Tuple(data_arg); if (data_fast == NULL) { + PyErr_SetString(PyExc_TypeError, + "sendmsg() argument 1 must be an iterable"); goto finally; } - ndataparts = PySequence_Fast_GET_SIZE(data_fast); + ndataparts = PyTuple_GET_SIZE(data_fast); if (ndataparts > INT_MAX) { PyErr_SetString(PyExc_OSError, "sendmsg() argument 1 is too long"); goto finally; @@ -4890,7 +4892,7 @@ sock_sendmsg_iovec(PySocketSockObject *s, PyObject *data_arg, } } for (; ndatabufs < ndataparts; ndatabufs++) { - if (PyObject_GetBuffer(PySequence_Fast_GET_ITEM(data_fast, ndatabufs), + if (PyObject_GetBuffer(PyTuple_GET_ITEM(data_fast, ndatabufs), &databufs[ndatabufs], PyBUF_SIMPLE) < 0) goto finally; iovs[ndatabufs].iov_base = databufs[ndatabufs].buf; From 584f5ce257d988d0ecf317d80932383f40b7ec32 Mon Sep 17 00:00:00 2001 From: Manoj K M Date: Wed, 10 Jun 2026 18:43:17 +0530 Subject: [PATCH 104/185] Docs: Fix typos in the "Memory Management" section (GH-151243) --- Doc/c-api/memory.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Doc/c-api/memory.rst b/Doc/c-api/memory.rst index 9f84e4bc6dfd91d..73310670ac371c9 100644 --- a/Doc/c-api/memory.rst +++ b/Doc/c-api/memory.rst @@ -77,7 +77,7 @@ memory footprint as a whole. Consequently, under certain circumstances, the Python memory manager may or may not trigger appropriate actions, like garbage collection, memory compaction or other preventive procedures. Note that by using the C library allocator as shown in the previous example, the allocated memory -for the I/O buffer escapes completely the Python memory manager. +for the I/O buffer completely escapes the Python memory manager. .. seealso:: @@ -157,7 +157,7 @@ zero bytes. .. c:function:: void* PyMem_RawCalloc(size_t nelem, size_t elsize) - Allocates *nelem* elements each whose size in bytes is *elsize* and returns + Allocates *nelem* elements each of size *elsize* bytes and returns a pointer of type :c:expr:`void*` to the allocated memory, or ``NULL`` if the request fails. The memory is initialized to zeros. @@ -235,7 +235,7 @@ In the GIL-enabled build (default build) the .. c:function:: void* PyMem_Calloc(size_t nelem, size_t elsize) - Allocates *nelem* elements each whose size in bytes is *elsize* and returns + Allocates *nelem* elements each of size *elsize* bytes and returns a pointer of type :c:expr:`void*` to the allocated memory, or ``NULL`` if the request fails. The memory is initialized to zeros. @@ -368,7 +368,7 @@ The :ref:`default object allocator ` uses the .. c:function:: void* PyObject_Calloc(size_t nelem, size_t elsize) - Allocates *nelem* elements each whose size in bytes is *elsize* and returns + Allocates *nelem* elements each of size *elsize* bytes and returns a pointer of type :c:expr:`void*` to the allocated memory, or ``NULL`` if the request fails. The memory is initialized to zeros. From 6f0676bf39071359daef818bed27c5115b144543 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Wed, 10 Jun 2026 19:32:11 +0530 Subject: [PATCH 105/185] add asyncio guide for Free-Threaded Python (#150456) --- Doc/library/asyncio-threading.rst | 154 ++++++++++++++++++++++++++++++ Doc/library/asyncio.rst | 1 + 2 files changed, 155 insertions(+) create mode 100644 Doc/library/asyncio-threading.rst diff --git a/Doc/library/asyncio-threading.rst b/Doc/library/asyncio-threading.rst new file mode 100644 index 000000000000000..526901a2e7eb206 --- /dev/null +++ b/Doc/library/asyncio-threading.rst @@ -0,0 +1,154 @@ +.. currentmodule:: asyncio + +.. _asyncio-threading: + +asyncio and free-threaded Python +================================ + +asyncio uses an event loop as a scheduler to enable highly efficient +concurrency by switching between tasks to allow non-blocking I/O +operations. This results in better performance for I/O-bound use +cases. It also allows off-loading CPU-bound work to a thread or +process pool, but that is still limited by the :term:`global +interpreter lock` in CPython. + +However, in :ref:`free-threaded Python `, +the GIL is disabled and Python can run true multi-threaded code. This +means that asyncio can now take advantage of multiple CPU cores without +the limitations imposed by the GIL. + +Since Python 3.14, asyncio has first-class support for free-threaded +Python, and the implementation of asyncio is safe to use in a +multi-threaded environment. + +A single event loop on one core can handle many connections +concurrently, but the Python code that runs to handle each one still +executes serially. Once requests involve a non-trivial amount of +per-request computation, that handling becomes the bottleneck, and a +single core can no longer keep up. Combining asyncio with threads is +most useful here: by running an event loop per thread, the handling of +different requests can run in parallel across multiple CPU cores. It is +also useful when you need to run blocking or CPU-bound code from an +asyncio application. + + +.. seealso:: + + `Scaling asyncio on Free-Threaded Python + `__, + a blog post by Kumar Aditya which explains the internal changes + that make asyncio safe and efficient under free-threaded Python, + together with benchmarks of the resulting improvements. + + +Thread safety considerations +---------------------------- + +While asyncio is designed to be thread-safe in a free-threaded Python +environment, there are still some considerations to keep in mind when +using asyncio with threads: + +1. **Event loop**: Each thread should have its own event loop which + should not be shared across threads. This ensures that the event loop + can manage its own tasks and callbacks without interference from + other threads. + +2. **Task management**: Tasks and futures created in one thread should + not be awaited or manipulated from another thread. + +3. **Thread-safe APIs**: When interacting with asyncio from multiple + threads, it's important to use thread-safe APIs provided by asyncio, + such as :func:`asyncio.run_coroutine_threadsafe` for submitting + coroutines to an event loop from another thread. If you need to + call a callback from a different thread, you can use + :meth:`loop.call_soon_threadsafe` to schedule it safely. + +4. **Synchronization**: The synchronization primitives provided by + asyncio (like :class:`asyncio.Lock` and :class:`asyncio.Event`) + are not designed to be used across threads. If you need to + synchronize between threads, you should use the synchronization + primitives from the :mod:`threading` module instead. + + +Using asyncio with threads +-------------------------- + +asyncio supports running one event loop per thread, which allows you to +take advantage of multiple CPU cores in a free-threaded Python +environment. Each thread can run its own event loop, and tasks can be +scheduled on those loops independently. + +Here's an example of how to use asyncio with threads:: + + import asyncio + import threading + + async def worker(name: str) -> None: + print(f"Worker {name} starting") + await asyncio.sleep(1) + print(f"Worker {name} done") + + def run_loop(name: str) -> None: + asyncio.run(worker(name)) + + threads = [ + threading.Thread(target=run_loop, args=(f"T{i}",)) + for i in range(4) + ] + for t in threads: + t.start() + for t in threads: + t.join() + +In this example, each thread creates its own event loop with +:func:`asyncio.run` and runs a coroutine on it. The threads execute +concurrently, and in a free-threaded build they can run on separate +CPU cores in parallel. + + +Producer/consumer across threads +-------------------------------- + +When a regular (non-asyncio) thread needs to hand work to an asyncio +event loop running in another thread, use a thread-safe primitive such +as :class:`queue.Queue` rather than :class:`asyncio.Queue`, which is +only safe within a single event loop.:: + + import asyncio + import queue + import threading + + def producer(q: queue.Queue[int]) -> None: + for i in range(5): + print(f"Producing {i}") + q.put(i) + q.shutdown() + + async def consumer(q: queue.Queue[int]) -> None: + while True: + try: + item = q.get_nowait() + except queue.Empty: + await asyncio.sleep(0.1) + continue + except queue.ShutDown: + break + print(f"Consumed {item}") + await asyncio.sleep(item) + + q: queue.Queue[int] = queue.Queue() + consumer_thread = threading.Thread( + target=lambda: asyncio.run(consumer(q)) + ) + consumer_thread.start() + producer(q) + consumer_thread.join() + +The producer runs on the main thread while the consumer runs inside an +event loop on its own thread, yet they communicate safely through +``queue.Queue``. When the queue is empty the consumer sleeps briefly +and tries again. When the producer is done it calls +:meth:`~queue.Queue.shutdown`, which causes subsequent +:meth:`~queue.Queue.get_nowait` calls to raise :exc:`queue.ShutDown` +so the consumer can exit cleanly. + diff --git a/Doc/library/asyncio.rst b/Doc/library/asyncio.rst index 0f72e31dee5f1d1..90a465f3e1d3af4 100644 --- a/Doc/library/asyncio.rst +++ b/Doc/library/asyncio.rst @@ -128,6 +128,7 @@ for full functionality and the latest features. asyncio-api-index.rst asyncio-llapi-index.rst asyncio-dev.rst + asyncio-threading.rst .. note:: The source code for asyncio can be found in :source:`Lib/asyncio/`. From 1a373fa7a614537105bcf991b6c0c3695f2cdb08 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 10 Jun 2026 16:45:30 +0200 Subject: [PATCH 106/185] gh-141984: Reword the Generator expressions section (GH-150518) Co-authored-by: Blaise Pabon Co-authored-by: Stan Ulbrych --- Doc/reference/expressions.rst | 113 ++++++++++++++++++++++++++-------- 1 file changed, 87 insertions(+), 26 deletions(-) diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst index a67e1e100178723..fe38e06cd1dcd2d 100644 --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -956,39 +956,100 @@ Generator expressions pair: object; generator single: () (parentheses); generator expression -A generator expression is a compact generator notation in parentheses: +The syntax for :dfn:`generator expressions` is the same as for +list :ref:`comprehensions `, except that they are enclosed in +parentheses instead of brackets. +For example:: -.. productionlist:: python-grammar - generator_expression: "(" `comprehension` ")" + >>> iterator = (x ** 2 for x in range(10)) + >>> iterator + at ...> + +At runtime, a generator expression evaluates to a :term:`generator iterator` +which yields the same values as the corresponding list comprehension:: + + >>> list(iterator) + [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] + +Thus, the example above is roughly equivalent to defining and calling +the following generator function:: -A generator expression yields a new generator object. Its syntax is the same as -for comprehensions, except that it is enclosed in parentheses instead of -brackets or curly braces. - -Variables used in the generator expression are evaluated lazily when the -:meth:`~generator.__next__` method is called for the generator object (in the same -fashion as normal generators). However, the iterable expression in the -leftmost :keyword:`!for` clause is immediately evaluated, and the -:term:`iterator` is immediately created for that iterable, so that an error -produced while creating the iterator will be emitted at the point where the generator expression -is defined, rather than at the point where the first value is retrieved. -Subsequent :keyword:`!for` clauses and any filter condition in the leftmost -:keyword:`!for` clause cannot be evaluated in the enclosing scope as they may -depend on the values obtained from the leftmost iterable. For example: -``(x*y for x in range(10) for y in range(x, x+10))``. - -The parentheses can be omitted on calls with only one argument. See section -:ref:`calls` for details. + def make_generator_of_squares(iterator): + for x in iterator: + yield x ** 2 + + make_generator_of_squares(iter(range(10))) + +The enclosing parentheses can be omitted in calls when the generator +expression is the only positional argument and there are no keyword +arguments. +See the :ref:`Calls section ` for details. +For example:: + + # The parentheses after `sum` are part of the call syntax: + >>> sum(x ** 2 for x in range(10)) + 285 + + # The generator needs its own parentheses if it's not the only argument: + >>> sum((x ** 2 for x in range(10)), start=1000) + 1285 + +The iterable expression in the leftmost :keyword:`!for` clause is +evaluated immediately, so that an error raised by this expression will be +emitted at the point where the generator expression is defined, +rather than at the point where the first value is retrieved:: + + >>> (x ** 2 for x in nonexistent_iterable) + Traceback (most recent call last): + ... + NameError: name 'nonexistent_iterable' is not defined + +After the expression is evaluated, an iterator is created +from the result, as if :py:func:`iter` was called on it. +Any error raised when creating the iterator is also emitted immediately:: + + >>> (x ** 2 for x in None) + Traceback (most recent call last): + ... + TypeError: 'NoneType' object is not iterable + +All other expressions are evaluated lazily, in the same fashion as normal +generators (that is, when the iterator is asked to yield a value):: + + >>> iterator = (nonexistent_value for x in range(10)) + >>> iterator + at ...> + >>> list(iterator) + Traceback (most recent call last): + ... + NameError: name 'nonexistent_value' is not defined + +:: + + >>> iterator = (x * y for x in range(10) for y in nonexistent_iterable) + >>> iterator + at ...> + >>> list(iterator) + Traceback (most recent call last): + ... + NameError: name 'nonexistent_iterable' is not defined To avoid interfering with the expected operation of the generator expression -itself, ``yield`` and ``yield from`` expressions are prohibited in the -implicitly defined generator. +itself, ``yield`` and ``yield from`` expressions are prohibited inside +the implicitly nested scope. If a generator expression contains either :keyword:`!async for` clauses or :keyword:`await` expressions it is called an -:dfn:`asynchronous generator expression`. An asynchronous generator -expression returns a new asynchronous generator object, -which is an asynchronous iterator (see :ref:`async-iterators`). +:dfn:`asynchronous generator expression`. +An asynchronous generator expression returns a new asynchronous generator +object, which is an asynchronous iterator (see :ref:`async-iterators`). + +The formal grammar for generator expressions is: + +.. grammar-snippet:: + :group: python-grammar + + generator_expression: "(" `comprehension` ")" .. versionadded:: 3.6 Asynchronous generator expressions were introduced. From 1798842cda3ad3d3cdb025a4912e66c574ea402f Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 10 Jun 2026 18:04:03 +0300 Subject: [PATCH 107/185] gh-62259: Add Tools/unicode/gen_expat_table.py (GH-150503) It was used to generate the _expat_decoding_table attribute of CodecInfo. --- Tools/unicode/gen_expat_table.py | 107 +++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 Tools/unicode/gen_expat_table.py diff --git a/Tools/unicode/gen_expat_table.py b/Tools/unicode/gen_expat_table.py new file mode 100644 index 000000000000000..ff08fb4c87dab39 --- /dev/null +++ b/Tools/unicode/gen_expat_table.py @@ -0,0 +1,107 @@ +import sys + + +def info(msg, *args): + print(msg % args, file=sys.stderr) + +def fail(msg, *args): + info(msg, *args) + sys.exit(2) + +# ASCII characters that can appear in a well-formed XML document +# except the characters "$@\^`{}~". +compulsory_chars = (set(b'\t\n\r') | set(range(32, 127))) - set(br'$@\^`{}~') + +def check_compatibility(encoding): + for i in compulsory_chars: + c = chr(i) + b = bytes([i]) + try: + decoded = b.decode(encoding) + except UnicodeDecodeError: + info('Cannot decode byte %#04x (%a)', i, c) + return False + if decoded != c: + info('Incompatible encoding: %#04x (%a) -> %a', i, c, decoded) + return False + return True + +def create_table(encoding): + mapping = [-1] * 256 + + non_bmp = None + for i in range(0x110000): + char = chr(i) + try: + encoded = char.encode(encoding) + except UnicodeEncodeError: + continue + if i >= 0x10000 and non_bmp is None: + non_bmp = i + info('Non-BMP character: %r (U+%04X)', char, i) + length = len(encoded) + k = encoded[0] + v = mapping[k] + if length == 1: + if v == -1: + mapping[k] = i + elif v < 0: + fail('Ambiguous mapping for %#04x: ' + '%r (U+%04X) and %d-byte sequence', + k, c, i, -v) + else: + info('Ambiguous mapping for %#04x: ' + '%r (U+%04X) and %r (U+%04X)', + k, chr(v), v, char, i) + else: + if length > 4: + fail('Too long encoding for %r (U+%04X): %r', + char, i, encoded) + if v == -1: + mapping[k] = -length + elif v != -length: + if v < 0: + fail('Ambiguous mapping for %#04x: ' + '%d-byte sequence and %d-byte sequence %r', + k, -v, length, encoded) + else: + fail('Ambiguous mapping for %#04x: ' + '%r (U+%04X) and %d-byte sequence %r', + k, chr(v), v, length, encoded) + return mapping + +def print_table(mapping): + print(' '*8 + '_expat_decoding_table=(', end='') + if mapping[:128] == list(range(128)): + print('*range(128),') + start = 128 + else: + print() + start = 0 + line = '' + pos = 0 + i = start + while True: + if i % 8 == 0: + if len(line) > 68: + print(' '*12 + line[:pos].rstrip()) + line = line[pos:] + pos = len(line) + if i == 256: + break + v = mapping[i] + if v >= 0: + v = hex(v) + line += f'{v}, ' + i += 1 + print(' '*12 + line.rstrip().rstrip(',') + '),') + + +encoding = sys.argv[1] + +if not check_compatibility(encoding): + print(' '*8 + '_expat_decoding_table=False,') + sys.exit(1) + +mapping = create_table(encoding) +print_table(mapping) From 13d4497f56eb98b02aa4c845ab8fede7c22fc534 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Wed, 10 Jun 2026 18:59:11 +0300 Subject: [PATCH 108/185] gh-151126: Fix missing memory errors in `_interpchannelsmodule.c` (#151239) --- .../2026-06-09-10-28-30.gh-issue-151126.DKa6Sl.rst | 6 ++---- Modules/_interpchannelsmodule.c | 6 +++++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-09-10-28-30.gh-issue-151126.DKa6Sl.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-09-10-28-30.gh-issue-151126.DKa6Sl.rst index 81e87e539865ce3..67e2ce4044431f9 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-09-10-28-30.gh-issue-151126.DKa6Sl.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-09-10-28-30.gh-issue-151126.DKa6Sl.rst @@ -1,7 +1,5 @@ Fix a crash, when there's no memory left on a device, -which happened in: - -- code compilation -- :func:`!_winapi.CreateProcess` +which happened in: code compilation, :mod:`!_interpchannels` module, +:func:`!_winapi.CreateProcess` function. Now these places raise proper :exc:`MemoryError` errors. diff --git a/Modules/_interpchannelsmodule.c b/Modules/_interpchannelsmodule.c index c6d107d243dda0e..3614890757d69da 100644 --- a/Modules/_interpchannelsmodule.c +++ b/Modules/_interpchannelsmodule.c @@ -921,7 +921,8 @@ static _channelends * _channelends_new(void) { _channelends *ends = GLOBAL_MALLOC(_channelends); - if (ends== NULL) { + if (ends == NULL) { + PyErr_NoMemory(); return NULL; } ends->numsendopen = 0; @@ -1115,6 +1116,7 @@ _channel_new(PyThread_type_lock mutex, struct _channeldefaults defaults) assert(check_unbound(defaults.unboundop)); _channel_state *chan = GLOBAL_MALLOC(_channel_state); if (chan == NULL) { + PyErr_NoMemory(); return NULL; } chan->mutex = mutex; @@ -1313,6 +1315,7 @@ _channelref_new(int64_t cid, _channel_state *chan) { _channelref *ref = GLOBAL_MALLOC(_channelref); if (ref == NULL) { + PyErr_NoMemory(); return NULL; } ref->cid = cid; @@ -1698,6 +1701,7 @@ _channel_set_closing(_channelref *ref, PyThread_type_lock mutex) { } chan->closing = GLOBAL_MALLOC(struct _channel_closing); if (chan->closing == NULL) { + PyErr_NoMemory(); goto done; } chan->closing->ref = ref; From 16b5977d313ed41bd162f0008abc31a0e36fe948 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 10 Jun 2026 18:43:38 +0200 Subject: [PATCH 109/185] gh-151253: Dump the Python path configuration on _PyCodec_InitRegistry() failure (#151250) If "import encodings" fails at Python startup, dump the Python path configuration to help users debugging their configuration. The encodings module is the first module imported during Python startup. --- Lib/test/test_cmd_line.py | 11 +++++++++++ .../2026-06-10-15-42-46.gh-issue-151253.7MMQ8P.rst | 3 +++ Python/codecs.c | 3 +++ 3 files changed, 17 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-06-10-15-42-46.gh-issue-151253.7MMQ8P.rst diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py index 3b556ec31445dfb..a8645af26b25d87 100644 --- a/Lib/test/test_cmd_line.py +++ b/Lib/test/test_cmd_line.py @@ -1314,6 +1314,17 @@ def test_presite(self): proc = assert_python_ok("-X", f"presite={entrypoint}", "-c", "pass") self.assertEqual(proc.out.rstrip(), b"presite func") + def test_dump_path_config(self): + # gh-151253: At the first import (import encodings) during Python + # startup, if the import fails, dump the Python path configuration. + nonexistent = '/nonexistent-python-path' + # Use -X frozen_modules=off to disable frozen encodings module + # on release build. + cmd = ["-X", "frozen_modules=off", "-c", "pass"] + proc = assert_python_failure(*cmd, PYTHONHOME=nonexistent) + self.assertIn(b'Python path configuration:', proc.err) + self.assertIn(f"PYTHONHOME = '{nonexistent}'".encode(), proc.err) + @unittest.skipIf(interpreter_requires_environment(), 'Cannot run -I tests when PYTHON env vars are required.') diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-10-15-42-46.gh-issue-151253.7MMQ8P.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-10-15-42-46.gh-issue-151253.7MMQ8P.rst new file mode 100644 index 000000000000000..56d2f3b2633bb01 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-10-15-42-46.gh-issue-151253.7MMQ8P.rst @@ -0,0 +1,3 @@ +If ``import encodings`` (first import) fails at Python startup, dump the +Python path configuration to help users debugging their configuration. Patch +by Victor Stinner. diff --git a/Python/codecs.c b/Python/codecs.c index a522e6b88068b3e..6d1ae651fa00058 100644 --- a/Python/codecs.c +++ b/Python/codecs.c @@ -11,6 +11,7 @@ Copyright (c) Corporation for National Research Initiatives. #include "Python.h" #include "pycore_call.h" // _PyObject_CallNoArgs() #include "pycore_codecs.h" // export _PyCodec_LookupTextEncoding() +#include "pycore_initconfig.h" // _Py_DumpPathConfig() #include "pycore_interp.h" // PyInterpreterState.codec_search_path #include "pycore_pyerrors.h" // _PyErr_FormatNote() #include "pycore_pystate.h" // _PyInterpreterState_GET() @@ -1686,6 +1687,8 @@ _PyCodec_InitRegistry(PyInterpreterState *interp) // search functions, so this is done after everything else is initialized. PyObject *mod = PyImport_ImportModule("encodings"); if (mod == NULL) { + PyThreadState *tstate = _PyThreadState_GET(); + _Py_DumpPathConfig(tstate); return PyStatus_Error("Failed to import encodings module"); } Py_DECREF(mod); From 795771473a5562f58130d959c190065153dc494b Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 10 Jun 2026 19:07:51 +0200 Subject: [PATCH 110/185] GHA: Display output when a sanitizer test fails (#151268) Modify GitHub Action "Reusable Sanitizer" to display output when a test fails: pass -W option. --- .github/workflows/reusable-san.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/reusable-san.yml b/.github/workflows/reusable-san.yml index 33f6f0ef455fe02..d1e9cb9636698b9 100644 --- a/.github/workflows/reusable-san.yml +++ b/.github/workflows/reusable-san.yml @@ -89,12 +89,12 @@ jobs: ./python -m test ${{ inputs.sanitizer == 'TSan' && '--tsan' || '' }} ${{ inputs.sanitizer == 'UBSan' && '-x test_capi -x test_faulthandler' || '' }} - -j4 + -j4 -W - name: Parallel tests if: >- inputs.sanitizer == 'TSan' && fromJSON(inputs.free-threading) - run: ./python -m test --tsan-parallel --parallel-threads=4 -j4 + run: ./python -m test --tsan-parallel --parallel-threads=4 -j4 -W - name: Display logs if: always() run: find "${GITHUB_WORKSPACE}" -name 'san_log.*' | xargs head -n 1000 From 29b3b75fb56bd172087a95316f4aa162252de884 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 10 Jun 2026 21:39:03 +0200 Subject: [PATCH 111/185] gh-151278: Fix test_faulthandler on UBSan (#151279) * Py_FatalError() no longer calls _PyFaulthandler_Fini() if it doesn't hold the GIL. * Skip test_faulthandler tests raising signals if run with UBSan. * Enable test_faulthandler in GitHub Action "Reusable Sanitizer". --- .github/workflows/reusable-san.yml | 4 ++-- Lib/test/test_faulthandler.py | 13 +++++++++---- Python/pylifecycle.c | 4 +++- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/.github/workflows/reusable-san.yml b/.github/workflows/reusable-san.yml index d1e9cb9636698b9..6127c7e1c053690 100644 --- a/.github/workflows/reusable-san.yml +++ b/.github/workflows/reusable-san.yml @@ -82,13 +82,13 @@ jobs: run: make -j4 - name: Display build info run: make pythoninfo - # test_{capi,faulthandler} are skipped under UBSan because + # test_capi is skipped under UBSan because # they raise signals that UBSan with halt_on_error=1 intercepts. - name: Tests run: >- ./python -m test ${{ inputs.sanitizer == 'TSan' && '--tsan' || '' }} - ${{ inputs.sanitizer == 'UBSan' && '-x test_capi -x test_faulthandler' || '' }} + ${{ inputs.sanitizer == 'UBSan' && '-x test_capi' || '' }} -j4 -W - name: Parallel tests if: >- diff --git a/Lib/test/test_faulthandler.py b/Lib/test/test_faulthandler.py index 11df59f2346f316..5a493a4fd956802 100644 --- a/Lib/test/test_faulthandler.py +++ b/Lib/test/test_faulthandler.py @@ -33,6 +33,11 @@ CURRENT_THREAD_HEADER = fr'{CURRENT_THREAD_ID} \(most recent call first\):' +def skip_if_sanitizer_signal(signame): + return support.skip_if_sanitizer(f"TSAN/UBSan itercepts {signame}", + thread=True, ub=True) + + def expected_traceback(lineno1, lineno2, header, min_count=1): regex = header regex += ' File "", line %s in func\n' % lineno1 @@ -224,7 +229,7 @@ def test_fatal_error_c_thread(self): func='faulthandler_fatal_error_thread', py_fatal_error=True) - @support.skip_if_sanitizer("TSAN itercepts SIGABRT", thread=True) + @skip_if_sanitizer_signal("SIGABRT") def test_sigabrt(self): self.check_fatal_error(""" import faulthandler @@ -236,7 +241,7 @@ def test_sigabrt(self): @unittest.skipIf(sys.platform == 'win32', "SIGFPE cannot be caught on Windows") - @support.skip_if_sanitizer("TSAN itercepts SIGFPE", thread=True) + @skip_if_sanitizer_signal("SIGFPE") def test_sigfpe(self): self.check_fatal_error(""" import faulthandler @@ -248,7 +253,7 @@ def test_sigfpe(self): @unittest.skipIf(_testcapi is None, 'need _testcapi') @unittest.skipUnless(hasattr(signal, 'SIGBUS'), 'need signal.SIGBUS') - @support.skip_if_sanitizer("TSAN itercepts SIGBUS", thread=True) + @skip_if_sanitizer_signal("SIGBUS") @skip_segfault_on_android def test_sigbus(self): self.check_fatal_error(""" @@ -263,7 +268,7 @@ def test_sigbus(self): @unittest.skipIf(_testcapi is None, 'need _testcapi') @unittest.skipUnless(hasattr(signal, 'SIGILL'), 'need signal.SIGILL') - @support.skip_if_sanitizer("TSAN itercepts SIGILL", thread=True) + @skip_if_sanitizer_signal("SIGILL") @skip_segfault_on_android def test_sigill(self): self.check_fatal_error(""" diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 97511d2daf2c8f7..2f7f10f523c2c1d 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -3731,7 +3731,9 @@ fatal_error(int fd, int header, const char *prefix, const char *msg, This function already did its best to display a traceback. Disable faulthandler to prevent writing a second traceback on abort(). */ - _PyFaulthandler_Fini(); + if (has_tstate_and_gil) { + _PyFaulthandler_Fini(); + } /* Check if the current Python thread hold the GIL */ if (has_tstate_and_gil) { From 5b894646ba8cca6c1315ab1e2775708c69b68f14 Mon Sep 17 00:00:00 2001 From: Victorien <65306057+Viicos@users.noreply.github.com> Date: Wed, 10 Jun 2026 22:43:09 +0200 Subject: [PATCH 112/185] gh-139819: Use builtin `sentinel` in `rlcompleter` (GH-151222) Co-authored-by: Ethan Furman --- Lib/rlcompleter.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/rlcompleter.py b/Lib/rlcompleter.py index 271b77a322fdd2b..26fcda612567ea4 100644 --- a/Lib/rlcompleter.py +++ b/Lib/rlcompleter.py @@ -41,7 +41,7 @@ __all__ = ["Completer"] # Sentinel object to distinguish "missing" from "present but None" -_SENTINEL = object() +_MISSING = sentinel("MISSING") class Completer: def __init__(self, namespace = None): @@ -200,9 +200,9 @@ def attr_matches(self, text): ): value = thisobject.__dict__.get(word) else: - value = getattr(thisobject, word, _SENTINEL) + value = getattr(thisobject, word, _MISSING) - if value is not _SENTINEL: + if value is not _MISSING: matches.append(self._callable_postfix(value, match)) elif word in getattr(type(thisobject), '__slots__', ()): matches.append(match) From d9ecd3802254785a2146123a87382ad5c9e00d1a Mon Sep 17 00:00:00 2001 From: glennglazer Date: Wed, 10 Jun 2026 23:14:34 -0700 Subject: [PATCH 113/185] gh-136880: Add warning about PYTHONPATH (GH-151098) --- Doc/tutorial/venv.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Doc/tutorial/venv.rst b/Doc/tutorial/venv.rst index f362e1943b666f7..6b6b8a768e3f58d 100644 --- a/Doc/tutorial/venv.rst +++ b/Doc/tutorial/venv.rst @@ -88,6 +88,11 @@ For example: '~/envs/tutorial-env/lib/python3.5/site-packages'] >>> +Note that the activated virtual environment does not alter the ``PYTHONPATH`` variable in any way. +This may lead to unexpected results if the path includes references to code which is incompatible with +the Python version the virtual environment is using. The best practice is to ``unset PYTHONPATH`` +in bash or the equivalent for the shell you are using. + To deactivate a virtual environment, type:: deactivate From c73e4629d2e985981f703eafda822e517ee787c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?tonghuaroot=20=28=E7=AB=A5=E8=AF=9D=29?= Date: Thu, 11 Jun 2026 15:51:43 +0800 Subject: [PATCH 114/185] gh-151295: Fix use-after-free in bytes.join()/bytearray.join() via re-entrant __buffer__ (GH-151296) --- Lib/test/test_bytes.py | 26 +++++++++++++++++++ ...-06-11-00-00-00.gh-issue-151295.NQYUzW.rst | 4 +++ Objects/stringlib/join.h | 5 ++++ 3 files changed, 35 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2026-06-11-00-00-00.gh-issue-151295.NQYUzW.rst diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py index e0e8dd4eccfb1b2..e211c3d15a4ed20 100644 --- a/Lib/test/test_bytes.py +++ b/Lib/test/test_bytes.py @@ -645,6 +645,32 @@ def test_join(self): with self.assertRaises(TypeError): dot_join([memoryview(b"ab"), "cd", b"ef"]) + def test_join_concurrent_buffer_mutation(self): + # __buffer__() can release the GIL, letting another thread concurrently + # mutate the joined sequence (simulated here by mutating in __buffer__). + # See: https://github.com/python/cpython/issues/151295 + def make_seq(mutate): + # Item is only referenced from the list slot, so mutate() frees it. + class Item: + def __buffer__(self, flags): + mutate(seq) + return memoryview(b'x') + seq = [b'a', Item(), b'c'] + return seq + + for sep in (self.type2test(b''), self.type2test(b'::')): + with self.subTest(sep=sep): + # Changing the list length is reported as a RuntimeError. + seq = make_seq(lambda seq: seq.clear()) + self.assertRaises(RuntimeError, sep.join, seq) + + # The list length is unchanged, so the size-change recheck + # cannot fire: only keeping the item alive avoids the crash. + def replace(seq): + seq[1] = b'z' + seq = make_seq(replace) + self.assertEqual(sep.join(seq), sep.join([b'a', b'x', b'c'])) + def test_count(self): b = self.type2test(b'mississippi') i = 105 diff --git a/Misc/NEWS.d/next/Library/2026-06-11-00-00-00.gh-issue-151295.NQYUzW.rst b/Misc/NEWS.d/next/Library/2026-06-11-00-00-00.gh-issue-151295.NQYUzW.rst new file mode 100644 index 000000000000000..e9012f023ff7f77 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-11-00-00-00.gh-issue-151295.NQYUzW.rst @@ -0,0 +1,4 @@ +Fixed a crash (use-after-free) in :meth:`bytes.join` and +:meth:`bytearray.join` that could occur if an item's +:meth:`~object.__buffer__` concurrently mutates the sequence being joined. +The mutation is now reported as a :exc:`RuntimeError` instead. diff --git a/Objects/stringlib/join.h b/Objects/stringlib/join.h index de6bd83ffe4c8bc..deebfeadc0f4fd8 100644 --- a/Objects/stringlib/join.h +++ b/Objects/stringlib/join.h @@ -68,13 +68,18 @@ STRINGLIB(bytes_join)(PyObject *sep, PyObject *iterable) buffers[i].len = PyBytes_GET_SIZE(item); } else { + /* item is only borrowed; its __buffer__() may run Python that + drops the sequence's last reference to it. */ + Py_INCREF(item); if (PyObject_GetBuffer(item, &buffers[i], PyBUF_SIMPLE) != 0) { + Py_DECREF(item); PyErr_Format(PyExc_TypeError, "sequence item %zd: expected a bytes-like object, " "%.80s found", i, Py_TYPE(item)->tp_name); goto error; } + Py_DECREF(item); /* If the backing objects are mutable, then dropping the GIL * opens up race conditions where another thread tries to modify * the object which we hold a buffer on it. Such code has data From 4f4343439963475b4b6b3b565de1a6f06f6e27d9 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Thu, 11 Jun 2026 09:46:39 +0100 Subject: [PATCH 115/185] Drop historical `:author:`s from HOWTOs (#151091) --- Doc/howto/annotations.rst | 2 -- Doc/howto/argparse.rst | 2 -- Doc/howto/curses.rst | 3 --- Doc/howto/descriptor.rst | 3 --- Doc/howto/functional.rst | 12 +++++------- Doc/howto/instrumentation.rst | 3 --- Doc/howto/ipaddress.rst | 3 --- Doc/howto/logging-cookbook.rst | 2 -- Doc/howto/logging.rst | 2 -- Doc/howto/perf_profiling.rst | 2 -- Doc/howto/pyporting.rst | 2 -- Doc/howto/regex.rst | 2 -- Doc/howto/sockets.rst | 3 --- Doc/howto/sorting.rst | 3 --- Doc/howto/unicode.rst | 2 -- Doc/howto/urllib2.rst | 3 --- 16 files changed, 5 insertions(+), 44 deletions(-) diff --git a/Doc/howto/annotations.rst b/Doc/howto/annotations.rst index e2a5aa708d7b630..fcc4fe838c783ba 100644 --- a/Doc/howto/annotations.rst +++ b/Doc/howto/annotations.rst @@ -4,8 +4,6 @@ Annotations Best Practices ************************** -:author: Larry Hastings - .. topic:: Abstract This document is designed to encapsulate the best practices diff --git a/Doc/howto/argparse.rst b/Doc/howto/argparse.rst index 902c50de00803c5..0cb8c5cc3ebd369 100644 --- a/Doc/howto/argparse.rst +++ b/Doc/howto/argparse.rst @@ -4,8 +4,6 @@ Argparse Tutorial ***************** -:author: Tshepang Mbambo - .. currentmodule:: argparse This tutorial is intended to be a gentle introduction to :mod:`argparse`, the diff --git a/Doc/howto/curses.rst b/Doc/howto/curses.rst index 816639552d7cd6d..e5f85e0110321c8 100644 --- a/Doc/howto/curses.rst +++ b/Doc/howto/curses.rst @@ -6,9 +6,6 @@ .. currentmodule:: curses -:Author: A.M. Kuchling, Eric S. Raymond -:Release: 2.04 - .. topic:: Abstract diff --git a/Doc/howto/descriptor.rst b/Doc/howto/descriptor.rst index 60dff850a61e2b1..7233898860ada14 100644 --- a/Doc/howto/descriptor.rst +++ b/Doc/howto/descriptor.rst @@ -4,9 +4,6 @@ Descriptor Guide ================ -:Author: Raymond Hettinger -:Contact: - .. Contents:: diff --git a/Doc/howto/functional.rst b/Doc/howto/functional.rst index ebc7a100d91a646..a61fdaee27f6b18 100644 --- a/Doc/howto/functional.rst +++ b/Doc/howto/functional.rst @@ -4,9 +4,6 @@ Functional Programming HOWTO ******************************** -:Author: \A. M. Kuchling -:Release: 0.32 - In this document, we'll take a tour of Python's features suitable for implementing programs in a functional style. After an introduction to the concepts of functional programming, we'll look at language features such as @@ -1185,7 +1182,8 @@ about whether this lambda-free style is better. Revision History and Acknowledgements ===================================== -The author would like to thank the following people for offering suggestions, +This HOWTO was originally written by A. M. Kuchling. The author would like to +thank the following people for offering suggestions, corrections and assistance with various drafts of this article: Ian Bicking, Nick Coghlan, Nick Efford, Raymond Hettinger, Jim Jewett, Mike Krell, Leandro Lameiro, Jussi Salmela, Collin Winter, Blake Winton. @@ -1239,9 +1237,9 @@ Text Processing". Mertz also wrote a 3-part series of articles on functional programming for IBM's DeveloperWorks site; see -`part 1 `__, -`part 2 `__, and -`part 3 `__, +`part 1 `__, +`part 2 `__, and +`part 3 `__. Python documentation diff --git a/Doc/howto/instrumentation.rst b/Doc/howto/instrumentation.rst index 06c1ae40da5e67e..8f0b0c41ea48617 100644 --- a/Doc/howto/instrumentation.rst +++ b/Doc/howto/instrumentation.rst @@ -6,9 +6,6 @@ Instrumenting CPython with DTrace and SystemTap =============================================== -:author: David Malcolm -:author: Łukasz Langa - DTrace and SystemTap are monitoring tools, each providing a way to inspect what the processes on a computer system are doing. They both use domain-specific languages allowing a user to write scripts which: diff --git a/Doc/howto/ipaddress.rst b/Doc/howto/ipaddress.rst index e852db98156facc..646c4c4d9e7ab14 100644 --- a/Doc/howto/ipaddress.rst +++ b/Doc/howto/ipaddress.rst @@ -8,9 +8,6 @@ An introduction to the ipaddress module *************************************** -:author: Peter Moody -:author: Nick Coghlan - .. topic:: Overview This document aims to provide a gentle introduction to the diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index 0ee4c0086dd98ce..87025814aafb9ab 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -4,8 +4,6 @@ Logging Cookbook ================ -:Author: Vinay Sajip - This page contains a number of recipes related to logging, which have been found useful in the past. For links to tutorial and reference information, please see :ref:`cookbook-ref-links`. diff --git a/Doc/howto/logging.rst b/Doc/howto/logging.rst index 454e2f4930e724d..c8ce0df9e937f8c 100644 --- a/Doc/howto/logging.rst +++ b/Doc/howto/logging.rst @@ -4,8 +4,6 @@ Logging HOWTO ============= -:Author: Vinay Sajip - .. _logging-basic-tutorial: .. currentmodule:: logging diff --git a/Doc/howto/perf_profiling.rst b/Doc/howto/perf_profiling.rst index 657cb287ad3d605..5565f99b244f11b 100644 --- a/Doc/howto/perf_profiling.rst +++ b/Doc/howto/perf_profiling.rst @@ -6,8 +6,6 @@ Python support for the ``perf map`` compatible profilers ======================================================== -:author: Pablo Galindo - `The Linux perf profiler `_ and `samply `_ are powerful tools that allow you to profile and obtain information about the performance of your application. diff --git a/Doc/howto/pyporting.rst b/Doc/howto/pyporting.rst index 9f73c811cfcbc0d..f19f6006dbac9e7 100644 --- a/Doc/howto/pyporting.rst +++ b/Doc/howto/pyporting.rst @@ -6,8 +6,6 @@ How to port Python 2 Code to Python 3 ************************************* -:author: Brett Cannon - Python 2 reached its official end-of-life at the start of 2020. This means that no new bug reports, fixes, or changes will be made to Python 2 - it's no longer supported: see :pep:`373` and diff --git a/Doc/howto/regex.rst b/Doc/howto/regex.rst index 6fc087c3f1c3673..ecdb35136a99f3b 100644 --- a/Doc/howto/regex.rst +++ b/Doc/howto/regex.rst @@ -4,8 +4,6 @@ Regular expression HOWTO **************************** -:Author: A.M. Kuchling - .. TODO: Document lookbehind assertions Better way of displaying a RE, a string, and what it matches diff --git a/Doc/howto/sockets.rst b/Doc/howto/sockets.rst index cbc49d15a0771b9..b17ab3f4391dad0 100644 --- a/Doc/howto/sockets.rst +++ b/Doc/howto/sockets.rst @@ -4,9 +4,6 @@ Socket Programming HOWTO **************************** -:Author: Gordon McMillan - - .. topic:: Abstract Sockets are used nearly everywhere, but are one of the most severely diff --git a/Doc/howto/sorting.rst b/Doc/howto/sorting.rst index 65e862e13e817a1..b511aa4e59fe1e7 100644 --- a/Doc/howto/sorting.rst +++ b/Doc/howto/sorting.rst @@ -3,9 +3,6 @@ Sorting Techniques ****************** -:Author: Andrew Dalke and Raymond Hettinger - - Python lists have a built-in :meth:`list.sort` method that modifies the list in-place. There is also a :func:`sorted` built-in function that builds a new sorted list from an iterable. diff --git a/Doc/howto/unicode.rst b/Doc/howto/unicode.rst index 243cc27bac7025d..cbd53d5b619f5a3 100644 --- a/Doc/howto/unicode.rst +++ b/Doc/howto/unicode.rst @@ -4,8 +4,6 @@ Unicode HOWTO ***************** -:Release: 1.12 - This HOWTO discusses Python's support for the Unicode specification for representing textual data, and explains various problems that people commonly encounter when trying to work with Unicode. diff --git a/Doc/howto/urllib2.rst b/Doc/howto/urllib2.rst index 4e77d2cb407f726..e4f218f088ba89d 100644 --- a/Doc/howto/urllib2.rst +++ b/Doc/howto/urllib2.rst @@ -4,9 +4,6 @@ HOWTO Fetch Internet Resources Using The urllib Package *********************************************************** -:Author: `Michael Foord `_ - - Introduction ============ From a406230c7402d47c57a7d8dfbe86d40a9402d9c6 Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Thu, 11 Jun 2026 05:06:48 -0400 Subject: [PATCH 116/185] gh-151177: Fix race condition in `_testembed` (GH-151293) --- Programs/_testembed.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Programs/_testembed.c b/Programs/_testembed.c index 7246cc06ffff036..74d0fe37ddaeadb 100644 --- a/Programs/_testembed.c +++ b/Programs/_testembed.c @@ -2715,8 +2715,8 @@ do_tstate_ensure(void *arg) PyThreadState_Release(tokens[2]); PyThreadState_Release(tokens[1]); PyThreadState_Release(tokens[0]); - PyInterpreterGuard_Close(guard); _Py_atomic_store_int(&data->done, 1); + PyInterpreterGuard_Close(guard); } static int From 8cb6dc6fe327154840ea446d6609bbe13a98e187 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Thu, 11 Jun 2026 10:16:27 +0100 Subject: [PATCH 117/185] gh-151112: Move an `assert` that may fail in `cfg_builder_check` (#151153) --- Python/flowgraph.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/flowgraph.c b/Python/flowgraph.c index 324a4fc1f10a68e..f135f243c74e2f6 100644 --- a/Python/flowgraph.c +++ b/Python/flowgraph.c @@ -404,7 +404,6 @@ cfg_builder_maybe_start_new_block(cfg_builder *g) static bool cfg_builder_check(cfg_builder *g) { - assert(g->g_entryblock->b_iused > 0); for (basicblock *block = g->g_block_list; block != NULL; block = block->b_list) { assert(!_PyMem_IsPtrFreed(block)); if (block->b_instr != NULL) { @@ -3762,6 +3761,7 @@ _PyCfg_OptimizeCodeUnit(cfg_builder *g, PyObject *consts, PyObject *const_cache, int nlocals, int nparams, int firstlineno) { assert(cfg_builder_check(g)); + assert(g->g_entryblock->b_iused > 0); /** Preprocessing **/ /* Map labels to targets and mark exception handlers */ RETURN_IF_ERROR(translate_jump_labels_to_targets(g->g_entryblock)); From 379deba2b2c116d964e7edbc8b985c85c824e717 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 11 Jun 2026 12:54:36 +0200 Subject: [PATCH 118/185] Remove unused internal _Py_REF_IS_QUEUED() function (#151236) --- Include/internal/pycore_object.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h index c2c508c1a71c5c0..dd7955c2770b997 100644 --- a/Include/internal/pycore_object.h +++ b/Include/internal/pycore_object.h @@ -266,12 +266,6 @@ _Py_REF_IS_MERGED(Py_ssize_t ob_ref_shared) return (ob_ref_shared & _Py_REF_SHARED_FLAG_MASK) == _Py_REF_MERGED; } -static inline int -_Py_REF_IS_QUEUED(Py_ssize_t ob_ref_shared) -{ - return (ob_ref_shared & _Py_REF_SHARED_FLAG_MASK) == _Py_REF_QUEUED; -} - // Merge the local and shared reference count fields and add `extra` to the // refcount when merging. Py_ssize_t _Py_ExplicitMergeRefcount(PyObject *op, Py_ssize_t extra); From 32debf11bccab45328e62d72c7d44573723aa3e7 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 11 Jun 2026 12:55:09 +0200 Subject: [PATCH 119/185] gh-151284: Fix test_capi on UBSan (#151286) Comment two checks relying on undefined behavior in test_fromwidechar() of test_capi. Enable test_capi in GitHub Action "Reusable Sanitizer". --- .github/workflows/reusable-san.yml | 3 --- Lib/test/test_capi/test_unicode.py | 6 ++++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/reusable-san.yml b/.github/workflows/reusable-san.yml index 6127c7e1c053690..2ceb1000e8a5a9c 100644 --- a/.github/workflows/reusable-san.yml +++ b/.github/workflows/reusable-san.yml @@ -82,13 +82,10 @@ jobs: run: make -j4 - name: Display build info run: make pythoninfo - # test_capi is skipped under UBSan because - # they raise signals that UBSan with halt_on_error=1 intercepts. - name: Tests run: >- ./python -m test ${{ inputs.sanitizer == 'TSan' && '--tsan' || '' }} - ${{ inputs.sanitizer == 'UBSan' && '-x test_capi' || '' }} -j4 -W - name: Parallel tests if: >- diff --git a/Lib/test/test_capi/test_unicode.py b/Lib/test/test_capi/test_unicode.py index 5dee25756fe2893..0dcd8a25ad0128d 100644 --- a/Lib/test/test_capi/test_unicode.py +++ b/Lib/test/test_capi/test_unicode.py @@ -842,9 +842,7 @@ def test_fromwidechar(self): if SIZEOF_WCHAR_T == 2: self.assertEqual(fromwidechar('a\U0001f600'.encode(encoding), 2), 'a\ud83d') - self.assertRaises(MemoryError, fromwidechar, b'', PY_SSIZE_T_MAX) self.assertRaises(SystemError, fromwidechar, b'\0'*SIZEOF_WCHAR_T, -2) - self.assertRaises(SystemError, fromwidechar, b'\0'*SIZEOF_WCHAR_T, PY_SSIZE_T_MIN) self.assertEqual(fromwidechar(NULL, 0), '') self.assertRaises(SystemError, fromwidechar, NULL, 1) self.assertRaises(SystemError, fromwidechar, NULL, PY_SSIZE_T_MAX) @@ -852,6 +850,10 @@ def test_fromwidechar(self): self.assertRaises(SystemError, fromwidechar, NULL, -2) self.assertRaises(SystemError, fromwidechar, NULL, PY_SSIZE_T_MIN) + # The following tests are skipped since they rely on undefined behavior + #self.assertRaises(MemoryError, fromwidechar, b'', PY_SSIZE_T_MAX) + #self.assertRaises(SystemError, fromwidechar, b'\0'*SIZEOF_WCHAR_T, PY_SSIZE_T_MIN) + @support.cpython_only @unittest.skipIf(_testlimitedcapi is None, 'need _testlimitedcapi module') def test_aswidechar(self): From 59a525dc911b3133688e076dcf4024a9fbcbb9d8 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 11 Jun 2026 12:55:13 +0200 Subject: [PATCH 120/185] gh-123619: Fix PyUnstable_Object_EnableDeferredRefcount() (#151260) Return 0 if the object is not tracked by the GC. --- Lib/test/test_capi/test_object.py | 6 ++++++ .../C_API/2026-06-10-16-43-37.gh-issue-123619.dV82r6.rst | 3 +++ Objects/object.c | 7 +++++++ 3 files changed, 16 insertions(+) create mode 100644 Misc/NEWS.d/next/C_API/2026-06-10-16-43-37.gh-issue-123619.dV82r6.rst diff --git a/Lib/test/test_capi/test_object.py b/Lib/test/test_capi/test_object.py index e5c50902a0118d4..433afac875aa7bf 100644 --- a/Lib/test/test_capi/test_object.py +++ b/Lib/test/test_capi/test_object.py @@ -178,11 +178,17 @@ class EnableDeferredRefcountingTest(unittest.TestCase): @support.requires_resource("cpu") def test_enable_deferred_refcount(self): from threading import Thread + import gc self.assertEqual(_testcapi.pyobject_enable_deferred_refcount("not tracked"), 0) foo = [] self.assertEqual(_testcapi.pyobject_enable_deferred_refcount(foo), int(support.Py_GIL_DISABLED)) + # The object must be tracked by the GC + not_gc_tracked = tuple([1, 2]) + self.assertFalse(gc.is_tracked(not_gc_tracked)) + self.assertEqual(_testcapi.pyobject_enable_deferred_refcount(not_gc_tracked), 0) + # Make sure reference counting works on foo now self.assertEqual(foo, []) if support.Py_GIL_DISABLED: diff --git a/Misc/NEWS.d/next/C_API/2026-06-10-16-43-37.gh-issue-123619.dV82r6.rst b/Misc/NEWS.d/next/C_API/2026-06-10-16-43-37.gh-issue-123619.dV82r6.rst new file mode 100644 index 000000000000000..4d4c94563330c06 --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2026-06-10-16-43-37.gh-issue-123619.dV82r6.rst @@ -0,0 +1,3 @@ +:c:func:`PyUnstable_Object_EnableDeferredRefcount` now returns ``0`` if the +object is not tracked by the garbage collector: if :func:`gc.is_tracked` is +false. Patch by Victor Stinner. diff --git a/Objects/object.c b/Objects/object.c index e0e26bb50d36537..bd23c2e23881949 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -2821,6 +2821,13 @@ PyUnstable_Object_EnableDeferredRefcount(PyObject *op) return 0; } + if (!PyObject_GC_IsTracked(op)) { + // When deferred refcount is enabled, the object will only be + // deallocated by the tracing garbage collector. So it must be tracked + // by the garbage collector. + return 0; + } + uint8_t bits = _Py_atomic_load_uint8(&op->ob_gc_bits); if ((bits & _PyGC_BITS_DEFERRED) != 0) { From 9dab3c9b5f1e0c9158a8a1185f3df20091279f8f Mon Sep 17 00:00:00 2001 From: Timofei <128279579+deadlovelll@users.noreply.github.com> Date: Thu, 11 Jun 2026 14:43:46 +0300 Subject: [PATCH 121/185] gh-151179: Fix pidfd leak in asyncio _PidfdChildWatcher (#151186) --- Lib/asyncio/unix_events.py | 4 +- Lib/test/test_asyncio/test_unix_events.py | 40 +++++++++++++++++++ ...-06-09-19-43-24.gh-issue-151179.hl_6Z0.rst | 3 ++ 3 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-06-09-19-43-24.gh-issue-151179.hl_6Z0.rst diff --git a/Lib/asyncio/unix_events.py b/Lib/asyncio/unix_events.py index 93c5802cd44460d..676e0b670faa49b 100644 --- a/Lib/asyncio/unix_events.py +++ b/Lib/asyncio/unix_events.py @@ -889,8 +889,8 @@ def _do_wait(self, pid, pidfd, callback, args): pid) else: returncode = waitstatus_to_exitcode(status) - - os.close(pidfd) + finally: + os.close(pidfd) callback(pid, returncode, *args) class _ThreadedChildWatcher: diff --git a/Lib/test/test_asyncio/test_unix_events.py b/Lib/test/test_asyncio/test_unix_events.py index d2b3de3b9a4cb61..118cb866997c51c 100644 --- a/Lib/test/test_asyncio/test_unix_events.py +++ b/Lib/test/test_asyncio/test_unix_events.py @@ -1333,5 +1333,45 @@ async def child_main(): self.assertEqual(result.value, 0) + +@unittest.skipUnless( + unix_events.can_use_pidfd(), + "operating system does not support pidfd", +) +class PidfdChildWatcherTests(test_utils.TestCase): + + def setUp(self): + super().setUp() + self.loop = asyncio.new_event_loop() + self.set_event_loop(self.loop) + + def test_pidfd_closed_when_waitpid_raises(self): + # _do_wait() must close the pidfd even when waitpid() + # fails with something other than ChildProcessError, otherwise the + # pidfd is leaked + self.loop.set_exception_handler(lambda loop, context: None) + + async def coro(): + before = os_helper.fd_count() + proc = await asyncio.create_subprocess_exec( + sys.executable, '-c', 'import sys; sys.stdin.read()', + stdin=asyncio.subprocess.PIPE + ) + + with mock.patch.object(os, 'waitpid', + side_effect=OSError('unexpected')) as m: + proc.stdin.close() + while not m.called: + await asyncio.sleep(0) + + os.waitpid(proc.pid, 0) + proc._transport._process_exited(0) + await proc.wait() + + self.assertEqual(os_helper.fd_count(), before) + + self.loop.run_until_complete(coro()) + + if __name__ == '__main__': unittest.main() diff --git a/Misc/NEWS.d/next/Library/2026-06-09-19-43-24.gh-issue-151179.hl_6Z0.rst b/Misc/NEWS.d/next/Library/2026-06-09-19-43-24.gh-issue-151179.hl_6Z0.rst new file mode 100644 index 000000000000000..904edf3a8c70908 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-09-19-43-24.gh-issue-151179.hl_6Z0.rst @@ -0,0 +1,3 @@ +Fix a pidfd leak in ``_PidfdChildWatcher`` on Linux: the watcher no +longer leaks the process file descriptor when ``waitpid()`` fails with an +error other than :exc:`ChildProcessError`. From 95102f641db7a236bfe6260d07f1eaea07f5e4cb Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 11 Jun 2026 15:23:19 +0300 Subject: [PATCH 122/185] gh-150285: Suppress showing the __getstate__() comment in the help for dataclasses (GH-151328) --- Lib/dataclasses.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py index 035678d902adaf9..bd317aa9b0f2f37 100644 --- a/Lib/dataclasses.py +++ b/Lib/dataclasses.py @@ -1250,6 +1250,7 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen, # classes with slots. These could be slightly more performant if we generated # the code instead of iterating over fields. But that can be a project for # another day, if performance becomes an issue. + def _dataclass_getstate(self): return [getattr(self, f.name) for f in fields(self)] From 10ffce92eefdc2b55dfb8bf47e18e60920742ab4 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Thu, 11 Jun 2026 15:26:46 +0100 Subject: [PATCH 123/185] gh-151238: Check for `_get_resized_exprs` failure in `_PyPegen_{joined,template}_str` (#151259) --- Lib/test/test_fstring.py | 11 +++++++++++ .../2026-06-10-15-19-58.gh-issue-151238.C9Wu4x.rst | 2 ++ Parser/action_helpers.c | 6 ++++++ 3 files changed, 19 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-06-10-15-19-58.gh-issue-151238.C9Wu4x.rst diff --git a/Lib/test/test_fstring.py b/Lib/test/test_fstring.py index 05d0cbd2445c4cb..5cc02c30ec2ba33 100644 --- a/Lib/test/test_fstring.py +++ b/Lib/test/test_fstring.py @@ -593,6 +593,17 @@ def test_compile_time_concat_errors(self): r"""b'' f''""", ]) + def test_concat_decode_failure_does_not_crash(self): + script = r''' +import builtins +builtins.__import__ = builtins # Breaks warning machinery so _get_resized_exprs returns NULL +try: + compile('"x"f"\]"b""', '', 'exec') +except Exception: + pass +''' + assert_python_ok('-c', script) + def test_literal(self): self.assertEqual(f'', '') self.assertEqual(f'a', 'a') diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-10-15-19-58.gh-issue-151238.C9Wu4x.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-10-15-19-58.gh-issue-151238.C9Wu4x.rst new file mode 100644 index 000000000000000..fe7519fb4878952 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-10-15-19-58.gh-issue-151238.C9Wu4x.rst @@ -0,0 +1,2 @@ +Fix a crash when compiling a concatenated f-string or t-string if an error +occurs when processing one of it's parts. diff --git a/Parser/action_helpers.c b/Parser/action_helpers.c index 5e52bb838719046..c74eb34deb7c35c 100644 --- a/Parser/action_helpers.c +++ b/Parser/action_helpers.c @@ -1416,6 +1416,9 @@ expr_ty _PyPegen_template_str(Parser *p, Token *a, asdl_expr_seq *raw_expressions, Token *b) { asdl_expr_seq *resized_exprs = _get_resized_exprs(p, a, raw_expressions, b, TSTRING); + if (resized_exprs == NULL) { + return NULL; + } return _PyAST_TemplateStr(resized_exprs, a->lineno, a->col_offset, b->end_lineno, b->end_col_offset, p->arena); @@ -1425,6 +1428,9 @@ expr_ty _PyPegen_joined_str(Parser *p, Token* a, asdl_expr_seq* raw_expressions, Token*b) { asdl_expr_seq *resized_exprs = _get_resized_exprs(p, a, raw_expressions, b, FSTRING); + if (resized_exprs == NULL) { + return NULL; + } return _PyAST_JoinedStr(resized_exprs, a->lineno, a->col_offset, b->end_lineno, b->end_col_offset, p->arena); From ffd6e2fc0044886d07066b5ee5f73b90d7eed324 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 11 Jun 2026 16:45:44 +0200 Subject: [PATCH 124/185] gh-150397: Fix pyexpat bigmem test (#151329) Add missing parameter (size). --- Lib/test/test_pyexpat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_pyexpat.py b/Lib/test/test_pyexpat.py index 060a509c1bd1c7a..42309dd8f6c1903 100644 --- a/Lib/test/test_pyexpat.py +++ b/Lib/test/test_pyexpat.py @@ -876,7 +876,7 @@ def test_change_size_2(self): @support.requires_resource('cpu') @support.requires_resource('walltime') @support.bigmemtest(size=2**31, memuse=4, dry_run=False) - def test_large_character_data_does_not_crash(self): + def test_large_character_data_does_not_crash(self, size): # See https://github.com/python/cpython/issues/148441 parser = expat.ParserCreate() parser.buffer_text = True From 3ed2f61ad2b0c0c637643e0812035ae1cee1d777 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Thu, 11 Jun 2026 15:52:11 +0100 Subject: [PATCH 125/185] gh-151112: Fix crash in `compiler_mod()` when entering the current compilation unit fails (#151234) --- Python/compile.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Python/compile.c b/Python/compile.c index eb9fc827bea40a8..e223ef42a42e22b 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -893,12 +893,15 @@ compiler_mod(compiler *c, mod_ty mod) { PyCodeObject *co = NULL; int addNone = mod->kind != Expression_kind; + assert(c->u == NULL); if (compiler_codegen(c, mod) < 0) { goto finally; } co = _PyCompile_OptimizeAndAssemble(c, addNone); finally: - _PyCompile_ExitScope(c); + if (c->u != NULL) { + _PyCompile_ExitScope(c); + } return co; } From 815b6acf4df5115e3b055edc825bef2eef271cfe Mon Sep 17 00:00:00 2001 From: Thomas Kowalski Date: Thu, 11 Jun 2026 17:07:36 +0200 Subject: [PATCH 126/185] gh-150858: fix data race while changing `__qualname__` of a type object(#150859) --- .../2026-06-03-16-27-00.gh-issue-150858.j2dSkD.rst | 1 + Objects/typeobject.c | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-06-03-16-27-00.gh-issue-150858.j2dSkD.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-03-16-27-00.gh-issue-150858.j2dSkD.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-03-16-27-00.gh-issue-150858.j2dSkD.rst new file mode 100644 index 000000000000000..dcb932bf320629a --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-03-16-27-00.gh-issue-150858.j2dSkD.rst @@ -0,0 +1 @@ +Fix a data race while changing ``__qualname__`` of a type concurrently on free-threaded builds. diff --git a/Objects/typeobject.c b/Objects/typeobject.c index e0464fe6475cfd2..de5bb5f1f2a0df0 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -1594,7 +1594,12 @@ type_set_qualname(PyObject *tp, PyObject *value, void *context) } et = (PyHeapTypeObject*)type; - Py_SETREF(et->ht_qualname, Py_NewRef(value)); + PyInterpreterState *interp = _PyInterpreterState_GET(); + _PyEval_StopTheWorld(interp); + PyObject *old_qualname = et->ht_qualname; + et->ht_qualname = Py_NewRef(value); + _PyEval_StartTheWorld(interp); + Py_DECREF(old_qualname); return 0; } From 546fecce1f2edf00fd7b2f9faf15646a355bb3c6 Mon Sep 17 00:00:00 2001 From: Maciej Olko Date: Thu, 11 Jun 2026 17:13:22 +0200 Subject: [PATCH 127/185] gh-139588: Fix nondeterministic `make latex` doc build under parallel make (#151343) --- Doc/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/Makefile b/Doc/Makefile index 60970d50833f147..d77ece1e681bcfc 100644 --- a/Doc/Makefile +++ b/Doc/Makefile @@ -88,9 +88,9 @@ htmlhelp: build "build/htmlhelp/pydoc.hhp project file." .PHONY: latex -latex: _ensure-sphinxcontrib-svg2pdfconverter latex: BUILDER = latex -latex: build +latex: _ensure-sphinxcontrib-svg2pdfconverter + $(MAKE) build BUILDER=$(BUILDER) @echo "Build finished; the LaTeX files are in build/latex." @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ "run these through (pdf)latex." From d2f87c0ef56f1830fe34dbcc5e1089bbc07190fa Mon Sep 17 00:00:00 2001 From: Cody Maloney Date: Thu, 11 Jun 2026 08:29:40 -0700 Subject: [PATCH 128/185] gh-101100: Fix Sphinx nitpick in `unittest.mock.rst` (#151302) --- Doc/library/unittest.mock.rst | 2 +- Doc/tools/.nitignore | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Doc/library/unittest.mock.rst b/Doc/library/unittest.mock.rst index 2ff1015af7a86e0..6ba1b87ea942a90 100644 --- a/Doc/library/unittest.mock.rst +++ b/Doc/library/unittest.mock.rst @@ -2223,7 +2223,7 @@ return something else:: >>> mock == 3 True -The return value of :meth:`MagicMock.__iter__` can be any iterable object and isn't +The return value of :meth:`!__iter__` can be any iterable object and isn't required to be an iterator: >>> mock = MagicMock() diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 31173134dd5019f..aa08f7acb99197a 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -29,7 +29,6 @@ Doc/library/test.rst Doc/library/tkinter.rst Doc/library/tkinter.scrolledtext.rst Doc/library/tkinter.ttk.rst -Doc/library/unittest.mock.rst Doc/library/urllib.parse.rst Doc/library/urllib.request.rst Doc/library/wsgiref.rst From 86df42558cd1c04177677f80cb55a30f2047314d Mon Sep 17 00:00:00 2001 From: sobolevn Date: Thu, 11 Jun 2026 18:48:08 +0300 Subject: [PATCH 129/185] gh-151126: Fix missing memory error in `os._path_splitroot` (#151339) --- .../next/Library/2026-06-11-16-25-38.gh-issue-151126.bh_Usy.rst | 2 ++ Modules/posixmodule.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2026-06-11-16-25-38.gh-issue-151126.bh_Usy.rst diff --git a/Misc/NEWS.d/next/Library/2026-06-11-16-25-38.gh-issue-151126.bh_Usy.rst b/Misc/NEWS.d/next/Library/2026-06-11-16-25-38.gh-issue-151126.bh_Usy.rst new file mode 100644 index 000000000000000..25149057aa7d092 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-11-16-25-38.gh-issue-151126.bh_Usy.rst @@ -0,0 +1,2 @@ +Fix a crash when :exc:`MemoryError` in :func:`!os._path_splitroot` +was not set properly. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index f9f53ca5cb5e49b..1f1b7fa729c01c8 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -5699,7 +5699,7 @@ os__path_splitroot_impl(PyObject *module, path_t *path) buffer = (wchar_t*)PyMem_Malloc(sizeof(wchar_t) * (wcslen(path->wide) + 1)); if (!buffer) { - return NULL; + return PyErr_NoMemory(); } wcscpy(buffer, path->wide); for (wchar_t *p = wcschr(buffer, L'/'); p; p = wcschr(p, L'/')) { From 3d3a448fabf91738b4dc2a39504cb3a1c8e5d350 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Thu, 11 Jun 2026 21:37:00 +0530 Subject: [PATCH 130/185] gh-151228: fix data race on clearing embedded dict values (#151330) --- Lib/test/test_free_threading/test_dict.py | 18 ++++++++++++++++++ Objects/dictobject.c | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_free_threading/test_dict.py b/Lib/test/test_free_threading/test_dict.py index dfe0634211d4b02..ad23290a92ab345 100644 --- a/Lib/test/test_free_threading/test_dict.py +++ b/Lib/test/test_free_threading/test_dict.py @@ -296,6 +296,24 @@ def reader(): threading_helper.run_concurrently([clearer, reader, reader]) + def test_racing_embedded_values_clear_and_lookup(self): + class C: + pass + + obj = C() + def writer(): + for _ in range(1000): + obj.x = 1 + obj.y = 2 + obj.z = 3 + obj.__dict__.clear() + + def reader(): + for _ in range(1000): + obj.__dict__.get('x') + + threading_helper.run_concurrently([writer, reader, reader]) + def test_racing_dict_update_and_method_lookup(self): # gh-144295: test race between dict modifications and method lookups. # Uses BytesIO because the race requires a type without Py_TPFLAGS_INLINE_VALUES diff --git a/Objects/dictobject.c b/Objects/dictobject.c index e279c8765dd464a..25dc81f726f59cb 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -3039,7 +3039,7 @@ clear_embedded_values(PyDictValues *values, Py_ssize_t nentries) assert(nentries <= SHARED_KEYS_MAX_SIZE); for (Py_ssize_t i = 0; i < nentries; i++) { refs[i] = values->values[i]; - values->values[i] = NULL; + FT_ATOMIC_STORE_PTR_RELEASE(values->values[i], NULL); } values->size = 0; for (Py_ssize_t i = 0; i < nentries; i++) { From d0d4e3500ab6f8b3a73d86b0eacd5893d58c1895 Mon Sep 17 00:00:00 2001 From: Dino Viehland Date: Thu, 11 Jun 2026 09:38:31 -0700 Subject: [PATCH 131/185] gh-150490: Raise PyType_Modified for insertion into split dictionary (#150489) Raise PyType_Modified for insertion into split dictionary --- Include/internal/pycore_dict.h | 13 + Include/internal/pycore_opcode_metadata.h | 4 +- Include/internal/pycore_uop_ids.h | 2391 ++++++++++----------- Include/internal/pycore_uop_metadata.h | 21 - Modules/_testinternalcapi/test_cases.c.h | 26 +- Objects/dictobject.c | 114 +- Objects/typeobject.c | 2 +- Python/bytecodes.c | 11 +- Python/executor_cases.c.h | 97 - Python/generated_cases.c.h | 26 +- Python/optimizer_cases.c.h | 4 - Python/specialize.c | 1 - 12 files changed, 1299 insertions(+), 1411 deletions(-) diff --git a/Include/internal/pycore_dict.h b/Include/internal/pycore_dict.h index ff6588b3e9718c6..9e11c0bb546b685 100644 --- a/Include/internal/pycore_dict.h +++ b/Include/internal/pycore_dict.h @@ -90,6 +90,8 @@ typedef struct { } PyDictUnicodeEntry; extern PyDictKeysObject *_PyDict_NewKeysForClass(PyHeapTypeObject *); +extern void _PyDict_RemoveKeysForClass(PyHeapTypeObject *); +extern void _PyDict_SplitKeysInvalidated(PyDictKeysObject* keys); extern PyObject *_PyDict_FromKeys(PyObject *, PyObject *, PyObject *); /* Implementations of the `|` and `|=` operators for dict, used by the @@ -239,6 +241,17 @@ struct _dictkeysobject { see the DK_ENTRIES() / DK_UNICODE_ENTRIES() functions below */ }; +struct _instancekeysobject { + PyTypeObject* dsk_owning_type; + struct _dictkeysobject dsk_keys; +}; + +static inline struct _instancekeysobject *_PyDictKeys_AsSharedKeys(struct _dictkeysobject *keys) +{ + assert(keys->dk_kind == DICT_KEYS_SPLIT); + return _Py_CONTAINER_OF(keys, struct _instancekeysobject, dsk_keys); +} + /* This must be no more than 250, for the prefix size to fit in one byte. */ #define SHARED_KEYS_MAX_SIZE 30 #define NEXT_LOG2_SHARED_KEYS_MAX_SIZE 6 diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index d2e29a1b95ede2f..80ad440bac8293c 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -1476,10 +1476,10 @@ _PyOpcode_macro_expansion[256] = { [LOAD_ATTR_INSTANCE_VALUE] = { .nuops = 6, .uops = { { _RECORD_TOS_TYPE, OPARG_SIMPLE, 1 }, { _GUARD_TYPE_VERSION, 2, 1 }, { _CHECK_MANAGED_OBJECT_HAS_VALUES, OPARG_SIMPLE, 3 }, { _LOAD_ATTR_INSTANCE_VALUE, 1, 3 }, { _POP_TOP, OPARG_SIMPLE, 4 }, { _PUSH_NULL_CONDITIONAL, OPARG_SIMPLE, 9 } } }, [LOAD_ATTR_METHOD_LAZY_DICT] = { .nuops = 4, .uops = { { _RECORD_TOS_TYPE, OPARG_SIMPLE, 1 }, { _GUARD_TYPE_VERSION, 2, 1 }, { _CHECK_ATTR_METHOD_LAZY_DICT, 1, 3 }, { _LOAD_ATTR_METHOD_LAZY_DICT, 4, 5 } } }, [LOAD_ATTR_METHOD_NO_DICT] = { .nuops = 3, .uops = { { _RECORD_TOS_TYPE, OPARG_SIMPLE, 1 }, { _GUARD_TYPE_VERSION, 2, 1 }, { _LOAD_ATTR_METHOD_NO_DICT, 4, 5 } } }, - [LOAD_ATTR_METHOD_WITH_VALUES] = { .nuops = 5, .uops = { { _RECORD_TOS_TYPE, OPARG_SIMPLE, 1 }, { _GUARD_TYPE_VERSION, 2, 1 }, { _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT, OPARG_SIMPLE, 3 }, { _GUARD_KEYS_VERSION, 2, 3 }, { _LOAD_ATTR_METHOD_WITH_VALUES, 4, 5 } } }, + [LOAD_ATTR_METHOD_WITH_VALUES] = { .nuops = 4, .uops = { { _RECORD_TOS_TYPE, OPARG_SIMPLE, 1 }, { _GUARD_TYPE_VERSION, 2, 1 }, { _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT, OPARG_SIMPLE, 3 }, { _LOAD_ATTR_METHOD_WITH_VALUES, 4, 5 } } }, [LOAD_ATTR_MODULE] = { .nuops = 4, .uops = { { _LOAD_ATTR_MODULE, 2, 1 }, { _LOAD_ATTR_MODULE, OPERAND1_1, 3 }, { _POP_TOP, OPARG_SIMPLE, 4 }, { _PUSH_NULL_CONDITIONAL, OPARG_SIMPLE, 9 } } }, [LOAD_ATTR_NONDESCRIPTOR_NO_DICT] = { .nuops = 3, .uops = { { _RECORD_TOS_TYPE, OPARG_SIMPLE, 1 }, { _GUARD_TYPE_VERSION, 2, 1 }, { _LOAD_ATTR_NONDESCRIPTOR_NO_DICT, 4, 5 } } }, - [LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES] = { .nuops = 5, .uops = { { _RECORD_TOS_TYPE, OPARG_SIMPLE, 1 }, { _GUARD_TYPE_VERSION, 2, 1 }, { _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT, OPARG_SIMPLE, 3 }, { _GUARD_KEYS_VERSION, 2, 3 }, { _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES, 4, 5 } } }, + [LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES] = { .nuops = 4, .uops = { { _RECORD_TOS_TYPE, OPARG_SIMPLE, 1 }, { _GUARD_TYPE_VERSION, 2, 1 }, { _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT, OPARG_SIMPLE, 3 }, { _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES, 4, 5 } } }, [LOAD_ATTR_PROPERTY] = { .nuops = 7, .uops = { { _RECORD_TOS_TYPE, OPARG_SIMPLE, 1 }, { _GUARD_TYPE_VERSION, 2, 1 }, { _CHECK_PEP_523, OPARG_SIMPLE, 3 }, { _LOAD_ATTR_PROPERTY_FRAME, 2, 3 }, { _LOAD_ATTR_PROPERTY_FRAME, OPERAND1_4, 5 }, { _SAVE_RETURN_OFFSET, OPARG_SAVE_RETURN_OFFSET, 9 }, { _PUSH_FRAME, OPARG_SIMPLE, 9 } } }, [LOAD_ATTR_SLOT] = { .nuops = 5, .uops = { { _RECORD_TOS_TYPE, OPARG_SIMPLE, 1 }, { _GUARD_TYPE_VERSION, 2, 1 }, { _LOAD_ATTR_SLOT, 1, 3 }, { _POP_TOP, OPARG_SIMPLE, 4 }, { _PUSH_NULL_CONDITIONAL, OPARG_SIMPLE, 9 } } }, [LOAD_ATTR_WITH_HINT] = { .nuops = 5, .uops = { { _RECORD_TOS_TYPE, OPARG_SIMPLE, 1 }, { _GUARD_TYPE_VERSION, 2, 1 }, { _LOAD_ATTR_WITH_HINT, 1, 3 }, { _POP_TOP, OPARG_SIMPLE, 4 }, { _PUSH_NULL_CONDITIONAL, OPARG_SIMPLE, 9 } } }, diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h index 7c44c0a430b7311..9d28c8590afcd7b 100644 --- a/Include/internal/pycore_uop_ids.h +++ b/Include/internal/pycore_uop_ids.h @@ -199,55 +199,54 @@ extern "C" { #define _GUARD_IS_TRUE_POP 460 #define _GUARD_ITERATOR 461 #define _GUARD_ITER_VIRTUAL 462 -#define _GUARD_KEYS_VERSION 463 -#define _GUARD_LOAD_SUPER_ATTR_METHOD 464 -#define _GUARD_NOS_COMPACT_ASCII 465 -#define _GUARD_NOS_DICT_STORE_SUBSCRIPT 466 -#define _GUARD_NOS_DICT_SUBSCRIPT 467 -#define _GUARD_NOS_FLOAT 468 -#define _GUARD_NOS_INT 469 -#define _GUARD_NOS_ITER_VIRTUAL 470 -#define _GUARD_NOS_LIST 471 -#define _GUARD_NOS_NOT_NULL 472 -#define _GUARD_NOS_NULL 473 -#define _GUARD_NOS_OVERFLOWED 474 -#define _GUARD_NOS_TUPLE 475 -#define _GUARD_NOS_TYPE_VERSION 476 -#define _GUARD_NOS_UNICODE 477 -#define _GUARD_NOT_EXHAUSTED_LIST 478 -#define _GUARD_NOT_EXHAUSTED_RANGE 479 -#define _GUARD_NOT_EXHAUSTED_TUPLE 480 -#define _GUARD_THIRD_NULL 481 -#define _GUARD_TOS_ANY_DICT 482 -#define _GUARD_TOS_ANY_SET 483 -#define _GUARD_TOS_DICT 484 -#define _GUARD_TOS_FLOAT 485 -#define _GUARD_TOS_FROZENDICT 486 -#define _GUARD_TOS_FROZENSET 487 -#define _GUARD_TOS_INT 488 -#define _GUARD_TOS_IS_NONE 489 -#define _GUARD_TOS_LIST 490 -#define _GUARD_TOS_NOT_NULL 491 -#define _GUARD_TOS_OVERFLOWED 492 -#define _GUARD_TOS_SET 493 -#define _GUARD_TOS_SLICE 494 -#define _GUARD_TOS_TUPLE 495 -#define _GUARD_TOS_UNICODE 496 -#define _GUARD_TYPE 497 -#define _GUARD_TYPE_ITER 498 -#define _GUARD_TYPE_VERSION 499 -#define _GUARD_TYPE_VERSION_LOCKED 500 -#define _HANDLE_PENDING_AND_DEOPT 501 +#define _GUARD_LOAD_SUPER_ATTR_METHOD 463 +#define _GUARD_NOS_COMPACT_ASCII 464 +#define _GUARD_NOS_DICT_STORE_SUBSCRIPT 465 +#define _GUARD_NOS_DICT_SUBSCRIPT 466 +#define _GUARD_NOS_FLOAT 467 +#define _GUARD_NOS_INT 468 +#define _GUARD_NOS_ITER_VIRTUAL 469 +#define _GUARD_NOS_LIST 470 +#define _GUARD_NOS_NOT_NULL 471 +#define _GUARD_NOS_NULL 472 +#define _GUARD_NOS_OVERFLOWED 473 +#define _GUARD_NOS_TUPLE 474 +#define _GUARD_NOS_TYPE_VERSION 475 +#define _GUARD_NOS_UNICODE 476 +#define _GUARD_NOT_EXHAUSTED_LIST 477 +#define _GUARD_NOT_EXHAUSTED_RANGE 478 +#define _GUARD_NOT_EXHAUSTED_TUPLE 479 +#define _GUARD_THIRD_NULL 480 +#define _GUARD_TOS_ANY_DICT 481 +#define _GUARD_TOS_ANY_SET 482 +#define _GUARD_TOS_DICT 483 +#define _GUARD_TOS_FLOAT 484 +#define _GUARD_TOS_FROZENDICT 485 +#define _GUARD_TOS_FROZENSET 486 +#define _GUARD_TOS_INT 487 +#define _GUARD_TOS_IS_NONE 488 +#define _GUARD_TOS_LIST 489 +#define _GUARD_TOS_NOT_NULL 490 +#define _GUARD_TOS_OVERFLOWED 491 +#define _GUARD_TOS_SET 492 +#define _GUARD_TOS_SLICE 493 +#define _GUARD_TOS_TUPLE 494 +#define _GUARD_TOS_UNICODE 495 +#define _GUARD_TYPE 496 +#define _GUARD_TYPE_ITER 497 +#define _GUARD_TYPE_VERSION 498 +#define _GUARD_TYPE_VERSION_LOCKED 499 +#define _HANDLE_PENDING_AND_DEOPT 500 #define _IMPORT_FROM IMPORT_FROM #define _IMPORT_NAME IMPORT_NAME -#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 502 -#define _INIT_CALL_PY_EXACT_ARGS 503 -#define _INIT_CALL_PY_EXACT_ARGS_0 504 -#define _INIT_CALL_PY_EXACT_ARGS_1 505 -#define _INIT_CALL_PY_EXACT_ARGS_2 506 -#define _INIT_CALL_PY_EXACT_ARGS_3 507 -#define _INIT_CALL_PY_EXACT_ARGS_4 508 -#define _INSERT_NULL 509 +#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 501 +#define _INIT_CALL_PY_EXACT_ARGS 502 +#define _INIT_CALL_PY_EXACT_ARGS_0 503 +#define _INIT_CALL_PY_EXACT_ARGS_1 504 +#define _INIT_CALL_PY_EXACT_ARGS_2 505 +#define _INIT_CALL_PY_EXACT_ARGS_3 506 +#define _INIT_CALL_PY_EXACT_ARGS_4 507 +#define _INSERT_NULL 508 #define _INSTRUMENTED_FOR_ITER INSTRUMENTED_FOR_ITER #define _INSTRUMENTED_INSTRUCTION INSTRUMENTED_INSTRUCTION #define _INSTRUMENTED_JUMP_FORWARD INSTRUMENTED_JUMP_FORWARD @@ -257,1194 +256,1190 @@ 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 _IS_NONE 510 -#define _IS_OP 511 -#define _ITER_CHECK_LIST 512 -#define _ITER_CHECK_RANGE 513 -#define _ITER_CHECK_TUPLE 514 -#define _ITER_JUMP_LIST 515 -#define _ITER_JUMP_RANGE 516 -#define _ITER_JUMP_TUPLE 517 -#define _ITER_NEXT_INLINE 518 -#define _ITER_NEXT_LIST 519 -#define _ITER_NEXT_LIST_TIER_TWO 520 -#define _ITER_NEXT_RANGE 521 -#define _ITER_NEXT_TUPLE 522 +#define _IS_NONE 509 +#define _IS_OP 510 +#define _ITER_CHECK_LIST 511 +#define _ITER_CHECK_RANGE 512 +#define _ITER_CHECK_TUPLE 513 +#define _ITER_JUMP_LIST 514 +#define _ITER_JUMP_RANGE 515 +#define _ITER_JUMP_TUPLE 516 +#define _ITER_NEXT_INLINE 517 +#define _ITER_NEXT_LIST 518 +#define _ITER_NEXT_LIST_TIER_TWO 519 +#define _ITER_NEXT_RANGE 520 +#define _ITER_NEXT_TUPLE 521 #define _JUMP_BACKWARD_NO_INTERRUPT JUMP_BACKWARD_NO_INTERRUPT -#define _JUMP_TO_TOP 523 +#define _JUMP_TO_TOP 522 #define _LIST_APPEND LIST_APPEND -#define _LIST_EXTEND 524 -#define _LOAD_ATTR 525 -#define _LOAD_ATTR_CLASS 526 -#define _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN_FRAME 527 -#define _LOAD_ATTR_INSTANCE_VALUE 528 -#define _LOAD_ATTR_METHOD_LAZY_DICT 529 -#define _LOAD_ATTR_METHOD_NO_DICT 530 -#define _LOAD_ATTR_METHOD_WITH_VALUES 531 -#define _LOAD_ATTR_MODULE 532 -#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT 533 -#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 534 -#define _LOAD_ATTR_PROPERTY_FRAME 535 -#define _LOAD_ATTR_SLOT 536 -#define _LOAD_ATTR_WITH_HINT 537 +#define _LIST_EXTEND 523 +#define _LOAD_ATTR 524 +#define _LOAD_ATTR_CLASS 525 +#define _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN_FRAME 526 +#define _LOAD_ATTR_INSTANCE_VALUE 527 +#define _LOAD_ATTR_METHOD_LAZY_DICT 528 +#define _LOAD_ATTR_METHOD_NO_DICT 529 +#define _LOAD_ATTR_METHOD_WITH_VALUES 530 +#define _LOAD_ATTR_MODULE 531 +#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT 532 +#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 533 +#define _LOAD_ATTR_PROPERTY_FRAME 534 +#define _LOAD_ATTR_SLOT 535 +#define _LOAD_ATTR_WITH_HINT 536 #define _LOAD_BUILD_CLASS LOAD_BUILD_CLASS -#define _LOAD_BYTECODE 538 +#define _LOAD_BYTECODE 537 #define _LOAD_COMMON_CONSTANT LOAD_COMMON_CONSTANT #define _LOAD_CONST LOAD_CONST -#define _LOAD_CONST_INLINE 539 -#define _LOAD_CONST_INLINE_BORROW 540 +#define _LOAD_CONST_INLINE 538 +#define _LOAD_CONST_INLINE_BORROW 539 #define _LOAD_DEREF LOAD_DEREF -#define _LOAD_FAST 541 -#define _LOAD_FAST_0 542 -#define _LOAD_FAST_1 543 -#define _LOAD_FAST_2 544 -#define _LOAD_FAST_3 545 -#define _LOAD_FAST_4 546 -#define _LOAD_FAST_5 547 -#define _LOAD_FAST_6 548 -#define _LOAD_FAST_7 549 +#define _LOAD_FAST 540 +#define _LOAD_FAST_0 541 +#define _LOAD_FAST_1 542 +#define _LOAD_FAST_2 543 +#define _LOAD_FAST_3 544 +#define _LOAD_FAST_4 545 +#define _LOAD_FAST_5 546 +#define _LOAD_FAST_6 547 +#define _LOAD_FAST_7 548 #define _LOAD_FAST_AND_CLEAR LOAD_FAST_AND_CLEAR -#define _LOAD_FAST_BORROW 550 -#define _LOAD_FAST_BORROW_0 551 -#define _LOAD_FAST_BORROW_1 552 -#define _LOAD_FAST_BORROW_2 553 -#define _LOAD_FAST_BORROW_3 554 -#define _LOAD_FAST_BORROW_4 555 -#define _LOAD_FAST_BORROW_5 556 -#define _LOAD_FAST_BORROW_6 557 -#define _LOAD_FAST_BORROW_7 558 +#define _LOAD_FAST_BORROW 549 +#define _LOAD_FAST_BORROW_0 550 +#define _LOAD_FAST_BORROW_1 551 +#define _LOAD_FAST_BORROW_2 552 +#define _LOAD_FAST_BORROW_3 553 +#define _LOAD_FAST_BORROW_4 554 +#define _LOAD_FAST_BORROW_5 555 +#define _LOAD_FAST_BORROW_6 556 +#define _LOAD_FAST_BORROW_7 557 #define _LOAD_FAST_CHECK LOAD_FAST_CHECK #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 559 -#define _LOAD_GLOBAL_BUILTINS 560 -#define _LOAD_GLOBAL_MODULE 561 +#define _LOAD_GLOBAL 558 +#define _LOAD_GLOBAL_BUILTINS 559 +#define _LOAD_GLOBAL_MODULE 560 #define _LOAD_LOCALS LOAD_LOCALS #define _LOAD_NAME LOAD_NAME -#define _LOAD_SMALL_INT 562 -#define _LOAD_SMALL_INT_0 563 -#define _LOAD_SMALL_INT_1 564 -#define _LOAD_SMALL_INT_2 565 -#define _LOAD_SMALL_INT_3 566 -#define _LOAD_SPECIAL 567 +#define _LOAD_SMALL_INT 561 +#define _LOAD_SMALL_INT_0 562 +#define _LOAD_SMALL_INT_1 563 +#define _LOAD_SMALL_INT_2 564 +#define _LOAD_SMALL_INT_3 565 +#define _LOAD_SPECIAL 566 #define _LOAD_SUPER_ATTR_ATTR LOAD_SUPER_ATTR_ATTR -#define _LOAD_SUPER_ATTR_METHOD 568 -#define _LOCK_OBJECT 569 -#define _MAKE_CALLARGS_A_TUPLE 570 +#define _LOAD_SUPER_ATTR_METHOD 567 +#define _LOCK_OBJECT 568 +#define _MAKE_CALLARGS_A_TUPLE 569 #define _MAKE_CELL MAKE_CELL -#define _MAKE_FUNCTION 571 -#define _MAKE_HEAP_SAFE 572 -#define _MAKE_WARM 573 +#define _MAKE_FUNCTION 570 +#define _MAKE_HEAP_SAFE 571 +#define _MAKE_WARM 572 #define _MAP_ADD MAP_ADD -#define _MATCH_CLASS 574 +#define _MATCH_CLASS 573 #define _MATCH_KEYS MATCH_KEYS #define _MATCH_MAPPING MATCH_MAPPING #define _MATCH_SEQUENCE MATCH_SEQUENCE -#define _MAYBE_EXPAND_METHOD 575 -#define _MAYBE_EXPAND_METHOD_KW 576 -#define _MONITOR_CALL 577 -#define _MONITOR_CALL_KW 578 -#define _MONITOR_JUMP_BACKWARD 579 -#define _MONITOR_RESUME 580 +#define _MAYBE_EXPAND_METHOD 574 +#define _MAYBE_EXPAND_METHOD_KW 575 +#define _MONITOR_CALL 576 +#define _MONITOR_CALL_KW 577 +#define _MONITOR_JUMP_BACKWARD 578 +#define _MONITOR_RESUME 579 #define _NOP NOP #define _POP_EXCEPT POP_EXCEPT #define _POP_ITER POP_ITER -#define _POP_JUMP_IF_FALSE 581 -#define _POP_JUMP_IF_TRUE 582 +#define _POP_JUMP_IF_FALSE 580 +#define _POP_JUMP_IF_TRUE 581 #define _POP_TOP POP_TOP -#define _POP_TOP_FLOAT 583 -#define _POP_TOP_INT 584 -#define _POP_TOP_NOP 585 -#define _POP_TOP_OPARG 586 -#define _POP_TOP_UNICODE 587 +#define _POP_TOP_FLOAT 582 +#define _POP_TOP_INT 583 +#define _POP_TOP_NOP 584 +#define _POP_TOP_OPARG 585 +#define _POP_TOP_UNICODE 586 #define _PUSH_EXC_INFO PUSH_EXC_INFO -#define _PUSH_FRAME 588 +#define _PUSH_FRAME 587 #define _PUSH_NULL PUSH_NULL -#define _PUSH_NULL_CONDITIONAL 589 -#define _PUSH_TAGGED_ZERO 590 -#define _PY_FRAME_EX 591 -#define _PY_FRAME_GENERAL 592 -#define _PY_FRAME_KW 593 -#define _RECORD_3OS_GEN_FUNC 594 -#define _RECORD_4OS 595 -#define _RECORD_BOUND_METHOD 596 -#define _RECORD_CALLABLE 597 -#define _RECORD_CALLABLE_KW 598 -#define _RECORD_CODE 599 -#define _RECORD_NOS 600 -#define _RECORD_NOS_GEN_FUNC 601 -#define _RECORD_NOS_TYPE 602 -#define _RECORD_TOS 603 -#define _RECORD_TOS_TYPE 604 -#define _REPLACE_WITH_TRUE 605 -#define _RESUME_CHECK 606 +#define _PUSH_NULL_CONDITIONAL 588 +#define _PUSH_TAGGED_ZERO 589 +#define _PY_FRAME_EX 590 +#define _PY_FRAME_GENERAL 591 +#define _PY_FRAME_KW 592 +#define _RECORD_3OS_GEN_FUNC 593 +#define _RECORD_4OS 594 +#define _RECORD_BOUND_METHOD 595 +#define _RECORD_CALLABLE 596 +#define _RECORD_CALLABLE_KW 597 +#define _RECORD_CODE 598 +#define _RECORD_NOS 599 +#define _RECORD_NOS_GEN_FUNC 600 +#define _RECORD_NOS_TYPE 601 +#define _RECORD_TOS 602 +#define _RECORD_TOS_TYPE 603 +#define _REPLACE_WITH_TRUE 604 +#define _RESUME_CHECK 605 #define _RETURN_GENERATOR RETURN_GENERATOR -#define _RETURN_VALUE 607 -#define _RROT_3 608 -#define _SAVE_RETURN_OFFSET 609 -#define _SEND_ASYNC_GEN 610 -#define _SEND_ASYNC_GEN_TIER_TWO 611 -#define _SEND_GEN_FRAME 612 -#define _SEND_VIRTUAL 613 -#define _SEND_VIRTUAL_TIER_TWO 614 +#define _RETURN_VALUE 606 +#define _RROT_3 607 +#define _SAVE_RETURN_OFFSET 608 +#define _SEND_ASYNC_GEN 609 +#define _SEND_ASYNC_GEN_TIER_TWO 610 +#define _SEND_GEN_FRAME 611 +#define _SEND_VIRTUAL 612 +#define _SEND_VIRTUAL_TIER_TWO 613 #define _SETUP_ANNOTATIONS SETUP_ANNOTATIONS #define _SET_ADD SET_ADD #define _SET_FUNCTION_ATTRIBUTE SET_FUNCTION_ATTRIBUTE -#define _SET_UPDATE 615 -#define _SPILL_OR_RELOAD 616 -#define _START_EXECUTOR 617 -#define _STORE_ATTR 618 -#define _STORE_ATTR_INSTANCE_VALUE 619 -#define _STORE_ATTR_SLOT 620 -#define _STORE_ATTR_WITH_HINT 621 +#define _SET_UPDATE 614 +#define _SPILL_OR_RELOAD 615 +#define _START_EXECUTOR 616 +#define _STORE_ATTR 617 +#define _STORE_ATTR_INSTANCE_VALUE 618 +#define _STORE_ATTR_SLOT 619 +#define _STORE_ATTR_WITH_HINT 620 #define _STORE_DEREF STORE_DEREF #define _STORE_GLOBAL STORE_GLOBAL #define _STORE_NAME STORE_NAME -#define _STORE_SLICE 622 -#define _STORE_SUBSCR 623 -#define _STORE_SUBSCR_DICT 624 -#define _STORE_SUBSCR_DICT_KNOWN_HASH 625 -#define _STORE_SUBSCR_LIST_INT 626 -#define _SWAP 627 -#define _SWAP_2 628 -#define _SWAP_3 629 -#define _SWAP_FAST 630 -#define _SWAP_FAST_0 631 -#define _SWAP_FAST_1 632 -#define _SWAP_FAST_2 633 -#define _SWAP_FAST_3 634 -#define _SWAP_FAST_4 635 -#define _SWAP_FAST_5 636 -#define _SWAP_FAST_6 637 -#define _SWAP_FAST_7 638 -#define _TIER2_RESUME_CHECK 639 -#define _TO_BOOL 640 +#define _STORE_SLICE 621 +#define _STORE_SUBSCR 622 +#define _STORE_SUBSCR_DICT 623 +#define _STORE_SUBSCR_DICT_KNOWN_HASH 624 +#define _STORE_SUBSCR_LIST_INT 625 +#define _SWAP 626 +#define _SWAP_2 627 +#define _SWAP_3 628 +#define _SWAP_FAST 629 +#define _SWAP_FAST_0 630 +#define _SWAP_FAST_1 631 +#define _SWAP_FAST_2 632 +#define _SWAP_FAST_3 633 +#define _SWAP_FAST_4 634 +#define _SWAP_FAST_5 635 +#define _SWAP_FAST_6 636 +#define _SWAP_FAST_7 637 +#define _TIER2_RESUME_CHECK 638 +#define _TO_BOOL 639 #define _TO_BOOL_BOOL TO_BOOL_BOOL -#define _TO_BOOL_INT 641 -#define _TO_BOOL_LIST 642 +#define _TO_BOOL_INT 640 +#define _TO_BOOL_LIST 641 #define _TO_BOOL_NONE TO_BOOL_NONE -#define _TO_BOOL_STR 643 +#define _TO_BOOL_STR 642 #define _TRACE_RECORD TRACE_RECORD -#define _UNARY_INVERT 644 -#define _UNARY_NEGATIVE 645 -#define _UNARY_NEGATIVE_FLOAT_INPLACE 646 +#define _UNARY_INVERT 643 +#define _UNARY_NEGATIVE 644 +#define _UNARY_NEGATIVE_FLOAT_INPLACE 645 #define _UNARY_NOT UNARY_NOT #define _UNPACK_EX UNPACK_EX -#define _UNPACK_SEQUENCE 647 -#define _UNPACK_SEQUENCE_LIST 648 -#define _UNPACK_SEQUENCE_TUPLE 649 -#define _UNPACK_SEQUENCE_TWO_TUPLE 650 -#define _UNPACK_SEQUENCE_UNIQUE_THREE_TUPLE 651 -#define _UNPACK_SEQUENCE_UNIQUE_TUPLE 652 -#define _UNPACK_SEQUENCE_UNIQUE_TWO_TUPLE 653 +#define _UNPACK_SEQUENCE 646 +#define _UNPACK_SEQUENCE_LIST 647 +#define _UNPACK_SEQUENCE_TUPLE 648 +#define _UNPACK_SEQUENCE_TWO_TUPLE 649 +#define _UNPACK_SEQUENCE_UNIQUE_THREE_TUPLE 650 +#define _UNPACK_SEQUENCE_UNIQUE_TUPLE 651 +#define _UNPACK_SEQUENCE_UNIQUE_TWO_TUPLE 652 #define _WITH_EXCEPT_START WITH_EXCEPT_START -#define _YIELD_VALUE 654 -#define MAX_UOP_ID 654 -#define _ALLOCATE_OBJECT_r00 655 -#define _BINARY_OP_r23 656 -#define _BINARY_OP_ADD_FLOAT_r03 657 -#define _BINARY_OP_ADD_FLOAT_r13 658 -#define _BINARY_OP_ADD_FLOAT_r23 659 -#define _BINARY_OP_ADD_FLOAT_INPLACE_r03 660 -#define _BINARY_OP_ADD_FLOAT_INPLACE_r13 661 -#define _BINARY_OP_ADD_FLOAT_INPLACE_r23 662 -#define _BINARY_OP_ADD_FLOAT_INPLACE_RIGHT_r03 663 -#define _BINARY_OP_ADD_FLOAT_INPLACE_RIGHT_r13 664 -#define _BINARY_OP_ADD_FLOAT_INPLACE_RIGHT_r23 665 -#define _BINARY_OP_ADD_INT_r03 666 -#define _BINARY_OP_ADD_INT_r13 667 -#define _BINARY_OP_ADD_INT_r23 668 -#define _BINARY_OP_ADD_INT_INPLACE_r03 669 -#define _BINARY_OP_ADD_INT_INPLACE_r13 670 -#define _BINARY_OP_ADD_INT_INPLACE_r23 671 -#define _BINARY_OP_ADD_INT_INPLACE_RIGHT_r03 672 -#define _BINARY_OP_ADD_INT_INPLACE_RIGHT_r13 673 -#define _BINARY_OP_ADD_INT_INPLACE_RIGHT_r23 674 -#define _BINARY_OP_ADD_UNICODE_r03 675 -#define _BINARY_OP_ADD_UNICODE_r13 676 -#define _BINARY_OP_ADD_UNICODE_r23 677 -#define _BINARY_OP_EXTEND_r23 678 -#define _BINARY_OP_INPLACE_ADD_UNICODE_r21 679 -#define _BINARY_OP_MULTIPLY_FLOAT_r03 680 -#define _BINARY_OP_MULTIPLY_FLOAT_r13 681 -#define _BINARY_OP_MULTIPLY_FLOAT_r23 682 -#define _BINARY_OP_MULTIPLY_FLOAT_INPLACE_r03 683 -#define _BINARY_OP_MULTIPLY_FLOAT_INPLACE_r13 684 -#define _BINARY_OP_MULTIPLY_FLOAT_INPLACE_r23 685 -#define _BINARY_OP_MULTIPLY_FLOAT_INPLACE_RIGHT_r03 686 -#define _BINARY_OP_MULTIPLY_FLOAT_INPLACE_RIGHT_r13 687 -#define _BINARY_OP_MULTIPLY_FLOAT_INPLACE_RIGHT_r23 688 -#define _BINARY_OP_MULTIPLY_INT_r03 689 -#define _BINARY_OP_MULTIPLY_INT_r13 690 -#define _BINARY_OP_MULTIPLY_INT_r23 691 -#define _BINARY_OP_MULTIPLY_INT_INPLACE_r03 692 -#define _BINARY_OP_MULTIPLY_INT_INPLACE_r13 693 -#define _BINARY_OP_MULTIPLY_INT_INPLACE_r23 694 -#define _BINARY_OP_MULTIPLY_INT_INPLACE_RIGHT_r03 695 -#define _BINARY_OP_MULTIPLY_INT_INPLACE_RIGHT_r13 696 -#define _BINARY_OP_MULTIPLY_INT_INPLACE_RIGHT_r23 697 -#define _BINARY_OP_SUBSCR_CHECK_FUNC_r23 698 -#define _BINARY_OP_SUBSCR_DICT_r23 699 -#define _BINARY_OP_SUBSCR_DICT_KNOWN_HASH_r23 700 -#define _BINARY_OP_SUBSCR_INIT_CALL_r01 701 -#define _BINARY_OP_SUBSCR_INIT_CALL_r11 702 -#define _BINARY_OP_SUBSCR_INIT_CALL_r21 703 -#define _BINARY_OP_SUBSCR_INIT_CALL_r31 704 -#define _BINARY_OP_SUBSCR_LIST_INT_r23 705 -#define _BINARY_OP_SUBSCR_LIST_SLICE_r23 706 -#define _BINARY_OP_SUBSCR_STR_INT_r23 707 -#define _BINARY_OP_SUBSCR_TUPLE_INT_r03 708 -#define _BINARY_OP_SUBSCR_TUPLE_INT_r13 709 -#define _BINARY_OP_SUBSCR_TUPLE_INT_r23 710 -#define _BINARY_OP_SUBSCR_USTR_INT_r23 711 -#define _BINARY_OP_SUBTRACT_FLOAT_r03 712 -#define _BINARY_OP_SUBTRACT_FLOAT_r13 713 -#define _BINARY_OP_SUBTRACT_FLOAT_r23 714 -#define _BINARY_OP_SUBTRACT_FLOAT_INPLACE_r03 715 -#define _BINARY_OP_SUBTRACT_FLOAT_INPLACE_r13 716 -#define _BINARY_OP_SUBTRACT_FLOAT_INPLACE_r23 717 -#define _BINARY_OP_SUBTRACT_FLOAT_INPLACE_RIGHT_r03 718 -#define _BINARY_OP_SUBTRACT_FLOAT_INPLACE_RIGHT_r13 719 -#define _BINARY_OP_SUBTRACT_FLOAT_INPLACE_RIGHT_r23 720 -#define _BINARY_OP_SUBTRACT_INT_r03 721 -#define _BINARY_OP_SUBTRACT_INT_r13 722 -#define _BINARY_OP_SUBTRACT_INT_r23 723 -#define _BINARY_OP_SUBTRACT_INT_INPLACE_r03 724 -#define _BINARY_OP_SUBTRACT_INT_INPLACE_r13 725 -#define _BINARY_OP_SUBTRACT_INT_INPLACE_r23 726 -#define _BINARY_OP_SUBTRACT_INT_INPLACE_RIGHT_r03 727 -#define _BINARY_OP_SUBTRACT_INT_INPLACE_RIGHT_r13 728 -#define _BINARY_OP_SUBTRACT_INT_INPLACE_RIGHT_r23 729 -#define _BINARY_OP_TRUEDIV_FLOAT_r23 730 -#define _BINARY_OP_TRUEDIV_FLOAT_INPLACE_r03 731 -#define _BINARY_OP_TRUEDIV_FLOAT_INPLACE_r13 732 -#define _BINARY_OP_TRUEDIV_FLOAT_INPLACE_r23 733 -#define _BINARY_OP_TRUEDIV_FLOAT_INPLACE_RIGHT_r03 734 -#define _BINARY_OP_TRUEDIV_FLOAT_INPLACE_RIGHT_r13 735 -#define _BINARY_OP_TRUEDIV_FLOAT_INPLACE_RIGHT_r23 736 -#define _BINARY_SLICE_r31 737 -#define _BUILD_INTERPOLATION_r01 738 -#define _BUILD_LIST_r01 739 -#define _BUILD_MAP_r01 740 -#define _BUILD_SET_r01 741 -#define _BUILD_SLICE_r01 742 -#define _BUILD_STRING_r01 743 -#define _BUILD_TEMPLATE_r21 744 -#define _BUILD_TUPLE_r01 745 -#define _CALL_BUILTIN_CLASS_r00 746 -#define _CALL_BUILTIN_FAST_r00 747 -#define _CALL_BUILTIN_FAST_WITH_KEYWORDS_r00 748 -#define _CALL_BUILTIN_O_r03 749 -#define _CALL_FUNCTION_EX_NON_PY_GENERAL_r31 750 -#define _CALL_INTRINSIC_1_r12 751 -#define _CALL_INTRINSIC_2_r23 752 -#define _CALL_ISINSTANCE_r31 753 -#define _CALL_KW_NON_PY_r11 754 -#define _CALL_LEN_r33 755 -#define _CALL_LIST_APPEND_r03 756 -#define _CALL_LIST_APPEND_r13 757 -#define _CALL_LIST_APPEND_r23 758 -#define _CALL_LIST_APPEND_r33 759 -#define _CALL_METHOD_DESCRIPTOR_FAST_r00 760 -#define _CALL_METHOD_DESCRIPTOR_FAST_INLINE_r00 761 -#define _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS_r00 762 -#define _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS_INLINE_r00 763 -#define _CALL_METHOD_DESCRIPTOR_NOARGS_r03 764 -#define _CALL_METHOD_DESCRIPTOR_NOARGS_INLINE_r03 765 -#define _CALL_METHOD_DESCRIPTOR_O_r03 766 -#define _CALL_METHOD_DESCRIPTOR_O_INLINE_r03 767 -#define _CALL_NON_PY_GENERAL_r01 768 -#define _CALL_STR_1_r32 769 -#define _CALL_TUPLE_1_r32 770 -#define _CALL_TYPE_1_r02 771 -#define _CALL_TYPE_1_r12 772 -#define _CALL_TYPE_1_r22 773 -#define _CALL_TYPE_1_r32 774 -#define _CHECK_ATTR_CLASS_r01 775 -#define _CHECK_ATTR_CLASS_r11 776 -#define _CHECK_ATTR_CLASS_r22 777 -#define _CHECK_ATTR_CLASS_r33 778 -#define _CHECK_ATTR_METHOD_LAZY_DICT_r01 779 -#define _CHECK_ATTR_METHOD_LAZY_DICT_r11 780 -#define _CHECK_ATTR_METHOD_LAZY_DICT_r22 781 -#define _CHECK_ATTR_METHOD_LAZY_DICT_r33 782 -#define _CHECK_CALL_BOUND_METHOD_EXACT_ARGS_r00 783 -#define _CHECK_EG_MATCH_r22 784 -#define _CHECK_EXC_MATCH_r22 785 -#define _CHECK_FUNCTION_EXACT_ARGS_r00 786 -#define _CHECK_FUNCTION_VERSION_r00 787 -#define _CHECK_FUNCTION_VERSION_INLINE_r00 788 -#define _CHECK_FUNCTION_VERSION_INLINE_r11 789 -#define _CHECK_FUNCTION_VERSION_INLINE_r22 790 -#define _CHECK_FUNCTION_VERSION_INLINE_r33 791 -#define _CHECK_FUNCTION_VERSION_KW_r11 792 -#define _CHECK_IS_NOT_PY_CALLABLE_r00 793 -#define _CHECK_IS_NOT_PY_CALLABLE_EX_r03 794 -#define _CHECK_IS_NOT_PY_CALLABLE_EX_r13 795 -#define _CHECK_IS_NOT_PY_CALLABLE_EX_r23 796 -#define _CHECK_IS_NOT_PY_CALLABLE_EX_r33 797 -#define _CHECK_IS_NOT_PY_CALLABLE_KW_r11 798 -#define _CHECK_IS_PY_CALLABLE_EX_r03 799 -#define _CHECK_IS_PY_CALLABLE_EX_r13 800 -#define _CHECK_IS_PY_CALLABLE_EX_r23 801 -#define _CHECK_IS_PY_CALLABLE_EX_r33 802 -#define _CHECK_MANAGED_OBJECT_HAS_VALUES_r01 803 -#define _CHECK_MANAGED_OBJECT_HAS_VALUES_r11 804 -#define _CHECK_MANAGED_OBJECT_HAS_VALUES_r22 805 -#define _CHECK_MANAGED_OBJECT_HAS_VALUES_r33 806 -#define _CHECK_METHOD_VERSION_r00 807 -#define _CHECK_METHOD_VERSION_KW_r11 808 -#define _CHECK_OBJECT_r00 809 -#define _CHECK_PEP_523_r00 810 -#define _CHECK_PEP_523_r11 811 -#define _CHECK_PEP_523_r22 812 -#define _CHECK_PEP_523_r33 813 -#define _CHECK_PERIODIC_r00 814 -#define _CHECK_PERIODIC_AT_END_r00 815 -#define _CHECK_PERIODIC_IF_NOT_YIELD_FROM_r00 816 -#define _CHECK_RECURSION_LIMIT_r00 817 -#define _CHECK_RECURSION_LIMIT_r11 818 -#define _CHECK_RECURSION_LIMIT_r22 819 -#define _CHECK_RECURSION_LIMIT_r33 820 -#define _CHECK_RECURSION_REMAINING_r00 821 -#define _CHECK_RECURSION_REMAINING_r11 822 -#define _CHECK_RECURSION_REMAINING_r22 823 -#define _CHECK_RECURSION_REMAINING_r33 824 -#define _CHECK_STACK_SPACE_r00 825 -#define _CHECK_STACK_SPACE_OPERAND_r00 826 -#define _CHECK_STACK_SPACE_OPERAND_r11 827 -#define _CHECK_STACK_SPACE_OPERAND_r22 828 -#define _CHECK_STACK_SPACE_OPERAND_r33 829 -#define _CHECK_VALIDITY_r00 830 -#define _CHECK_VALIDITY_r11 831 -#define _CHECK_VALIDITY_r22 832 -#define _CHECK_VALIDITY_r33 833 -#define _COLD_DYNAMIC_EXIT_r00 834 -#define _COLD_EXIT_r00 835 -#define _COMPARE_OP_r21 836 -#define _COMPARE_OP_FLOAT_r03 837 -#define _COMPARE_OP_FLOAT_r13 838 -#define _COMPARE_OP_FLOAT_r23 839 -#define _COMPARE_OP_INT_r23 840 -#define _COMPARE_OP_STR_r23 841 -#define _CONTAINS_OP_r23 842 -#define _CONTAINS_OP_DICT_r23 843 -#define _CONTAINS_OP_SET_r23 844 -#define _CONVERT_VALUE_r11 845 -#define _COPY_r01 846 -#define _COPY_1_r02 847 -#define _COPY_1_r12 848 -#define _COPY_1_r23 849 -#define _COPY_2_r03 850 -#define _COPY_2_r13 851 -#define _COPY_2_r23 852 -#define _COPY_3_r03 853 -#define _COPY_3_r13 854 -#define _COPY_3_r23 855 -#define _COPY_3_r33 856 -#define _COPY_FREE_VARS_r00 857 -#define _COPY_FREE_VARS_r11 858 -#define _COPY_FREE_VARS_r22 859 -#define _COPY_FREE_VARS_r33 860 -#define _CREATE_INIT_FRAME_r01 861 -#define _DELETE_ATTR_r10 862 -#define _DELETE_DEREF_r00 863 -#define _DELETE_FAST_r00 864 -#define _DELETE_GLOBAL_r00 865 -#define _DELETE_NAME_r00 866 -#define _DELETE_SUBSCR_r20 867 -#define _DEOPT_r00 868 -#define _DEOPT_r10 869 -#define _DEOPT_r20 870 -#define _DEOPT_r30 871 -#define _DICT_MERGE_r11 872 -#define _DICT_UPDATE_r11 873 -#define _DO_CALL_r01 874 -#define _DO_CALL_FUNCTION_EX_r31 875 -#define _DO_CALL_KW_r11 876 -#define _DYNAMIC_EXIT_r00 877 -#define _DYNAMIC_EXIT_r10 878 -#define _DYNAMIC_EXIT_r20 879 -#define _DYNAMIC_EXIT_r30 880 -#define _END_FOR_r10 881 -#define _END_SEND_r31 882 -#define _ERROR_POP_N_r00 883 -#define _EXIT_INIT_CHECK_r10 884 -#define _EXIT_TRACE_r00 885 -#define _EXIT_TRACE_r10 886 -#define _EXIT_TRACE_r20 887 -#define _EXIT_TRACE_r30 888 -#define _EXPAND_METHOD_r00 889 -#define _EXPAND_METHOD_KW_r11 890 -#define _FATAL_ERROR_r00 891 -#define _FATAL_ERROR_r11 892 -#define _FATAL_ERROR_r22 893 -#define _FATAL_ERROR_r33 894 -#define _FORMAT_SIMPLE_r11 895 -#define _FORMAT_WITH_SPEC_r21 896 -#define _FOR_ITER_r23 897 -#define _FOR_ITER_GEN_FRAME_r03 898 -#define _FOR_ITER_GEN_FRAME_r13 899 -#define _FOR_ITER_GEN_FRAME_r23 900 -#define _FOR_ITER_TIER_TWO_r23 901 -#define _FOR_ITER_VIRTUAL_r23 902 -#define _FOR_ITER_VIRTUAL_TIER_TWO_r23 903 -#define _GET_AITER_r11 904 -#define _GET_ANEXT_r12 905 -#define _GET_AWAITABLE_r11 906 -#define _GET_ITER_r12 907 -#define _GET_ITER_TRAD_r12 908 -#define _GET_LEN_r12 909 -#define _GUARD_3OS_ASYNC_GEN_ASEND_r03 910 -#define _GUARD_3OS_ASYNC_GEN_ASEND_r13 911 -#define _GUARD_3OS_ASYNC_GEN_ASEND_r23 912 -#define _GUARD_3OS_ASYNC_GEN_ASEND_r33 913 -#define _GUARD_BINARY_OP_EXTEND_r22 914 -#define _GUARD_BINARY_OP_EXTEND_LHS_r02 915 -#define _GUARD_BINARY_OP_EXTEND_LHS_r12 916 -#define _GUARD_BINARY_OP_EXTEND_LHS_r22 917 -#define _GUARD_BINARY_OP_EXTEND_LHS_r33 918 -#define _GUARD_BINARY_OP_EXTEND_RHS_r02 919 -#define _GUARD_BINARY_OP_EXTEND_RHS_r12 920 -#define _GUARD_BINARY_OP_EXTEND_RHS_r22 921 -#define _GUARD_BINARY_OP_EXTEND_RHS_r33 922 -#define _GUARD_BINARY_OP_SUBSCR_TUPLE_INT_BOUNDS_r02 923 -#define _GUARD_BINARY_OP_SUBSCR_TUPLE_INT_BOUNDS_r12 924 -#define _GUARD_BINARY_OP_SUBSCR_TUPLE_INT_BOUNDS_r22 925 -#define _GUARD_BINARY_OP_SUBSCR_TUPLE_INT_BOUNDS_r33 926 -#define _GUARD_BIT_IS_SET_POP_r00 927 -#define _GUARD_BIT_IS_SET_POP_r10 928 -#define _GUARD_BIT_IS_SET_POP_r21 929 -#define _GUARD_BIT_IS_SET_POP_r32 930 -#define _GUARD_BIT_IS_SET_POP_4_r00 931 -#define _GUARD_BIT_IS_SET_POP_4_r10 932 -#define _GUARD_BIT_IS_SET_POP_4_r21 933 -#define _GUARD_BIT_IS_SET_POP_4_r32 934 -#define _GUARD_BIT_IS_SET_POP_5_r00 935 -#define _GUARD_BIT_IS_SET_POP_5_r10 936 -#define _GUARD_BIT_IS_SET_POP_5_r21 937 -#define _GUARD_BIT_IS_SET_POP_5_r32 938 -#define _GUARD_BIT_IS_SET_POP_6_r00 939 -#define _GUARD_BIT_IS_SET_POP_6_r10 940 -#define _GUARD_BIT_IS_SET_POP_6_r21 941 -#define _GUARD_BIT_IS_SET_POP_6_r32 942 -#define _GUARD_BIT_IS_SET_POP_7_r00 943 -#define _GUARD_BIT_IS_SET_POP_7_r10 944 -#define _GUARD_BIT_IS_SET_POP_7_r21 945 -#define _GUARD_BIT_IS_SET_POP_7_r32 946 -#define _GUARD_BIT_IS_UNSET_POP_r00 947 -#define _GUARD_BIT_IS_UNSET_POP_r10 948 -#define _GUARD_BIT_IS_UNSET_POP_r21 949 -#define _GUARD_BIT_IS_UNSET_POP_r32 950 -#define _GUARD_BIT_IS_UNSET_POP_4_r00 951 -#define _GUARD_BIT_IS_UNSET_POP_4_r10 952 -#define _GUARD_BIT_IS_UNSET_POP_4_r21 953 -#define _GUARD_BIT_IS_UNSET_POP_4_r32 954 -#define _GUARD_BIT_IS_UNSET_POP_5_r00 955 -#define _GUARD_BIT_IS_UNSET_POP_5_r10 956 -#define _GUARD_BIT_IS_UNSET_POP_5_r21 957 -#define _GUARD_BIT_IS_UNSET_POP_5_r32 958 -#define _GUARD_BIT_IS_UNSET_POP_6_r00 959 -#define _GUARD_BIT_IS_UNSET_POP_6_r10 960 -#define _GUARD_BIT_IS_UNSET_POP_6_r21 961 -#define _GUARD_BIT_IS_UNSET_POP_6_r32 962 -#define _GUARD_BIT_IS_UNSET_POP_7_r00 963 -#define _GUARD_BIT_IS_UNSET_POP_7_r10 964 -#define _GUARD_BIT_IS_UNSET_POP_7_r21 965 -#define _GUARD_BIT_IS_UNSET_POP_7_r32 966 -#define _GUARD_CALLABLE_BUILTIN_CLASS_r00 967 -#define _GUARD_CALLABLE_BUILTIN_FAST_r00 968 -#define _GUARD_CALLABLE_BUILTIN_FAST_WITH_KEYWORDS_r00 969 -#define _GUARD_CALLABLE_BUILTIN_O_r00 970 -#define _GUARD_CALLABLE_ISINSTANCE_r03 971 -#define _GUARD_CALLABLE_ISINSTANCE_r13 972 -#define _GUARD_CALLABLE_ISINSTANCE_r23 973 -#define _GUARD_CALLABLE_ISINSTANCE_r33 974 -#define _GUARD_CALLABLE_LEN_r03 975 -#define _GUARD_CALLABLE_LEN_r13 976 -#define _GUARD_CALLABLE_LEN_r23 977 -#define _GUARD_CALLABLE_LEN_r33 978 -#define _GUARD_CALLABLE_LIST_APPEND_r03 979 -#define _GUARD_CALLABLE_LIST_APPEND_r13 980 -#define _GUARD_CALLABLE_LIST_APPEND_r23 981 -#define _GUARD_CALLABLE_LIST_APPEND_r33 982 -#define _GUARD_CALLABLE_METHOD_DESCRIPTOR_FAST_r00 983 -#define _GUARD_CALLABLE_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS_r00 984 -#define _GUARD_CALLABLE_METHOD_DESCRIPTOR_NOARGS_r00 985 -#define _GUARD_CALLABLE_METHOD_DESCRIPTOR_O_r00 986 -#define _GUARD_CALLABLE_STR_1_r03 987 -#define _GUARD_CALLABLE_STR_1_r13 988 -#define _GUARD_CALLABLE_STR_1_r23 989 -#define _GUARD_CALLABLE_STR_1_r33 990 -#define _GUARD_CALLABLE_TUPLE_1_r03 991 -#define _GUARD_CALLABLE_TUPLE_1_r13 992 -#define _GUARD_CALLABLE_TUPLE_1_r23 993 -#define _GUARD_CALLABLE_TUPLE_1_r33 994 -#define _GUARD_CALLABLE_TYPE_1_r03 995 -#define _GUARD_CALLABLE_TYPE_1_r13 996 -#define _GUARD_CALLABLE_TYPE_1_r23 997 -#define _GUARD_CALLABLE_TYPE_1_r33 998 -#define _GUARD_CODE_VERSION_RETURN_GENERATOR_r00 999 -#define _GUARD_CODE_VERSION_RETURN_GENERATOR_r11 1000 -#define _GUARD_CODE_VERSION_RETURN_GENERATOR_r22 1001 -#define _GUARD_CODE_VERSION_RETURN_GENERATOR_r33 1002 -#define _GUARD_CODE_VERSION_RETURN_VALUE_r00 1003 -#define _GUARD_CODE_VERSION_RETURN_VALUE_r11 1004 -#define _GUARD_CODE_VERSION_RETURN_VALUE_r22 1005 -#define _GUARD_CODE_VERSION_RETURN_VALUE_r33 1006 -#define _GUARD_CODE_VERSION_YIELD_VALUE_r00 1007 -#define _GUARD_CODE_VERSION_YIELD_VALUE_r11 1008 -#define _GUARD_CODE_VERSION_YIELD_VALUE_r22 1009 -#define _GUARD_CODE_VERSION_YIELD_VALUE_r33 1010 -#define _GUARD_CODE_VERSION__PUSH_FRAME_r00 1011 -#define _GUARD_CODE_VERSION__PUSH_FRAME_r11 1012 -#define _GUARD_CODE_VERSION__PUSH_FRAME_r22 1013 -#define _GUARD_CODE_VERSION__PUSH_FRAME_r33 1014 -#define _GUARD_DORV_NO_DICT_r01 1015 -#define _GUARD_DORV_NO_DICT_r11 1016 -#define _GUARD_DORV_NO_DICT_r22 1017 -#define _GUARD_DORV_NO_DICT_r33 1018 -#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_r01 1019 -#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_r11 1020 -#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_r22 1021 -#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_r33 1022 -#define _GUARD_GLOBALS_VERSION_r00 1023 -#define _GUARD_GLOBALS_VERSION_r11 1024 -#define _GUARD_GLOBALS_VERSION_r22 1025 -#define _GUARD_GLOBALS_VERSION_r33 1026 -#define _GUARD_IP_RETURN_GENERATOR_r00 1027 -#define _GUARD_IP_RETURN_GENERATOR_r11 1028 -#define _GUARD_IP_RETURN_GENERATOR_r22 1029 -#define _GUARD_IP_RETURN_GENERATOR_r33 1030 -#define _GUARD_IP_RETURN_VALUE_r00 1031 -#define _GUARD_IP_RETURN_VALUE_r11 1032 -#define _GUARD_IP_RETURN_VALUE_r22 1033 -#define _GUARD_IP_RETURN_VALUE_r33 1034 -#define _GUARD_IP_YIELD_VALUE_r00 1035 -#define _GUARD_IP_YIELD_VALUE_r11 1036 -#define _GUARD_IP_YIELD_VALUE_r22 1037 -#define _GUARD_IP_YIELD_VALUE_r33 1038 -#define _GUARD_IP__PUSH_FRAME_r00 1039 -#define _GUARD_IP__PUSH_FRAME_r11 1040 -#define _GUARD_IP__PUSH_FRAME_r22 1041 -#define _GUARD_IP__PUSH_FRAME_r33 1042 -#define _GUARD_IS_FALSE_POP_r00 1043 -#define _GUARD_IS_FALSE_POP_r10 1044 -#define _GUARD_IS_FALSE_POP_r21 1045 -#define _GUARD_IS_FALSE_POP_r32 1046 -#define _GUARD_IS_NONE_POP_r00 1047 -#define _GUARD_IS_NONE_POP_r10 1048 -#define _GUARD_IS_NONE_POP_r21 1049 -#define _GUARD_IS_NONE_POP_r32 1050 -#define _GUARD_IS_NOT_NONE_POP_r10 1051 -#define _GUARD_IS_TRUE_POP_r00 1052 -#define _GUARD_IS_TRUE_POP_r10 1053 -#define _GUARD_IS_TRUE_POP_r21 1054 -#define _GUARD_IS_TRUE_POP_r32 1055 -#define _GUARD_ITERATOR_r01 1056 -#define _GUARD_ITERATOR_r11 1057 -#define _GUARD_ITERATOR_r22 1058 -#define _GUARD_ITERATOR_r33 1059 -#define _GUARD_ITER_VIRTUAL_r01 1060 -#define _GUARD_ITER_VIRTUAL_r11 1061 -#define _GUARD_ITER_VIRTUAL_r22 1062 -#define _GUARD_ITER_VIRTUAL_r33 1063 -#define _GUARD_KEYS_VERSION_r01 1064 -#define _GUARD_KEYS_VERSION_r11 1065 -#define _GUARD_KEYS_VERSION_r22 1066 -#define _GUARD_KEYS_VERSION_r33 1067 -#define _GUARD_LOAD_SUPER_ATTR_METHOD_r03 1068 -#define _GUARD_LOAD_SUPER_ATTR_METHOD_r13 1069 -#define _GUARD_LOAD_SUPER_ATTR_METHOD_r23 1070 -#define _GUARD_LOAD_SUPER_ATTR_METHOD_r33 1071 -#define _GUARD_NOS_COMPACT_ASCII_r02 1072 -#define _GUARD_NOS_COMPACT_ASCII_r12 1073 -#define _GUARD_NOS_COMPACT_ASCII_r22 1074 -#define _GUARD_NOS_COMPACT_ASCII_r33 1075 -#define _GUARD_NOS_DICT_STORE_SUBSCRIPT_r03 1076 -#define _GUARD_NOS_DICT_STORE_SUBSCRIPT_r13 1077 -#define _GUARD_NOS_DICT_STORE_SUBSCRIPT_r23 1078 -#define _GUARD_NOS_DICT_STORE_SUBSCRIPT_r33 1079 -#define _GUARD_NOS_DICT_SUBSCRIPT_r02 1080 -#define _GUARD_NOS_DICT_SUBSCRIPT_r12 1081 -#define _GUARD_NOS_DICT_SUBSCRIPT_r22 1082 -#define _GUARD_NOS_DICT_SUBSCRIPT_r33 1083 -#define _GUARD_NOS_FLOAT_r02 1084 -#define _GUARD_NOS_FLOAT_r12 1085 -#define _GUARD_NOS_FLOAT_r22 1086 -#define _GUARD_NOS_FLOAT_r33 1087 -#define _GUARD_NOS_INT_r02 1088 -#define _GUARD_NOS_INT_r12 1089 -#define _GUARD_NOS_INT_r22 1090 -#define _GUARD_NOS_INT_r33 1091 -#define _GUARD_NOS_ITER_VIRTUAL_r02 1092 -#define _GUARD_NOS_ITER_VIRTUAL_r12 1093 -#define _GUARD_NOS_ITER_VIRTUAL_r22 1094 -#define _GUARD_NOS_ITER_VIRTUAL_r33 1095 -#define _GUARD_NOS_LIST_r02 1096 -#define _GUARD_NOS_LIST_r12 1097 -#define _GUARD_NOS_LIST_r22 1098 -#define _GUARD_NOS_LIST_r33 1099 -#define _GUARD_NOS_NOT_NULL_r02 1100 -#define _GUARD_NOS_NOT_NULL_r12 1101 -#define _GUARD_NOS_NOT_NULL_r22 1102 -#define _GUARD_NOS_NOT_NULL_r33 1103 -#define _GUARD_NOS_NULL_r02 1104 -#define _GUARD_NOS_NULL_r12 1105 -#define _GUARD_NOS_NULL_r22 1106 -#define _GUARD_NOS_NULL_r33 1107 -#define _GUARD_NOS_OVERFLOWED_r02 1108 -#define _GUARD_NOS_OVERFLOWED_r12 1109 -#define _GUARD_NOS_OVERFLOWED_r22 1110 -#define _GUARD_NOS_OVERFLOWED_r33 1111 -#define _GUARD_NOS_TUPLE_r02 1112 -#define _GUARD_NOS_TUPLE_r12 1113 -#define _GUARD_NOS_TUPLE_r22 1114 -#define _GUARD_NOS_TUPLE_r33 1115 -#define _GUARD_NOS_TYPE_VERSION_r02 1116 -#define _GUARD_NOS_TYPE_VERSION_r12 1117 -#define _GUARD_NOS_TYPE_VERSION_r22 1118 -#define _GUARD_NOS_TYPE_VERSION_r33 1119 -#define _GUARD_NOS_UNICODE_r02 1120 -#define _GUARD_NOS_UNICODE_r12 1121 -#define _GUARD_NOS_UNICODE_r22 1122 -#define _GUARD_NOS_UNICODE_r33 1123 -#define _GUARD_NOT_EXHAUSTED_LIST_r02 1124 -#define _GUARD_NOT_EXHAUSTED_LIST_r12 1125 -#define _GUARD_NOT_EXHAUSTED_LIST_r22 1126 -#define _GUARD_NOT_EXHAUSTED_LIST_r33 1127 -#define _GUARD_NOT_EXHAUSTED_RANGE_r02 1128 -#define _GUARD_NOT_EXHAUSTED_RANGE_r12 1129 -#define _GUARD_NOT_EXHAUSTED_RANGE_r22 1130 -#define _GUARD_NOT_EXHAUSTED_RANGE_r33 1131 -#define _GUARD_NOT_EXHAUSTED_TUPLE_r02 1132 -#define _GUARD_NOT_EXHAUSTED_TUPLE_r12 1133 -#define _GUARD_NOT_EXHAUSTED_TUPLE_r22 1134 -#define _GUARD_NOT_EXHAUSTED_TUPLE_r33 1135 -#define _GUARD_THIRD_NULL_r03 1136 -#define _GUARD_THIRD_NULL_r13 1137 -#define _GUARD_THIRD_NULL_r23 1138 -#define _GUARD_THIRD_NULL_r33 1139 -#define _GUARD_TOS_ANY_DICT_r01 1140 -#define _GUARD_TOS_ANY_DICT_r11 1141 -#define _GUARD_TOS_ANY_DICT_r22 1142 -#define _GUARD_TOS_ANY_DICT_r33 1143 -#define _GUARD_TOS_ANY_SET_r01 1144 -#define _GUARD_TOS_ANY_SET_r11 1145 -#define _GUARD_TOS_ANY_SET_r22 1146 -#define _GUARD_TOS_ANY_SET_r33 1147 -#define _GUARD_TOS_DICT_r01 1148 -#define _GUARD_TOS_DICT_r11 1149 -#define _GUARD_TOS_DICT_r22 1150 -#define _GUARD_TOS_DICT_r33 1151 -#define _GUARD_TOS_FLOAT_r01 1152 -#define _GUARD_TOS_FLOAT_r11 1153 -#define _GUARD_TOS_FLOAT_r22 1154 -#define _GUARD_TOS_FLOAT_r33 1155 -#define _GUARD_TOS_FROZENDICT_r01 1156 -#define _GUARD_TOS_FROZENDICT_r11 1157 -#define _GUARD_TOS_FROZENDICT_r22 1158 -#define _GUARD_TOS_FROZENDICT_r33 1159 -#define _GUARD_TOS_FROZENSET_r01 1160 -#define _GUARD_TOS_FROZENSET_r11 1161 -#define _GUARD_TOS_FROZENSET_r22 1162 -#define _GUARD_TOS_FROZENSET_r33 1163 -#define _GUARD_TOS_INT_r01 1164 -#define _GUARD_TOS_INT_r11 1165 -#define _GUARD_TOS_INT_r22 1166 -#define _GUARD_TOS_INT_r33 1167 -#define _GUARD_TOS_IS_NONE_r01 1168 -#define _GUARD_TOS_IS_NONE_r11 1169 -#define _GUARD_TOS_IS_NONE_r22 1170 -#define _GUARD_TOS_IS_NONE_r33 1171 -#define _GUARD_TOS_LIST_r01 1172 -#define _GUARD_TOS_LIST_r11 1173 -#define _GUARD_TOS_LIST_r22 1174 -#define _GUARD_TOS_LIST_r33 1175 -#define _GUARD_TOS_NOT_NULL_r01 1176 -#define _GUARD_TOS_NOT_NULL_r11 1177 -#define _GUARD_TOS_NOT_NULL_r22 1178 -#define _GUARD_TOS_NOT_NULL_r33 1179 -#define _GUARD_TOS_OVERFLOWED_r01 1180 -#define _GUARD_TOS_OVERFLOWED_r11 1181 -#define _GUARD_TOS_OVERFLOWED_r22 1182 -#define _GUARD_TOS_OVERFLOWED_r33 1183 -#define _GUARD_TOS_SET_r01 1184 -#define _GUARD_TOS_SET_r11 1185 -#define _GUARD_TOS_SET_r22 1186 -#define _GUARD_TOS_SET_r33 1187 -#define _GUARD_TOS_SLICE_r01 1188 -#define _GUARD_TOS_SLICE_r11 1189 -#define _GUARD_TOS_SLICE_r22 1190 -#define _GUARD_TOS_SLICE_r33 1191 -#define _GUARD_TOS_TUPLE_r01 1192 -#define _GUARD_TOS_TUPLE_r11 1193 -#define _GUARD_TOS_TUPLE_r22 1194 -#define _GUARD_TOS_TUPLE_r33 1195 -#define _GUARD_TOS_UNICODE_r01 1196 -#define _GUARD_TOS_UNICODE_r11 1197 -#define _GUARD_TOS_UNICODE_r22 1198 -#define _GUARD_TOS_UNICODE_r33 1199 -#define _GUARD_TYPE_r01 1200 -#define _GUARD_TYPE_r11 1201 -#define _GUARD_TYPE_r22 1202 -#define _GUARD_TYPE_r33 1203 -#define _GUARD_TYPE_ITER_r02 1204 -#define _GUARD_TYPE_ITER_r12 1205 -#define _GUARD_TYPE_ITER_r22 1206 -#define _GUARD_TYPE_ITER_r33 1207 -#define _GUARD_TYPE_VERSION_r01 1208 -#define _GUARD_TYPE_VERSION_r11 1209 -#define _GUARD_TYPE_VERSION_r22 1210 -#define _GUARD_TYPE_VERSION_r33 1211 -#define _GUARD_TYPE_VERSION_LOCKED_r01 1212 -#define _GUARD_TYPE_VERSION_LOCKED_r11 1213 -#define _GUARD_TYPE_VERSION_LOCKED_r22 1214 -#define _GUARD_TYPE_VERSION_LOCKED_r33 1215 -#define _HANDLE_PENDING_AND_DEOPT_r00 1216 -#define _HANDLE_PENDING_AND_DEOPT_r10 1217 -#define _HANDLE_PENDING_AND_DEOPT_r20 1218 -#define _HANDLE_PENDING_AND_DEOPT_r30 1219 -#define _IMPORT_FROM_r12 1220 -#define _IMPORT_NAME_r21 1221 -#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS_r00 1222 -#define _INIT_CALL_PY_EXACT_ARGS_r01 1223 -#define _INIT_CALL_PY_EXACT_ARGS_0_r01 1224 -#define _INIT_CALL_PY_EXACT_ARGS_1_r01 1225 -#define _INIT_CALL_PY_EXACT_ARGS_2_r01 1226 -#define _INIT_CALL_PY_EXACT_ARGS_3_r01 1227 -#define _INIT_CALL_PY_EXACT_ARGS_4_r01 1228 -#define _INSERT_NULL_r10 1229 -#define _INSTRUMENTED_FOR_ITER_r23 1230 -#define _INSTRUMENTED_INSTRUCTION_r00 1231 -#define _INSTRUMENTED_JUMP_FORWARD_r00 1232 -#define _INSTRUMENTED_JUMP_FORWARD_r11 1233 -#define _INSTRUMENTED_JUMP_FORWARD_r22 1234 -#define _INSTRUMENTED_JUMP_FORWARD_r33 1235 -#define _INSTRUMENTED_LINE_r00 1236 -#define _INSTRUMENTED_NOT_TAKEN_r00 1237 -#define _INSTRUMENTED_NOT_TAKEN_r11 1238 -#define _INSTRUMENTED_NOT_TAKEN_r22 1239 -#define _INSTRUMENTED_NOT_TAKEN_r33 1240 -#define _INSTRUMENTED_POP_JUMP_IF_FALSE_r00 1241 -#define _INSTRUMENTED_POP_JUMP_IF_FALSE_r10 1242 -#define _INSTRUMENTED_POP_JUMP_IF_FALSE_r21 1243 -#define _INSTRUMENTED_POP_JUMP_IF_FALSE_r32 1244 -#define _INSTRUMENTED_POP_JUMP_IF_NONE_r10 1245 -#define _INSTRUMENTED_POP_JUMP_IF_NOT_NONE_r10 1246 -#define _INSTRUMENTED_POP_JUMP_IF_TRUE_r00 1247 -#define _INSTRUMENTED_POP_JUMP_IF_TRUE_r10 1248 -#define _INSTRUMENTED_POP_JUMP_IF_TRUE_r21 1249 -#define _INSTRUMENTED_POP_JUMP_IF_TRUE_r32 1250 -#define _IS_NONE_r11 1251 -#define _IS_OP_r03 1252 -#define _IS_OP_r13 1253 -#define _IS_OP_r23 1254 -#define _ITER_CHECK_LIST_r02 1255 -#define _ITER_CHECK_LIST_r12 1256 -#define _ITER_CHECK_LIST_r22 1257 -#define _ITER_CHECK_LIST_r33 1258 -#define _ITER_CHECK_RANGE_r02 1259 -#define _ITER_CHECK_RANGE_r12 1260 -#define _ITER_CHECK_RANGE_r22 1261 -#define _ITER_CHECK_RANGE_r33 1262 -#define _ITER_CHECK_TUPLE_r02 1263 -#define _ITER_CHECK_TUPLE_r12 1264 -#define _ITER_CHECK_TUPLE_r22 1265 -#define _ITER_CHECK_TUPLE_r33 1266 -#define _ITER_JUMP_LIST_r02 1267 -#define _ITER_JUMP_LIST_r12 1268 -#define _ITER_JUMP_LIST_r22 1269 -#define _ITER_JUMP_LIST_r33 1270 -#define _ITER_JUMP_RANGE_r02 1271 -#define _ITER_JUMP_RANGE_r12 1272 -#define _ITER_JUMP_RANGE_r22 1273 -#define _ITER_JUMP_RANGE_r33 1274 -#define _ITER_JUMP_TUPLE_r02 1275 -#define _ITER_JUMP_TUPLE_r12 1276 -#define _ITER_JUMP_TUPLE_r22 1277 -#define _ITER_JUMP_TUPLE_r33 1278 -#define _ITER_NEXT_INLINE_r23 1279 -#define _ITER_NEXT_LIST_r23 1280 -#define _ITER_NEXT_LIST_TIER_TWO_r23 1281 -#define _ITER_NEXT_RANGE_r03 1282 -#define _ITER_NEXT_RANGE_r13 1283 -#define _ITER_NEXT_RANGE_r23 1284 -#define _ITER_NEXT_TUPLE_r03 1285 -#define _ITER_NEXT_TUPLE_r13 1286 -#define _ITER_NEXT_TUPLE_r23 1287 -#define _JUMP_BACKWARD_NO_INTERRUPT_r00 1288 -#define _JUMP_BACKWARD_NO_INTERRUPT_r11 1289 -#define _JUMP_BACKWARD_NO_INTERRUPT_r22 1290 -#define _JUMP_BACKWARD_NO_INTERRUPT_r33 1291 -#define _JUMP_TO_TOP_r00 1292 -#define _LIST_APPEND_r10 1293 -#define _LIST_EXTEND_r11 1294 -#define _LOAD_ATTR_r10 1295 -#define _LOAD_ATTR_CLASS_r11 1296 -#define _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN_FRAME_r11 1297 -#define _LOAD_ATTR_INSTANCE_VALUE_r02 1298 -#define _LOAD_ATTR_INSTANCE_VALUE_r12 1299 -#define _LOAD_ATTR_INSTANCE_VALUE_r23 1300 -#define _LOAD_ATTR_METHOD_LAZY_DICT_r02 1301 -#define _LOAD_ATTR_METHOD_LAZY_DICT_r12 1302 -#define _LOAD_ATTR_METHOD_LAZY_DICT_r23 1303 -#define _LOAD_ATTR_METHOD_NO_DICT_r02 1304 -#define _LOAD_ATTR_METHOD_NO_DICT_r12 1305 -#define _LOAD_ATTR_METHOD_NO_DICT_r23 1306 -#define _LOAD_ATTR_METHOD_WITH_VALUES_r02 1307 -#define _LOAD_ATTR_METHOD_WITH_VALUES_r12 1308 -#define _LOAD_ATTR_METHOD_WITH_VALUES_r23 1309 -#define _LOAD_ATTR_MODULE_r12 1310 -#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT_r11 1311 -#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES_r11 1312 -#define _LOAD_ATTR_PROPERTY_FRAME_r01 1313 -#define _LOAD_ATTR_PROPERTY_FRAME_r11 1314 -#define _LOAD_ATTR_PROPERTY_FRAME_r22 1315 -#define _LOAD_ATTR_PROPERTY_FRAME_r33 1316 -#define _LOAD_ATTR_SLOT_r02 1317 -#define _LOAD_ATTR_SLOT_r12 1318 -#define _LOAD_ATTR_SLOT_r23 1319 -#define _LOAD_ATTR_WITH_HINT_r12 1320 -#define _LOAD_BUILD_CLASS_r01 1321 -#define _LOAD_BYTECODE_r00 1322 -#define _LOAD_COMMON_CONSTANT_r01 1323 -#define _LOAD_COMMON_CONSTANT_r12 1324 -#define _LOAD_COMMON_CONSTANT_r23 1325 -#define _LOAD_CONST_r01 1326 -#define _LOAD_CONST_r12 1327 -#define _LOAD_CONST_r23 1328 -#define _LOAD_CONST_INLINE_r01 1329 -#define _LOAD_CONST_INLINE_r12 1330 -#define _LOAD_CONST_INLINE_r23 1331 -#define _LOAD_CONST_INLINE_BORROW_r01 1332 -#define _LOAD_CONST_INLINE_BORROW_r12 1333 -#define _LOAD_CONST_INLINE_BORROW_r23 1334 -#define _LOAD_DEREF_r01 1335 -#define _LOAD_FAST_r01 1336 -#define _LOAD_FAST_r12 1337 -#define _LOAD_FAST_r23 1338 -#define _LOAD_FAST_0_r01 1339 -#define _LOAD_FAST_0_r12 1340 -#define _LOAD_FAST_0_r23 1341 -#define _LOAD_FAST_1_r01 1342 -#define _LOAD_FAST_1_r12 1343 -#define _LOAD_FAST_1_r23 1344 -#define _LOAD_FAST_2_r01 1345 -#define _LOAD_FAST_2_r12 1346 -#define _LOAD_FAST_2_r23 1347 -#define _LOAD_FAST_3_r01 1348 -#define _LOAD_FAST_3_r12 1349 -#define _LOAD_FAST_3_r23 1350 -#define _LOAD_FAST_4_r01 1351 -#define _LOAD_FAST_4_r12 1352 -#define _LOAD_FAST_4_r23 1353 -#define _LOAD_FAST_5_r01 1354 -#define _LOAD_FAST_5_r12 1355 -#define _LOAD_FAST_5_r23 1356 -#define _LOAD_FAST_6_r01 1357 -#define _LOAD_FAST_6_r12 1358 -#define _LOAD_FAST_6_r23 1359 -#define _LOAD_FAST_7_r01 1360 -#define _LOAD_FAST_7_r12 1361 -#define _LOAD_FAST_7_r23 1362 -#define _LOAD_FAST_AND_CLEAR_r01 1363 -#define _LOAD_FAST_AND_CLEAR_r12 1364 -#define _LOAD_FAST_AND_CLEAR_r23 1365 -#define _LOAD_FAST_BORROW_r01 1366 -#define _LOAD_FAST_BORROW_r12 1367 -#define _LOAD_FAST_BORROW_r23 1368 -#define _LOAD_FAST_BORROW_0_r01 1369 -#define _LOAD_FAST_BORROW_0_r12 1370 -#define _LOAD_FAST_BORROW_0_r23 1371 -#define _LOAD_FAST_BORROW_1_r01 1372 -#define _LOAD_FAST_BORROW_1_r12 1373 -#define _LOAD_FAST_BORROW_1_r23 1374 -#define _LOAD_FAST_BORROW_2_r01 1375 -#define _LOAD_FAST_BORROW_2_r12 1376 -#define _LOAD_FAST_BORROW_2_r23 1377 -#define _LOAD_FAST_BORROW_3_r01 1378 -#define _LOAD_FAST_BORROW_3_r12 1379 -#define _LOAD_FAST_BORROW_3_r23 1380 -#define _LOAD_FAST_BORROW_4_r01 1381 -#define _LOAD_FAST_BORROW_4_r12 1382 -#define _LOAD_FAST_BORROW_4_r23 1383 -#define _LOAD_FAST_BORROW_5_r01 1384 -#define _LOAD_FAST_BORROW_5_r12 1385 -#define _LOAD_FAST_BORROW_5_r23 1386 -#define _LOAD_FAST_BORROW_6_r01 1387 -#define _LOAD_FAST_BORROW_6_r12 1388 -#define _LOAD_FAST_BORROW_6_r23 1389 -#define _LOAD_FAST_BORROW_7_r01 1390 -#define _LOAD_FAST_BORROW_7_r12 1391 -#define _LOAD_FAST_BORROW_7_r23 1392 -#define _LOAD_FAST_BORROW_LOAD_FAST_BORROW_r02 1393 -#define _LOAD_FAST_BORROW_LOAD_FAST_BORROW_r13 1394 -#define _LOAD_FAST_CHECK_r01 1395 -#define _LOAD_FAST_CHECK_r12 1396 -#define _LOAD_FAST_CHECK_r23 1397 -#define _LOAD_FAST_LOAD_FAST_r02 1398 -#define _LOAD_FAST_LOAD_FAST_r13 1399 -#define _LOAD_FROM_DICT_OR_DEREF_r11 1400 -#define _LOAD_FROM_DICT_OR_GLOBALS_r11 1401 -#define _LOAD_GLOBAL_r00 1402 -#define _LOAD_GLOBAL_BUILTINS_r01 1403 -#define _LOAD_GLOBAL_MODULE_r01 1404 -#define _LOAD_LOCALS_r01 1405 -#define _LOAD_LOCALS_r12 1406 -#define _LOAD_LOCALS_r23 1407 -#define _LOAD_NAME_r01 1408 -#define _LOAD_SMALL_INT_r01 1409 -#define _LOAD_SMALL_INT_r12 1410 -#define _LOAD_SMALL_INT_r23 1411 -#define _LOAD_SMALL_INT_0_r01 1412 -#define _LOAD_SMALL_INT_0_r12 1413 -#define _LOAD_SMALL_INT_0_r23 1414 -#define _LOAD_SMALL_INT_1_r01 1415 -#define _LOAD_SMALL_INT_1_r12 1416 -#define _LOAD_SMALL_INT_1_r23 1417 -#define _LOAD_SMALL_INT_2_r01 1418 -#define _LOAD_SMALL_INT_2_r12 1419 -#define _LOAD_SMALL_INT_2_r23 1420 -#define _LOAD_SMALL_INT_3_r01 1421 -#define _LOAD_SMALL_INT_3_r12 1422 -#define _LOAD_SMALL_INT_3_r23 1423 -#define _LOAD_SPECIAL_r00 1424 -#define _LOAD_SUPER_ATTR_ATTR_r31 1425 -#define _LOAD_SUPER_ATTR_METHOD_r32 1426 -#define _LOCK_OBJECT_r01 1427 -#define _LOCK_OBJECT_r11 1428 -#define _LOCK_OBJECT_r22 1429 -#define _LOCK_OBJECT_r33 1430 -#define _MAKE_CALLARGS_A_TUPLE_r33 1431 -#define _MAKE_CELL_r00 1432 -#define _MAKE_FUNCTION_r12 1433 -#define _MAKE_HEAP_SAFE_r01 1434 -#define _MAKE_HEAP_SAFE_r11 1435 -#define _MAKE_HEAP_SAFE_r22 1436 -#define _MAKE_HEAP_SAFE_r33 1437 -#define _MAKE_WARM_r00 1438 -#define _MAKE_WARM_r11 1439 -#define _MAKE_WARM_r22 1440 -#define _MAKE_WARM_r33 1441 -#define _MAP_ADD_r20 1442 -#define _MATCH_CLASS_r33 1443 -#define _MATCH_KEYS_r23 1444 -#define _MATCH_MAPPING_r02 1445 -#define _MATCH_MAPPING_r12 1446 -#define _MATCH_MAPPING_r23 1447 -#define _MATCH_SEQUENCE_r02 1448 -#define _MATCH_SEQUENCE_r12 1449 -#define _MATCH_SEQUENCE_r23 1450 -#define _MAYBE_EXPAND_METHOD_r00 1451 -#define _MAYBE_EXPAND_METHOD_KW_r11 1452 -#define _MONITOR_CALL_r00 1453 -#define _MONITOR_CALL_KW_r11 1454 -#define _MONITOR_JUMP_BACKWARD_r00 1455 -#define _MONITOR_JUMP_BACKWARD_r11 1456 -#define _MONITOR_JUMP_BACKWARD_r22 1457 -#define _MONITOR_JUMP_BACKWARD_r33 1458 -#define _MONITOR_RESUME_r00 1459 -#define _NOP_r00 1460 -#define _NOP_r11 1461 -#define _NOP_r22 1462 -#define _NOP_r33 1463 -#define _POP_EXCEPT_r10 1464 -#define _POP_ITER_r20 1465 -#define _POP_JUMP_IF_FALSE_r00 1466 -#define _POP_JUMP_IF_FALSE_r10 1467 -#define _POP_JUMP_IF_FALSE_r21 1468 -#define _POP_JUMP_IF_FALSE_r32 1469 -#define _POP_JUMP_IF_TRUE_r00 1470 -#define _POP_JUMP_IF_TRUE_r10 1471 -#define _POP_JUMP_IF_TRUE_r21 1472 -#define _POP_JUMP_IF_TRUE_r32 1473 -#define _POP_TOP_r10 1474 -#define _POP_TOP_FLOAT_r00 1475 -#define _POP_TOP_FLOAT_r10 1476 -#define _POP_TOP_FLOAT_r21 1477 -#define _POP_TOP_FLOAT_r32 1478 -#define _POP_TOP_INT_r00 1479 -#define _POP_TOP_INT_r10 1480 -#define _POP_TOP_INT_r21 1481 -#define _POP_TOP_INT_r32 1482 -#define _POP_TOP_NOP_r00 1483 -#define _POP_TOP_NOP_r10 1484 -#define _POP_TOP_NOP_r21 1485 -#define _POP_TOP_NOP_r32 1486 -#define _POP_TOP_OPARG_r00 1487 -#define _POP_TOP_UNICODE_r00 1488 -#define _POP_TOP_UNICODE_r10 1489 -#define _POP_TOP_UNICODE_r21 1490 -#define _POP_TOP_UNICODE_r32 1491 -#define _PUSH_EXC_INFO_r02 1492 -#define _PUSH_EXC_INFO_r12 1493 -#define _PUSH_EXC_INFO_r23 1494 -#define _PUSH_FRAME_r10 1495 -#define _PUSH_NULL_r01 1496 -#define _PUSH_NULL_r12 1497 -#define _PUSH_NULL_r23 1498 -#define _PUSH_NULL_CONDITIONAL_r00 1499 -#define _PUSH_TAGGED_ZERO_r01 1500 -#define _PUSH_TAGGED_ZERO_r12 1501 -#define _PUSH_TAGGED_ZERO_r23 1502 -#define _PY_FRAME_EX_r31 1503 -#define _PY_FRAME_GENERAL_r01 1504 -#define _PY_FRAME_KW_r11 1505 -#define _REPLACE_WITH_TRUE_r02 1506 -#define _REPLACE_WITH_TRUE_r12 1507 -#define _REPLACE_WITH_TRUE_r23 1508 -#define _RESUME_CHECK_r00 1509 -#define _RESUME_CHECK_r11 1510 -#define _RESUME_CHECK_r22 1511 -#define _RESUME_CHECK_r33 1512 -#define _RETURN_GENERATOR_r01 1513 -#define _RETURN_VALUE_r11 1514 -#define _RROT_3_r03 1515 -#define _RROT_3_r13 1516 -#define _RROT_3_r23 1517 -#define _RROT_3_r33 1518 -#define _SAVE_RETURN_OFFSET_r00 1519 -#define _SAVE_RETURN_OFFSET_r11 1520 -#define _SAVE_RETURN_OFFSET_r22 1521 -#define _SAVE_RETURN_OFFSET_r33 1522 -#define _SEND_ASYNC_GEN_r33 1523 -#define _SEND_ASYNC_GEN_TIER_TWO_r33 1524 -#define _SEND_GEN_FRAME_r33 1525 -#define _SEND_VIRTUAL_r33 1526 -#define _SEND_VIRTUAL_TIER_TWO_r03 1527 -#define _SEND_VIRTUAL_TIER_TWO_r13 1528 -#define _SEND_VIRTUAL_TIER_TWO_r23 1529 -#define _SEND_VIRTUAL_TIER_TWO_r33 1530 -#define _SETUP_ANNOTATIONS_r00 1531 -#define _SET_ADD_r10 1532 -#define _SET_FUNCTION_ATTRIBUTE_r01 1533 -#define _SET_FUNCTION_ATTRIBUTE_r11 1534 -#define _SET_FUNCTION_ATTRIBUTE_r21 1535 -#define _SET_FUNCTION_ATTRIBUTE_r32 1536 -#define _SET_IP_r00 1537 -#define _SET_IP_r11 1538 -#define _SET_IP_r22 1539 -#define _SET_IP_r33 1540 -#define _SET_UPDATE_r11 1541 -#define _SPILL_OR_RELOAD_r01 1542 -#define _SPILL_OR_RELOAD_r02 1543 -#define _SPILL_OR_RELOAD_r03 1544 -#define _SPILL_OR_RELOAD_r10 1545 -#define _SPILL_OR_RELOAD_r12 1546 -#define _SPILL_OR_RELOAD_r13 1547 -#define _SPILL_OR_RELOAD_r20 1548 -#define _SPILL_OR_RELOAD_r21 1549 -#define _SPILL_OR_RELOAD_r23 1550 -#define _SPILL_OR_RELOAD_r30 1551 -#define _SPILL_OR_RELOAD_r31 1552 -#define _SPILL_OR_RELOAD_r32 1553 -#define _START_EXECUTOR_r00 1554 -#define _STORE_ATTR_r20 1555 -#define _STORE_ATTR_INSTANCE_VALUE_r21 1556 -#define _STORE_ATTR_SLOT_r21 1557 -#define _STORE_ATTR_WITH_HINT_r21 1558 -#define _STORE_DEREF_r10 1559 -#define _STORE_FAST_LOAD_FAST_r11 1560 -#define _STORE_FAST_STORE_FAST_r20 1561 -#define _STORE_GLOBAL_r10 1562 -#define _STORE_NAME_r10 1563 -#define _STORE_SLICE_r30 1564 -#define _STORE_SUBSCR_r30 1565 -#define _STORE_SUBSCR_DICT_r31 1566 -#define _STORE_SUBSCR_DICT_KNOWN_HASH_r31 1567 -#define _STORE_SUBSCR_LIST_INT_r32 1568 -#define _SWAP_r11 1569 -#define _SWAP_2_r02 1570 -#define _SWAP_2_r12 1571 -#define _SWAP_2_r22 1572 -#define _SWAP_2_r33 1573 -#define _SWAP_3_r03 1574 -#define _SWAP_3_r13 1575 -#define _SWAP_3_r23 1576 -#define _SWAP_3_r33 1577 -#define _SWAP_FAST_r01 1578 -#define _SWAP_FAST_r11 1579 -#define _SWAP_FAST_r22 1580 -#define _SWAP_FAST_r33 1581 -#define _SWAP_FAST_0_r01 1582 -#define _SWAP_FAST_0_r11 1583 -#define _SWAP_FAST_0_r22 1584 -#define _SWAP_FAST_0_r33 1585 -#define _SWAP_FAST_1_r01 1586 -#define _SWAP_FAST_1_r11 1587 -#define _SWAP_FAST_1_r22 1588 -#define _SWAP_FAST_1_r33 1589 -#define _SWAP_FAST_2_r01 1590 -#define _SWAP_FAST_2_r11 1591 -#define _SWAP_FAST_2_r22 1592 -#define _SWAP_FAST_2_r33 1593 -#define _SWAP_FAST_3_r01 1594 -#define _SWAP_FAST_3_r11 1595 -#define _SWAP_FAST_3_r22 1596 -#define _SWAP_FAST_3_r33 1597 -#define _SWAP_FAST_4_r01 1598 -#define _SWAP_FAST_4_r11 1599 -#define _SWAP_FAST_4_r22 1600 -#define _SWAP_FAST_4_r33 1601 -#define _SWAP_FAST_5_r01 1602 -#define _SWAP_FAST_5_r11 1603 -#define _SWAP_FAST_5_r22 1604 -#define _SWAP_FAST_5_r33 1605 -#define _SWAP_FAST_6_r01 1606 -#define _SWAP_FAST_6_r11 1607 -#define _SWAP_FAST_6_r22 1608 -#define _SWAP_FAST_6_r33 1609 -#define _SWAP_FAST_7_r01 1610 -#define _SWAP_FAST_7_r11 1611 -#define _SWAP_FAST_7_r22 1612 -#define _SWAP_FAST_7_r33 1613 -#define _TIER2_RESUME_CHECK_r00 1614 -#define _TIER2_RESUME_CHECK_r11 1615 -#define _TIER2_RESUME_CHECK_r22 1616 -#define _TIER2_RESUME_CHECK_r33 1617 -#define _TO_BOOL_r11 1618 -#define _TO_BOOL_BOOL_r01 1619 -#define _TO_BOOL_BOOL_r11 1620 -#define _TO_BOOL_BOOL_r22 1621 -#define _TO_BOOL_BOOL_r33 1622 -#define _TO_BOOL_INT_r02 1623 -#define _TO_BOOL_INT_r12 1624 -#define _TO_BOOL_INT_r23 1625 -#define _TO_BOOL_LIST_r02 1626 -#define _TO_BOOL_LIST_r12 1627 -#define _TO_BOOL_LIST_r23 1628 -#define _TO_BOOL_NONE_r01 1629 -#define _TO_BOOL_NONE_r11 1630 -#define _TO_BOOL_NONE_r22 1631 -#define _TO_BOOL_NONE_r33 1632 -#define _TO_BOOL_STR_r02 1633 -#define _TO_BOOL_STR_r12 1634 -#define _TO_BOOL_STR_r23 1635 -#define _TRACE_RECORD_r00 1636 -#define _UNARY_INVERT_r12 1637 -#define _UNARY_NEGATIVE_r12 1638 -#define _UNARY_NEGATIVE_FLOAT_INPLACE_r02 1639 -#define _UNARY_NEGATIVE_FLOAT_INPLACE_r12 1640 -#define _UNARY_NEGATIVE_FLOAT_INPLACE_r23 1641 -#define _UNARY_NOT_r01 1642 -#define _UNARY_NOT_r11 1643 -#define _UNARY_NOT_r22 1644 -#define _UNARY_NOT_r33 1645 -#define _UNPACK_EX_r10 1646 -#define _UNPACK_SEQUENCE_r10 1647 -#define _UNPACK_SEQUENCE_LIST_r10 1648 -#define _UNPACK_SEQUENCE_TUPLE_r10 1649 -#define _UNPACK_SEQUENCE_TWO_TUPLE_r12 1650 -#define _UNPACK_SEQUENCE_UNIQUE_THREE_TUPLE_r03 1651 -#define _UNPACK_SEQUENCE_UNIQUE_THREE_TUPLE_r13 1652 -#define _UNPACK_SEQUENCE_UNIQUE_TUPLE_r10 1653 -#define _UNPACK_SEQUENCE_UNIQUE_TWO_TUPLE_r02 1654 -#define _UNPACK_SEQUENCE_UNIQUE_TWO_TUPLE_r12 1655 -#define _UNPACK_SEQUENCE_UNIQUE_TWO_TUPLE_r23 1656 -#define _WITH_EXCEPT_START_r33 1657 -#define _YIELD_VALUE_r11 1658 -#define MAX_UOP_REGS_ID 1658 +#define _YIELD_VALUE 653 +#define MAX_UOP_ID 653 +#define _ALLOCATE_OBJECT_r00 654 +#define _BINARY_OP_r23 655 +#define _BINARY_OP_ADD_FLOAT_r03 656 +#define _BINARY_OP_ADD_FLOAT_r13 657 +#define _BINARY_OP_ADD_FLOAT_r23 658 +#define _BINARY_OP_ADD_FLOAT_INPLACE_r03 659 +#define _BINARY_OP_ADD_FLOAT_INPLACE_r13 660 +#define _BINARY_OP_ADD_FLOAT_INPLACE_r23 661 +#define _BINARY_OP_ADD_FLOAT_INPLACE_RIGHT_r03 662 +#define _BINARY_OP_ADD_FLOAT_INPLACE_RIGHT_r13 663 +#define _BINARY_OP_ADD_FLOAT_INPLACE_RIGHT_r23 664 +#define _BINARY_OP_ADD_INT_r03 665 +#define _BINARY_OP_ADD_INT_r13 666 +#define _BINARY_OP_ADD_INT_r23 667 +#define _BINARY_OP_ADD_INT_INPLACE_r03 668 +#define _BINARY_OP_ADD_INT_INPLACE_r13 669 +#define _BINARY_OP_ADD_INT_INPLACE_r23 670 +#define _BINARY_OP_ADD_INT_INPLACE_RIGHT_r03 671 +#define _BINARY_OP_ADD_INT_INPLACE_RIGHT_r13 672 +#define _BINARY_OP_ADD_INT_INPLACE_RIGHT_r23 673 +#define _BINARY_OP_ADD_UNICODE_r03 674 +#define _BINARY_OP_ADD_UNICODE_r13 675 +#define _BINARY_OP_ADD_UNICODE_r23 676 +#define _BINARY_OP_EXTEND_r23 677 +#define _BINARY_OP_INPLACE_ADD_UNICODE_r21 678 +#define _BINARY_OP_MULTIPLY_FLOAT_r03 679 +#define _BINARY_OP_MULTIPLY_FLOAT_r13 680 +#define _BINARY_OP_MULTIPLY_FLOAT_r23 681 +#define _BINARY_OP_MULTIPLY_FLOAT_INPLACE_r03 682 +#define _BINARY_OP_MULTIPLY_FLOAT_INPLACE_r13 683 +#define _BINARY_OP_MULTIPLY_FLOAT_INPLACE_r23 684 +#define _BINARY_OP_MULTIPLY_FLOAT_INPLACE_RIGHT_r03 685 +#define _BINARY_OP_MULTIPLY_FLOAT_INPLACE_RIGHT_r13 686 +#define _BINARY_OP_MULTIPLY_FLOAT_INPLACE_RIGHT_r23 687 +#define _BINARY_OP_MULTIPLY_INT_r03 688 +#define _BINARY_OP_MULTIPLY_INT_r13 689 +#define _BINARY_OP_MULTIPLY_INT_r23 690 +#define _BINARY_OP_MULTIPLY_INT_INPLACE_r03 691 +#define _BINARY_OP_MULTIPLY_INT_INPLACE_r13 692 +#define _BINARY_OP_MULTIPLY_INT_INPLACE_r23 693 +#define _BINARY_OP_MULTIPLY_INT_INPLACE_RIGHT_r03 694 +#define _BINARY_OP_MULTIPLY_INT_INPLACE_RIGHT_r13 695 +#define _BINARY_OP_MULTIPLY_INT_INPLACE_RIGHT_r23 696 +#define _BINARY_OP_SUBSCR_CHECK_FUNC_r23 697 +#define _BINARY_OP_SUBSCR_DICT_r23 698 +#define _BINARY_OP_SUBSCR_DICT_KNOWN_HASH_r23 699 +#define _BINARY_OP_SUBSCR_INIT_CALL_r01 700 +#define _BINARY_OP_SUBSCR_INIT_CALL_r11 701 +#define _BINARY_OP_SUBSCR_INIT_CALL_r21 702 +#define _BINARY_OP_SUBSCR_INIT_CALL_r31 703 +#define _BINARY_OP_SUBSCR_LIST_INT_r23 704 +#define _BINARY_OP_SUBSCR_LIST_SLICE_r23 705 +#define _BINARY_OP_SUBSCR_STR_INT_r23 706 +#define _BINARY_OP_SUBSCR_TUPLE_INT_r03 707 +#define _BINARY_OP_SUBSCR_TUPLE_INT_r13 708 +#define _BINARY_OP_SUBSCR_TUPLE_INT_r23 709 +#define _BINARY_OP_SUBSCR_USTR_INT_r23 710 +#define _BINARY_OP_SUBTRACT_FLOAT_r03 711 +#define _BINARY_OP_SUBTRACT_FLOAT_r13 712 +#define _BINARY_OP_SUBTRACT_FLOAT_r23 713 +#define _BINARY_OP_SUBTRACT_FLOAT_INPLACE_r03 714 +#define _BINARY_OP_SUBTRACT_FLOAT_INPLACE_r13 715 +#define _BINARY_OP_SUBTRACT_FLOAT_INPLACE_r23 716 +#define _BINARY_OP_SUBTRACT_FLOAT_INPLACE_RIGHT_r03 717 +#define _BINARY_OP_SUBTRACT_FLOAT_INPLACE_RIGHT_r13 718 +#define _BINARY_OP_SUBTRACT_FLOAT_INPLACE_RIGHT_r23 719 +#define _BINARY_OP_SUBTRACT_INT_r03 720 +#define _BINARY_OP_SUBTRACT_INT_r13 721 +#define _BINARY_OP_SUBTRACT_INT_r23 722 +#define _BINARY_OP_SUBTRACT_INT_INPLACE_r03 723 +#define _BINARY_OP_SUBTRACT_INT_INPLACE_r13 724 +#define _BINARY_OP_SUBTRACT_INT_INPLACE_r23 725 +#define _BINARY_OP_SUBTRACT_INT_INPLACE_RIGHT_r03 726 +#define _BINARY_OP_SUBTRACT_INT_INPLACE_RIGHT_r13 727 +#define _BINARY_OP_SUBTRACT_INT_INPLACE_RIGHT_r23 728 +#define _BINARY_OP_TRUEDIV_FLOAT_r23 729 +#define _BINARY_OP_TRUEDIV_FLOAT_INPLACE_r03 730 +#define _BINARY_OP_TRUEDIV_FLOAT_INPLACE_r13 731 +#define _BINARY_OP_TRUEDIV_FLOAT_INPLACE_r23 732 +#define _BINARY_OP_TRUEDIV_FLOAT_INPLACE_RIGHT_r03 733 +#define _BINARY_OP_TRUEDIV_FLOAT_INPLACE_RIGHT_r13 734 +#define _BINARY_OP_TRUEDIV_FLOAT_INPLACE_RIGHT_r23 735 +#define _BINARY_SLICE_r31 736 +#define _BUILD_INTERPOLATION_r01 737 +#define _BUILD_LIST_r01 738 +#define _BUILD_MAP_r01 739 +#define _BUILD_SET_r01 740 +#define _BUILD_SLICE_r01 741 +#define _BUILD_STRING_r01 742 +#define _BUILD_TEMPLATE_r21 743 +#define _BUILD_TUPLE_r01 744 +#define _CALL_BUILTIN_CLASS_r00 745 +#define _CALL_BUILTIN_FAST_r00 746 +#define _CALL_BUILTIN_FAST_WITH_KEYWORDS_r00 747 +#define _CALL_BUILTIN_O_r03 748 +#define _CALL_FUNCTION_EX_NON_PY_GENERAL_r31 749 +#define _CALL_INTRINSIC_1_r12 750 +#define _CALL_INTRINSIC_2_r23 751 +#define _CALL_ISINSTANCE_r31 752 +#define _CALL_KW_NON_PY_r11 753 +#define _CALL_LEN_r33 754 +#define _CALL_LIST_APPEND_r03 755 +#define _CALL_LIST_APPEND_r13 756 +#define _CALL_LIST_APPEND_r23 757 +#define _CALL_LIST_APPEND_r33 758 +#define _CALL_METHOD_DESCRIPTOR_FAST_r00 759 +#define _CALL_METHOD_DESCRIPTOR_FAST_INLINE_r00 760 +#define _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS_r00 761 +#define _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS_INLINE_r00 762 +#define _CALL_METHOD_DESCRIPTOR_NOARGS_r03 763 +#define _CALL_METHOD_DESCRIPTOR_NOARGS_INLINE_r03 764 +#define _CALL_METHOD_DESCRIPTOR_O_r03 765 +#define _CALL_METHOD_DESCRIPTOR_O_INLINE_r03 766 +#define _CALL_NON_PY_GENERAL_r01 767 +#define _CALL_STR_1_r32 768 +#define _CALL_TUPLE_1_r32 769 +#define _CALL_TYPE_1_r02 770 +#define _CALL_TYPE_1_r12 771 +#define _CALL_TYPE_1_r22 772 +#define _CALL_TYPE_1_r32 773 +#define _CHECK_ATTR_CLASS_r01 774 +#define _CHECK_ATTR_CLASS_r11 775 +#define _CHECK_ATTR_CLASS_r22 776 +#define _CHECK_ATTR_CLASS_r33 777 +#define _CHECK_ATTR_METHOD_LAZY_DICT_r01 778 +#define _CHECK_ATTR_METHOD_LAZY_DICT_r11 779 +#define _CHECK_ATTR_METHOD_LAZY_DICT_r22 780 +#define _CHECK_ATTR_METHOD_LAZY_DICT_r33 781 +#define _CHECK_CALL_BOUND_METHOD_EXACT_ARGS_r00 782 +#define _CHECK_EG_MATCH_r22 783 +#define _CHECK_EXC_MATCH_r22 784 +#define _CHECK_FUNCTION_EXACT_ARGS_r00 785 +#define _CHECK_FUNCTION_VERSION_r00 786 +#define _CHECK_FUNCTION_VERSION_INLINE_r00 787 +#define _CHECK_FUNCTION_VERSION_INLINE_r11 788 +#define _CHECK_FUNCTION_VERSION_INLINE_r22 789 +#define _CHECK_FUNCTION_VERSION_INLINE_r33 790 +#define _CHECK_FUNCTION_VERSION_KW_r11 791 +#define _CHECK_IS_NOT_PY_CALLABLE_r00 792 +#define _CHECK_IS_NOT_PY_CALLABLE_EX_r03 793 +#define _CHECK_IS_NOT_PY_CALLABLE_EX_r13 794 +#define _CHECK_IS_NOT_PY_CALLABLE_EX_r23 795 +#define _CHECK_IS_NOT_PY_CALLABLE_EX_r33 796 +#define _CHECK_IS_NOT_PY_CALLABLE_KW_r11 797 +#define _CHECK_IS_PY_CALLABLE_EX_r03 798 +#define _CHECK_IS_PY_CALLABLE_EX_r13 799 +#define _CHECK_IS_PY_CALLABLE_EX_r23 800 +#define _CHECK_IS_PY_CALLABLE_EX_r33 801 +#define _CHECK_MANAGED_OBJECT_HAS_VALUES_r01 802 +#define _CHECK_MANAGED_OBJECT_HAS_VALUES_r11 803 +#define _CHECK_MANAGED_OBJECT_HAS_VALUES_r22 804 +#define _CHECK_MANAGED_OBJECT_HAS_VALUES_r33 805 +#define _CHECK_METHOD_VERSION_r00 806 +#define _CHECK_METHOD_VERSION_KW_r11 807 +#define _CHECK_OBJECT_r00 808 +#define _CHECK_PEP_523_r00 809 +#define _CHECK_PEP_523_r11 810 +#define _CHECK_PEP_523_r22 811 +#define _CHECK_PEP_523_r33 812 +#define _CHECK_PERIODIC_r00 813 +#define _CHECK_PERIODIC_AT_END_r00 814 +#define _CHECK_PERIODIC_IF_NOT_YIELD_FROM_r00 815 +#define _CHECK_RECURSION_LIMIT_r00 816 +#define _CHECK_RECURSION_LIMIT_r11 817 +#define _CHECK_RECURSION_LIMIT_r22 818 +#define _CHECK_RECURSION_LIMIT_r33 819 +#define _CHECK_RECURSION_REMAINING_r00 820 +#define _CHECK_RECURSION_REMAINING_r11 821 +#define _CHECK_RECURSION_REMAINING_r22 822 +#define _CHECK_RECURSION_REMAINING_r33 823 +#define _CHECK_STACK_SPACE_r00 824 +#define _CHECK_STACK_SPACE_OPERAND_r00 825 +#define _CHECK_STACK_SPACE_OPERAND_r11 826 +#define _CHECK_STACK_SPACE_OPERAND_r22 827 +#define _CHECK_STACK_SPACE_OPERAND_r33 828 +#define _CHECK_VALIDITY_r00 829 +#define _CHECK_VALIDITY_r11 830 +#define _CHECK_VALIDITY_r22 831 +#define _CHECK_VALIDITY_r33 832 +#define _COLD_DYNAMIC_EXIT_r00 833 +#define _COLD_EXIT_r00 834 +#define _COMPARE_OP_r21 835 +#define _COMPARE_OP_FLOAT_r03 836 +#define _COMPARE_OP_FLOAT_r13 837 +#define _COMPARE_OP_FLOAT_r23 838 +#define _COMPARE_OP_INT_r23 839 +#define _COMPARE_OP_STR_r23 840 +#define _CONTAINS_OP_r23 841 +#define _CONTAINS_OP_DICT_r23 842 +#define _CONTAINS_OP_SET_r23 843 +#define _CONVERT_VALUE_r11 844 +#define _COPY_r01 845 +#define _COPY_1_r02 846 +#define _COPY_1_r12 847 +#define _COPY_1_r23 848 +#define _COPY_2_r03 849 +#define _COPY_2_r13 850 +#define _COPY_2_r23 851 +#define _COPY_3_r03 852 +#define _COPY_3_r13 853 +#define _COPY_3_r23 854 +#define _COPY_3_r33 855 +#define _COPY_FREE_VARS_r00 856 +#define _COPY_FREE_VARS_r11 857 +#define _COPY_FREE_VARS_r22 858 +#define _COPY_FREE_VARS_r33 859 +#define _CREATE_INIT_FRAME_r01 860 +#define _DELETE_ATTR_r10 861 +#define _DELETE_DEREF_r00 862 +#define _DELETE_FAST_r00 863 +#define _DELETE_GLOBAL_r00 864 +#define _DELETE_NAME_r00 865 +#define _DELETE_SUBSCR_r20 866 +#define _DEOPT_r00 867 +#define _DEOPT_r10 868 +#define _DEOPT_r20 869 +#define _DEOPT_r30 870 +#define _DICT_MERGE_r11 871 +#define _DICT_UPDATE_r11 872 +#define _DO_CALL_r01 873 +#define _DO_CALL_FUNCTION_EX_r31 874 +#define _DO_CALL_KW_r11 875 +#define _DYNAMIC_EXIT_r00 876 +#define _DYNAMIC_EXIT_r10 877 +#define _DYNAMIC_EXIT_r20 878 +#define _DYNAMIC_EXIT_r30 879 +#define _END_FOR_r10 880 +#define _END_SEND_r31 881 +#define _ERROR_POP_N_r00 882 +#define _EXIT_INIT_CHECK_r10 883 +#define _EXIT_TRACE_r00 884 +#define _EXIT_TRACE_r10 885 +#define _EXIT_TRACE_r20 886 +#define _EXIT_TRACE_r30 887 +#define _EXPAND_METHOD_r00 888 +#define _EXPAND_METHOD_KW_r11 889 +#define _FATAL_ERROR_r00 890 +#define _FATAL_ERROR_r11 891 +#define _FATAL_ERROR_r22 892 +#define _FATAL_ERROR_r33 893 +#define _FORMAT_SIMPLE_r11 894 +#define _FORMAT_WITH_SPEC_r21 895 +#define _FOR_ITER_r23 896 +#define _FOR_ITER_GEN_FRAME_r03 897 +#define _FOR_ITER_GEN_FRAME_r13 898 +#define _FOR_ITER_GEN_FRAME_r23 899 +#define _FOR_ITER_TIER_TWO_r23 900 +#define _FOR_ITER_VIRTUAL_r23 901 +#define _FOR_ITER_VIRTUAL_TIER_TWO_r23 902 +#define _GET_AITER_r11 903 +#define _GET_ANEXT_r12 904 +#define _GET_AWAITABLE_r11 905 +#define _GET_ITER_r12 906 +#define _GET_ITER_TRAD_r12 907 +#define _GET_LEN_r12 908 +#define _GUARD_3OS_ASYNC_GEN_ASEND_r03 909 +#define _GUARD_3OS_ASYNC_GEN_ASEND_r13 910 +#define _GUARD_3OS_ASYNC_GEN_ASEND_r23 911 +#define _GUARD_3OS_ASYNC_GEN_ASEND_r33 912 +#define _GUARD_BINARY_OP_EXTEND_r22 913 +#define _GUARD_BINARY_OP_EXTEND_LHS_r02 914 +#define _GUARD_BINARY_OP_EXTEND_LHS_r12 915 +#define _GUARD_BINARY_OP_EXTEND_LHS_r22 916 +#define _GUARD_BINARY_OP_EXTEND_LHS_r33 917 +#define _GUARD_BINARY_OP_EXTEND_RHS_r02 918 +#define _GUARD_BINARY_OP_EXTEND_RHS_r12 919 +#define _GUARD_BINARY_OP_EXTEND_RHS_r22 920 +#define _GUARD_BINARY_OP_EXTEND_RHS_r33 921 +#define _GUARD_BINARY_OP_SUBSCR_TUPLE_INT_BOUNDS_r02 922 +#define _GUARD_BINARY_OP_SUBSCR_TUPLE_INT_BOUNDS_r12 923 +#define _GUARD_BINARY_OP_SUBSCR_TUPLE_INT_BOUNDS_r22 924 +#define _GUARD_BINARY_OP_SUBSCR_TUPLE_INT_BOUNDS_r33 925 +#define _GUARD_BIT_IS_SET_POP_r00 926 +#define _GUARD_BIT_IS_SET_POP_r10 927 +#define _GUARD_BIT_IS_SET_POP_r21 928 +#define _GUARD_BIT_IS_SET_POP_r32 929 +#define _GUARD_BIT_IS_SET_POP_4_r00 930 +#define _GUARD_BIT_IS_SET_POP_4_r10 931 +#define _GUARD_BIT_IS_SET_POP_4_r21 932 +#define _GUARD_BIT_IS_SET_POP_4_r32 933 +#define _GUARD_BIT_IS_SET_POP_5_r00 934 +#define _GUARD_BIT_IS_SET_POP_5_r10 935 +#define _GUARD_BIT_IS_SET_POP_5_r21 936 +#define _GUARD_BIT_IS_SET_POP_5_r32 937 +#define _GUARD_BIT_IS_SET_POP_6_r00 938 +#define _GUARD_BIT_IS_SET_POP_6_r10 939 +#define _GUARD_BIT_IS_SET_POP_6_r21 940 +#define _GUARD_BIT_IS_SET_POP_6_r32 941 +#define _GUARD_BIT_IS_SET_POP_7_r00 942 +#define _GUARD_BIT_IS_SET_POP_7_r10 943 +#define _GUARD_BIT_IS_SET_POP_7_r21 944 +#define _GUARD_BIT_IS_SET_POP_7_r32 945 +#define _GUARD_BIT_IS_UNSET_POP_r00 946 +#define _GUARD_BIT_IS_UNSET_POP_r10 947 +#define _GUARD_BIT_IS_UNSET_POP_r21 948 +#define _GUARD_BIT_IS_UNSET_POP_r32 949 +#define _GUARD_BIT_IS_UNSET_POP_4_r00 950 +#define _GUARD_BIT_IS_UNSET_POP_4_r10 951 +#define _GUARD_BIT_IS_UNSET_POP_4_r21 952 +#define _GUARD_BIT_IS_UNSET_POP_4_r32 953 +#define _GUARD_BIT_IS_UNSET_POP_5_r00 954 +#define _GUARD_BIT_IS_UNSET_POP_5_r10 955 +#define _GUARD_BIT_IS_UNSET_POP_5_r21 956 +#define _GUARD_BIT_IS_UNSET_POP_5_r32 957 +#define _GUARD_BIT_IS_UNSET_POP_6_r00 958 +#define _GUARD_BIT_IS_UNSET_POP_6_r10 959 +#define _GUARD_BIT_IS_UNSET_POP_6_r21 960 +#define _GUARD_BIT_IS_UNSET_POP_6_r32 961 +#define _GUARD_BIT_IS_UNSET_POP_7_r00 962 +#define _GUARD_BIT_IS_UNSET_POP_7_r10 963 +#define _GUARD_BIT_IS_UNSET_POP_7_r21 964 +#define _GUARD_BIT_IS_UNSET_POP_7_r32 965 +#define _GUARD_CALLABLE_BUILTIN_CLASS_r00 966 +#define _GUARD_CALLABLE_BUILTIN_FAST_r00 967 +#define _GUARD_CALLABLE_BUILTIN_FAST_WITH_KEYWORDS_r00 968 +#define _GUARD_CALLABLE_BUILTIN_O_r00 969 +#define _GUARD_CALLABLE_ISINSTANCE_r03 970 +#define _GUARD_CALLABLE_ISINSTANCE_r13 971 +#define _GUARD_CALLABLE_ISINSTANCE_r23 972 +#define _GUARD_CALLABLE_ISINSTANCE_r33 973 +#define _GUARD_CALLABLE_LEN_r03 974 +#define _GUARD_CALLABLE_LEN_r13 975 +#define _GUARD_CALLABLE_LEN_r23 976 +#define _GUARD_CALLABLE_LEN_r33 977 +#define _GUARD_CALLABLE_LIST_APPEND_r03 978 +#define _GUARD_CALLABLE_LIST_APPEND_r13 979 +#define _GUARD_CALLABLE_LIST_APPEND_r23 980 +#define _GUARD_CALLABLE_LIST_APPEND_r33 981 +#define _GUARD_CALLABLE_METHOD_DESCRIPTOR_FAST_r00 982 +#define _GUARD_CALLABLE_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS_r00 983 +#define _GUARD_CALLABLE_METHOD_DESCRIPTOR_NOARGS_r00 984 +#define _GUARD_CALLABLE_METHOD_DESCRIPTOR_O_r00 985 +#define _GUARD_CALLABLE_STR_1_r03 986 +#define _GUARD_CALLABLE_STR_1_r13 987 +#define _GUARD_CALLABLE_STR_1_r23 988 +#define _GUARD_CALLABLE_STR_1_r33 989 +#define _GUARD_CALLABLE_TUPLE_1_r03 990 +#define _GUARD_CALLABLE_TUPLE_1_r13 991 +#define _GUARD_CALLABLE_TUPLE_1_r23 992 +#define _GUARD_CALLABLE_TUPLE_1_r33 993 +#define _GUARD_CALLABLE_TYPE_1_r03 994 +#define _GUARD_CALLABLE_TYPE_1_r13 995 +#define _GUARD_CALLABLE_TYPE_1_r23 996 +#define _GUARD_CALLABLE_TYPE_1_r33 997 +#define _GUARD_CODE_VERSION_RETURN_GENERATOR_r00 998 +#define _GUARD_CODE_VERSION_RETURN_GENERATOR_r11 999 +#define _GUARD_CODE_VERSION_RETURN_GENERATOR_r22 1000 +#define _GUARD_CODE_VERSION_RETURN_GENERATOR_r33 1001 +#define _GUARD_CODE_VERSION_RETURN_VALUE_r00 1002 +#define _GUARD_CODE_VERSION_RETURN_VALUE_r11 1003 +#define _GUARD_CODE_VERSION_RETURN_VALUE_r22 1004 +#define _GUARD_CODE_VERSION_RETURN_VALUE_r33 1005 +#define _GUARD_CODE_VERSION_YIELD_VALUE_r00 1006 +#define _GUARD_CODE_VERSION_YIELD_VALUE_r11 1007 +#define _GUARD_CODE_VERSION_YIELD_VALUE_r22 1008 +#define _GUARD_CODE_VERSION_YIELD_VALUE_r33 1009 +#define _GUARD_CODE_VERSION__PUSH_FRAME_r00 1010 +#define _GUARD_CODE_VERSION__PUSH_FRAME_r11 1011 +#define _GUARD_CODE_VERSION__PUSH_FRAME_r22 1012 +#define _GUARD_CODE_VERSION__PUSH_FRAME_r33 1013 +#define _GUARD_DORV_NO_DICT_r01 1014 +#define _GUARD_DORV_NO_DICT_r11 1015 +#define _GUARD_DORV_NO_DICT_r22 1016 +#define _GUARD_DORV_NO_DICT_r33 1017 +#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_r01 1018 +#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_r11 1019 +#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_r22 1020 +#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_r33 1021 +#define _GUARD_GLOBALS_VERSION_r00 1022 +#define _GUARD_GLOBALS_VERSION_r11 1023 +#define _GUARD_GLOBALS_VERSION_r22 1024 +#define _GUARD_GLOBALS_VERSION_r33 1025 +#define _GUARD_IP_RETURN_GENERATOR_r00 1026 +#define _GUARD_IP_RETURN_GENERATOR_r11 1027 +#define _GUARD_IP_RETURN_GENERATOR_r22 1028 +#define _GUARD_IP_RETURN_GENERATOR_r33 1029 +#define _GUARD_IP_RETURN_VALUE_r00 1030 +#define _GUARD_IP_RETURN_VALUE_r11 1031 +#define _GUARD_IP_RETURN_VALUE_r22 1032 +#define _GUARD_IP_RETURN_VALUE_r33 1033 +#define _GUARD_IP_YIELD_VALUE_r00 1034 +#define _GUARD_IP_YIELD_VALUE_r11 1035 +#define _GUARD_IP_YIELD_VALUE_r22 1036 +#define _GUARD_IP_YIELD_VALUE_r33 1037 +#define _GUARD_IP__PUSH_FRAME_r00 1038 +#define _GUARD_IP__PUSH_FRAME_r11 1039 +#define _GUARD_IP__PUSH_FRAME_r22 1040 +#define _GUARD_IP__PUSH_FRAME_r33 1041 +#define _GUARD_IS_FALSE_POP_r00 1042 +#define _GUARD_IS_FALSE_POP_r10 1043 +#define _GUARD_IS_FALSE_POP_r21 1044 +#define _GUARD_IS_FALSE_POP_r32 1045 +#define _GUARD_IS_NONE_POP_r00 1046 +#define _GUARD_IS_NONE_POP_r10 1047 +#define _GUARD_IS_NONE_POP_r21 1048 +#define _GUARD_IS_NONE_POP_r32 1049 +#define _GUARD_IS_NOT_NONE_POP_r10 1050 +#define _GUARD_IS_TRUE_POP_r00 1051 +#define _GUARD_IS_TRUE_POP_r10 1052 +#define _GUARD_IS_TRUE_POP_r21 1053 +#define _GUARD_IS_TRUE_POP_r32 1054 +#define _GUARD_ITERATOR_r01 1055 +#define _GUARD_ITERATOR_r11 1056 +#define _GUARD_ITERATOR_r22 1057 +#define _GUARD_ITERATOR_r33 1058 +#define _GUARD_ITER_VIRTUAL_r01 1059 +#define _GUARD_ITER_VIRTUAL_r11 1060 +#define _GUARD_ITER_VIRTUAL_r22 1061 +#define _GUARD_ITER_VIRTUAL_r33 1062 +#define _GUARD_LOAD_SUPER_ATTR_METHOD_r03 1063 +#define _GUARD_LOAD_SUPER_ATTR_METHOD_r13 1064 +#define _GUARD_LOAD_SUPER_ATTR_METHOD_r23 1065 +#define _GUARD_LOAD_SUPER_ATTR_METHOD_r33 1066 +#define _GUARD_NOS_COMPACT_ASCII_r02 1067 +#define _GUARD_NOS_COMPACT_ASCII_r12 1068 +#define _GUARD_NOS_COMPACT_ASCII_r22 1069 +#define _GUARD_NOS_COMPACT_ASCII_r33 1070 +#define _GUARD_NOS_DICT_STORE_SUBSCRIPT_r03 1071 +#define _GUARD_NOS_DICT_STORE_SUBSCRIPT_r13 1072 +#define _GUARD_NOS_DICT_STORE_SUBSCRIPT_r23 1073 +#define _GUARD_NOS_DICT_STORE_SUBSCRIPT_r33 1074 +#define _GUARD_NOS_DICT_SUBSCRIPT_r02 1075 +#define _GUARD_NOS_DICT_SUBSCRIPT_r12 1076 +#define _GUARD_NOS_DICT_SUBSCRIPT_r22 1077 +#define _GUARD_NOS_DICT_SUBSCRIPT_r33 1078 +#define _GUARD_NOS_FLOAT_r02 1079 +#define _GUARD_NOS_FLOAT_r12 1080 +#define _GUARD_NOS_FLOAT_r22 1081 +#define _GUARD_NOS_FLOAT_r33 1082 +#define _GUARD_NOS_INT_r02 1083 +#define _GUARD_NOS_INT_r12 1084 +#define _GUARD_NOS_INT_r22 1085 +#define _GUARD_NOS_INT_r33 1086 +#define _GUARD_NOS_ITER_VIRTUAL_r02 1087 +#define _GUARD_NOS_ITER_VIRTUAL_r12 1088 +#define _GUARD_NOS_ITER_VIRTUAL_r22 1089 +#define _GUARD_NOS_ITER_VIRTUAL_r33 1090 +#define _GUARD_NOS_LIST_r02 1091 +#define _GUARD_NOS_LIST_r12 1092 +#define _GUARD_NOS_LIST_r22 1093 +#define _GUARD_NOS_LIST_r33 1094 +#define _GUARD_NOS_NOT_NULL_r02 1095 +#define _GUARD_NOS_NOT_NULL_r12 1096 +#define _GUARD_NOS_NOT_NULL_r22 1097 +#define _GUARD_NOS_NOT_NULL_r33 1098 +#define _GUARD_NOS_NULL_r02 1099 +#define _GUARD_NOS_NULL_r12 1100 +#define _GUARD_NOS_NULL_r22 1101 +#define _GUARD_NOS_NULL_r33 1102 +#define _GUARD_NOS_OVERFLOWED_r02 1103 +#define _GUARD_NOS_OVERFLOWED_r12 1104 +#define _GUARD_NOS_OVERFLOWED_r22 1105 +#define _GUARD_NOS_OVERFLOWED_r33 1106 +#define _GUARD_NOS_TUPLE_r02 1107 +#define _GUARD_NOS_TUPLE_r12 1108 +#define _GUARD_NOS_TUPLE_r22 1109 +#define _GUARD_NOS_TUPLE_r33 1110 +#define _GUARD_NOS_TYPE_VERSION_r02 1111 +#define _GUARD_NOS_TYPE_VERSION_r12 1112 +#define _GUARD_NOS_TYPE_VERSION_r22 1113 +#define _GUARD_NOS_TYPE_VERSION_r33 1114 +#define _GUARD_NOS_UNICODE_r02 1115 +#define _GUARD_NOS_UNICODE_r12 1116 +#define _GUARD_NOS_UNICODE_r22 1117 +#define _GUARD_NOS_UNICODE_r33 1118 +#define _GUARD_NOT_EXHAUSTED_LIST_r02 1119 +#define _GUARD_NOT_EXHAUSTED_LIST_r12 1120 +#define _GUARD_NOT_EXHAUSTED_LIST_r22 1121 +#define _GUARD_NOT_EXHAUSTED_LIST_r33 1122 +#define _GUARD_NOT_EXHAUSTED_RANGE_r02 1123 +#define _GUARD_NOT_EXHAUSTED_RANGE_r12 1124 +#define _GUARD_NOT_EXHAUSTED_RANGE_r22 1125 +#define _GUARD_NOT_EXHAUSTED_RANGE_r33 1126 +#define _GUARD_NOT_EXHAUSTED_TUPLE_r02 1127 +#define _GUARD_NOT_EXHAUSTED_TUPLE_r12 1128 +#define _GUARD_NOT_EXHAUSTED_TUPLE_r22 1129 +#define _GUARD_NOT_EXHAUSTED_TUPLE_r33 1130 +#define _GUARD_THIRD_NULL_r03 1131 +#define _GUARD_THIRD_NULL_r13 1132 +#define _GUARD_THIRD_NULL_r23 1133 +#define _GUARD_THIRD_NULL_r33 1134 +#define _GUARD_TOS_ANY_DICT_r01 1135 +#define _GUARD_TOS_ANY_DICT_r11 1136 +#define _GUARD_TOS_ANY_DICT_r22 1137 +#define _GUARD_TOS_ANY_DICT_r33 1138 +#define _GUARD_TOS_ANY_SET_r01 1139 +#define _GUARD_TOS_ANY_SET_r11 1140 +#define _GUARD_TOS_ANY_SET_r22 1141 +#define _GUARD_TOS_ANY_SET_r33 1142 +#define _GUARD_TOS_DICT_r01 1143 +#define _GUARD_TOS_DICT_r11 1144 +#define _GUARD_TOS_DICT_r22 1145 +#define _GUARD_TOS_DICT_r33 1146 +#define _GUARD_TOS_FLOAT_r01 1147 +#define _GUARD_TOS_FLOAT_r11 1148 +#define _GUARD_TOS_FLOAT_r22 1149 +#define _GUARD_TOS_FLOAT_r33 1150 +#define _GUARD_TOS_FROZENDICT_r01 1151 +#define _GUARD_TOS_FROZENDICT_r11 1152 +#define _GUARD_TOS_FROZENDICT_r22 1153 +#define _GUARD_TOS_FROZENDICT_r33 1154 +#define _GUARD_TOS_FROZENSET_r01 1155 +#define _GUARD_TOS_FROZENSET_r11 1156 +#define _GUARD_TOS_FROZENSET_r22 1157 +#define _GUARD_TOS_FROZENSET_r33 1158 +#define _GUARD_TOS_INT_r01 1159 +#define _GUARD_TOS_INT_r11 1160 +#define _GUARD_TOS_INT_r22 1161 +#define _GUARD_TOS_INT_r33 1162 +#define _GUARD_TOS_IS_NONE_r01 1163 +#define _GUARD_TOS_IS_NONE_r11 1164 +#define _GUARD_TOS_IS_NONE_r22 1165 +#define _GUARD_TOS_IS_NONE_r33 1166 +#define _GUARD_TOS_LIST_r01 1167 +#define _GUARD_TOS_LIST_r11 1168 +#define _GUARD_TOS_LIST_r22 1169 +#define _GUARD_TOS_LIST_r33 1170 +#define _GUARD_TOS_NOT_NULL_r01 1171 +#define _GUARD_TOS_NOT_NULL_r11 1172 +#define _GUARD_TOS_NOT_NULL_r22 1173 +#define _GUARD_TOS_NOT_NULL_r33 1174 +#define _GUARD_TOS_OVERFLOWED_r01 1175 +#define _GUARD_TOS_OVERFLOWED_r11 1176 +#define _GUARD_TOS_OVERFLOWED_r22 1177 +#define _GUARD_TOS_OVERFLOWED_r33 1178 +#define _GUARD_TOS_SET_r01 1179 +#define _GUARD_TOS_SET_r11 1180 +#define _GUARD_TOS_SET_r22 1181 +#define _GUARD_TOS_SET_r33 1182 +#define _GUARD_TOS_SLICE_r01 1183 +#define _GUARD_TOS_SLICE_r11 1184 +#define _GUARD_TOS_SLICE_r22 1185 +#define _GUARD_TOS_SLICE_r33 1186 +#define _GUARD_TOS_TUPLE_r01 1187 +#define _GUARD_TOS_TUPLE_r11 1188 +#define _GUARD_TOS_TUPLE_r22 1189 +#define _GUARD_TOS_TUPLE_r33 1190 +#define _GUARD_TOS_UNICODE_r01 1191 +#define _GUARD_TOS_UNICODE_r11 1192 +#define _GUARD_TOS_UNICODE_r22 1193 +#define _GUARD_TOS_UNICODE_r33 1194 +#define _GUARD_TYPE_r01 1195 +#define _GUARD_TYPE_r11 1196 +#define _GUARD_TYPE_r22 1197 +#define _GUARD_TYPE_r33 1198 +#define _GUARD_TYPE_ITER_r02 1199 +#define _GUARD_TYPE_ITER_r12 1200 +#define _GUARD_TYPE_ITER_r22 1201 +#define _GUARD_TYPE_ITER_r33 1202 +#define _GUARD_TYPE_VERSION_r01 1203 +#define _GUARD_TYPE_VERSION_r11 1204 +#define _GUARD_TYPE_VERSION_r22 1205 +#define _GUARD_TYPE_VERSION_r33 1206 +#define _GUARD_TYPE_VERSION_LOCKED_r01 1207 +#define _GUARD_TYPE_VERSION_LOCKED_r11 1208 +#define _GUARD_TYPE_VERSION_LOCKED_r22 1209 +#define _GUARD_TYPE_VERSION_LOCKED_r33 1210 +#define _HANDLE_PENDING_AND_DEOPT_r00 1211 +#define _HANDLE_PENDING_AND_DEOPT_r10 1212 +#define _HANDLE_PENDING_AND_DEOPT_r20 1213 +#define _HANDLE_PENDING_AND_DEOPT_r30 1214 +#define _IMPORT_FROM_r12 1215 +#define _IMPORT_NAME_r21 1216 +#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS_r00 1217 +#define _INIT_CALL_PY_EXACT_ARGS_r01 1218 +#define _INIT_CALL_PY_EXACT_ARGS_0_r01 1219 +#define _INIT_CALL_PY_EXACT_ARGS_1_r01 1220 +#define _INIT_CALL_PY_EXACT_ARGS_2_r01 1221 +#define _INIT_CALL_PY_EXACT_ARGS_3_r01 1222 +#define _INIT_CALL_PY_EXACT_ARGS_4_r01 1223 +#define _INSERT_NULL_r10 1224 +#define _INSTRUMENTED_FOR_ITER_r23 1225 +#define _INSTRUMENTED_INSTRUCTION_r00 1226 +#define _INSTRUMENTED_JUMP_FORWARD_r00 1227 +#define _INSTRUMENTED_JUMP_FORWARD_r11 1228 +#define _INSTRUMENTED_JUMP_FORWARD_r22 1229 +#define _INSTRUMENTED_JUMP_FORWARD_r33 1230 +#define _INSTRUMENTED_LINE_r00 1231 +#define _INSTRUMENTED_NOT_TAKEN_r00 1232 +#define _INSTRUMENTED_NOT_TAKEN_r11 1233 +#define _INSTRUMENTED_NOT_TAKEN_r22 1234 +#define _INSTRUMENTED_NOT_TAKEN_r33 1235 +#define _INSTRUMENTED_POP_JUMP_IF_FALSE_r00 1236 +#define _INSTRUMENTED_POP_JUMP_IF_FALSE_r10 1237 +#define _INSTRUMENTED_POP_JUMP_IF_FALSE_r21 1238 +#define _INSTRUMENTED_POP_JUMP_IF_FALSE_r32 1239 +#define _INSTRUMENTED_POP_JUMP_IF_NONE_r10 1240 +#define _INSTRUMENTED_POP_JUMP_IF_NOT_NONE_r10 1241 +#define _INSTRUMENTED_POP_JUMP_IF_TRUE_r00 1242 +#define _INSTRUMENTED_POP_JUMP_IF_TRUE_r10 1243 +#define _INSTRUMENTED_POP_JUMP_IF_TRUE_r21 1244 +#define _INSTRUMENTED_POP_JUMP_IF_TRUE_r32 1245 +#define _IS_NONE_r11 1246 +#define _IS_OP_r03 1247 +#define _IS_OP_r13 1248 +#define _IS_OP_r23 1249 +#define _ITER_CHECK_LIST_r02 1250 +#define _ITER_CHECK_LIST_r12 1251 +#define _ITER_CHECK_LIST_r22 1252 +#define _ITER_CHECK_LIST_r33 1253 +#define _ITER_CHECK_RANGE_r02 1254 +#define _ITER_CHECK_RANGE_r12 1255 +#define _ITER_CHECK_RANGE_r22 1256 +#define _ITER_CHECK_RANGE_r33 1257 +#define _ITER_CHECK_TUPLE_r02 1258 +#define _ITER_CHECK_TUPLE_r12 1259 +#define _ITER_CHECK_TUPLE_r22 1260 +#define _ITER_CHECK_TUPLE_r33 1261 +#define _ITER_JUMP_LIST_r02 1262 +#define _ITER_JUMP_LIST_r12 1263 +#define _ITER_JUMP_LIST_r22 1264 +#define _ITER_JUMP_LIST_r33 1265 +#define _ITER_JUMP_RANGE_r02 1266 +#define _ITER_JUMP_RANGE_r12 1267 +#define _ITER_JUMP_RANGE_r22 1268 +#define _ITER_JUMP_RANGE_r33 1269 +#define _ITER_JUMP_TUPLE_r02 1270 +#define _ITER_JUMP_TUPLE_r12 1271 +#define _ITER_JUMP_TUPLE_r22 1272 +#define _ITER_JUMP_TUPLE_r33 1273 +#define _ITER_NEXT_INLINE_r23 1274 +#define _ITER_NEXT_LIST_r23 1275 +#define _ITER_NEXT_LIST_TIER_TWO_r23 1276 +#define _ITER_NEXT_RANGE_r03 1277 +#define _ITER_NEXT_RANGE_r13 1278 +#define _ITER_NEXT_RANGE_r23 1279 +#define _ITER_NEXT_TUPLE_r03 1280 +#define _ITER_NEXT_TUPLE_r13 1281 +#define _ITER_NEXT_TUPLE_r23 1282 +#define _JUMP_BACKWARD_NO_INTERRUPT_r00 1283 +#define _JUMP_BACKWARD_NO_INTERRUPT_r11 1284 +#define _JUMP_BACKWARD_NO_INTERRUPT_r22 1285 +#define _JUMP_BACKWARD_NO_INTERRUPT_r33 1286 +#define _JUMP_TO_TOP_r00 1287 +#define _LIST_APPEND_r10 1288 +#define _LIST_EXTEND_r11 1289 +#define _LOAD_ATTR_r10 1290 +#define _LOAD_ATTR_CLASS_r11 1291 +#define _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN_FRAME_r11 1292 +#define _LOAD_ATTR_INSTANCE_VALUE_r02 1293 +#define _LOAD_ATTR_INSTANCE_VALUE_r12 1294 +#define _LOAD_ATTR_INSTANCE_VALUE_r23 1295 +#define _LOAD_ATTR_METHOD_LAZY_DICT_r02 1296 +#define _LOAD_ATTR_METHOD_LAZY_DICT_r12 1297 +#define _LOAD_ATTR_METHOD_LAZY_DICT_r23 1298 +#define _LOAD_ATTR_METHOD_NO_DICT_r02 1299 +#define _LOAD_ATTR_METHOD_NO_DICT_r12 1300 +#define _LOAD_ATTR_METHOD_NO_DICT_r23 1301 +#define _LOAD_ATTR_METHOD_WITH_VALUES_r02 1302 +#define _LOAD_ATTR_METHOD_WITH_VALUES_r12 1303 +#define _LOAD_ATTR_METHOD_WITH_VALUES_r23 1304 +#define _LOAD_ATTR_MODULE_r12 1305 +#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT_r11 1306 +#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES_r11 1307 +#define _LOAD_ATTR_PROPERTY_FRAME_r01 1308 +#define _LOAD_ATTR_PROPERTY_FRAME_r11 1309 +#define _LOAD_ATTR_PROPERTY_FRAME_r22 1310 +#define _LOAD_ATTR_PROPERTY_FRAME_r33 1311 +#define _LOAD_ATTR_SLOT_r02 1312 +#define _LOAD_ATTR_SLOT_r12 1313 +#define _LOAD_ATTR_SLOT_r23 1314 +#define _LOAD_ATTR_WITH_HINT_r12 1315 +#define _LOAD_BUILD_CLASS_r01 1316 +#define _LOAD_BYTECODE_r00 1317 +#define _LOAD_COMMON_CONSTANT_r01 1318 +#define _LOAD_COMMON_CONSTANT_r12 1319 +#define _LOAD_COMMON_CONSTANT_r23 1320 +#define _LOAD_CONST_r01 1321 +#define _LOAD_CONST_r12 1322 +#define _LOAD_CONST_r23 1323 +#define _LOAD_CONST_INLINE_r01 1324 +#define _LOAD_CONST_INLINE_r12 1325 +#define _LOAD_CONST_INLINE_r23 1326 +#define _LOAD_CONST_INLINE_BORROW_r01 1327 +#define _LOAD_CONST_INLINE_BORROW_r12 1328 +#define _LOAD_CONST_INLINE_BORROW_r23 1329 +#define _LOAD_DEREF_r01 1330 +#define _LOAD_FAST_r01 1331 +#define _LOAD_FAST_r12 1332 +#define _LOAD_FAST_r23 1333 +#define _LOAD_FAST_0_r01 1334 +#define _LOAD_FAST_0_r12 1335 +#define _LOAD_FAST_0_r23 1336 +#define _LOAD_FAST_1_r01 1337 +#define _LOAD_FAST_1_r12 1338 +#define _LOAD_FAST_1_r23 1339 +#define _LOAD_FAST_2_r01 1340 +#define _LOAD_FAST_2_r12 1341 +#define _LOAD_FAST_2_r23 1342 +#define _LOAD_FAST_3_r01 1343 +#define _LOAD_FAST_3_r12 1344 +#define _LOAD_FAST_3_r23 1345 +#define _LOAD_FAST_4_r01 1346 +#define _LOAD_FAST_4_r12 1347 +#define _LOAD_FAST_4_r23 1348 +#define _LOAD_FAST_5_r01 1349 +#define _LOAD_FAST_5_r12 1350 +#define _LOAD_FAST_5_r23 1351 +#define _LOAD_FAST_6_r01 1352 +#define _LOAD_FAST_6_r12 1353 +#define _LOAD_FAST_6_r23 1354 +#define _LOAD_FAST_7_r01 1355 +#define _LOAD_FAST_7_r12 1356 +#define _LOAD_FAST_7_r23 1357 +#define _LOAD_FAST_AND_CLEAR_r01 1358 +#define _LOAD_FAST_AND_CLEAR_r12 1359 +#define _LOAD_FAST_AND_CLEAR_r23 1360 +#define _LOAD_FAST_BORROW_r01 1361 +#define _LOAD_FAST_BORROW_r12 1362 +#define _LOAD_FAST_BORROW_r23 1363 +#define _LOAD_FAST_BORROW_0_r01 1364 +#define _LOAD_FAST_BORROW_0_r12 1365 +#define _LOAD_FAST_BORROW_0_r23 1366 +#define _LOAD_FAST_BORROW_1_r01 1367 +#define _LOAD_FAST_BORROW_1_r12 1368 +#define _LOAD_FAST_BORROW_1_r23 1369 +#define _LOAD_FAST_BORROW_2_r01 1370 +#define _LOAD_FAST_BORROW_2_r12 1371 +#define _LOAD_FAST_BORROW_2_r23 1372 +#define _LOAD_FAST_BORROW_3_r01 1373 +#define _LOAD_FAST_BORROW_3_r12 1374 +#define _LOAD_FAST_BORROW_3_r23 1375 +#define _LOAD_FAST_BORROW_4_r01 1376 +#define _LOAD_FAST_BORROW_4_r12 1377 +#define _LOAD_FAST_BORROW_4_r23 1378 +#define _LOAD_FAST_BORROW_5_r01 1379 +#define _LOAD_FAST_BORROW_5_r12 1380 +#define _LOAD_FAST_BORROW_5_r23 1381 +#define _LOAD_FAST_BORROW_6_r01 1382 +#define _LOAD_FAST_BORROW_6_r12 1383 +#define _LOAD_FAST_BORROW_6_r23 1384 +#define _LOAD_FAST_BORROW_7_r01 1385 +#define _LOAD_FAST_BORROW_7_r12 1386 +#define _LOAD_FAST_BORROW_7_r23 1387 +#define _LOAD_FAST_BORROW_LOAD_FAST_BORROW_r02 1388 +#define _LOAD_FAST_BORROW_LOAD_FAST_BORROW_r13 1389 +#define _LOAD_FAST_CHECK_r01 1390 +#define _LOAD_FAST_CHECK_r12 1391 +#define _LOAD_FAST_CHECK_r23 1392 +#define _LOAD_FAST_LOAD_FAST_r02 1393 +#define _LOAD_FAST_LOAD_FAST_r13 1394 +#define _LOAD_FROM_DICT_OR_DEREF_r11 1395 +#define _LOAD_FROM_DICT_OR_GLOBALS_r11 1396 +#define _LOAD_GLOBAL_r00 1397 +#define _LOAD_GLOBAL_BUILTINS_r01 1398 +#define _LOAD_GLOBAL_MODULE_r01 1399 +#define _LOAD_LOCALS_r01 1400 +#define _LOAD_LOCALS_r12 1401 +#define _LOAD_LOCALS_r23 1402 +#define _LOAD_NAME_r01 1403 +#define _LOAD_SMALL_INT_r01 1404 +#define _LOAD_SMALL_INT_r12 1405 +#define _LOAD_SMALL_INT_r23 1406 +#define _LOAD_SMALL_INT_0_r01 1407 +#define _LOAD_SMALL_INT_0_r12 1408 +#define _LOAD_SMALL_INT_0_r23 1409 +#define _LOAD_SMALL_INT_1_r01 1410 +#define _LOAD_SMALL_INT_1_r12 1411 +#define _LOAD_SMALL_INT_1_r23 1412 +#define _LOAD_SMALL_INT_2_r01 1413 +#define _LOAD_SMALL_INT_2_r12 1414 +#define _LOAD_SMALL_INT_2_r23 1415 +#define _LOAD_SMALL_INT_3_r01 1416 +#define _LOAD_SMALL_INT_3_r12 1417 +#define _LOAD_SMALL_INT_3_r23 1418 +#define _LOAD_SPECIAL_r00 1419 +#define _LOAD_SUPER_ATTR_ATTR_r31 1420 +#define _LOAD_SUPER_ATTR_METHOD_r32 1421 +#define _LOCK_OBJECT_r01 1422 +#define _LOCK_OBJECT_r11 1423 +#define _LOCK_OBJECT_r22 1424 +#define _LOCK_OBJECT_r33 1425 +#define _MAKE_CALLARGS_A_TUPLE_r33 1426 +#define _MAKE_CELL_r00 1427 +#define _MAKE_FUNCTION_r12 1428 +#define _MAKE_HEAP_SAFE_r01 1429 +#define _MAKE_HEAP_SAFE_r11 1430 +#define _MAKE_HEAP_SAFE_r22 1431 +#define _MAKE_HEAP_SAFE_r33 1432 +#define _MAKE_WARM_r00 1433 +#define _MAKE_WARM_r11 1434 +#define _MAKE_WARM_r22 1435 +#define _MAKE_WARM_r33 1436 +#define _MAP_ADD_r20 1437 +#define _MATCH_CLASS_r33 1438 +#define _MATCH_KEYS_r23 1439 +#define _MATCH_MAPPING_r02 1440 +#define _MATCH_MAPPING_r12 1441 +#define _MATCH_MAPPING_r23 1442 +#define _MATCH_SEQUENCE_r02 1443 +#define _MATCH_SEQUENCE_r12 1444 +#define _MATCH_SEQUENCE_r23 1445 +#define _MAYBE_EXPAND_METHOD_r00 1446 +#define _MAYBE_EXPAND_METHOD_KW_r11 1447 +#define _MONITOR_CALL_r00 1448 +#define _MONITOR_CALL_KW_r11 1449 +#define _MONITOR_JUMP_BACKWARD_r00 1450 +#define _MONITOR_JUMP_BACKWARD_r11 1451 +#define _MONITOR_JUMP_BACKWARD_r22 1452 +#define _MONITOR_JUMP_BACKWARD_r33 1453 +#define _MONITOR_RESUME_r00 1454 +#define _NOP_r00 1455 +#define _NOP_r11 1456 +#define _NOP_r22 1457 +#define _NOP_r33 1458 +#define _POP_EXCEPT_r10 1459 +#define _POP_ITER_r20 1460 +#define _POP_JUMP_IF_FALSE_r00 1461 +#define _POP_JUMP_IF_FALSE_r10 1462 +#define _POP_JUMP_IF_FALSE_r21 1463 +#define _POP_JUMP_IF_FALSE_r32 1464 +#define _POP_JUMP_IF_TRUE_r00 1465 +#define _POP_JUMP_IF_TRUE_r10 1466 +#define _POP_JUMP_IF_TRUE_r21 1467 +#define _POP_JUMP_IF_TRUE_r32 1468 +#define _POP_TOP_r10 1469 +#define _POP_TOP_FLOAT_r00 1470 +#define _POP_TOP_FLOAT_r10 1471 +#define _POP_TOP_FLOAT_r21 1472 +#define _POP_TOP_FLOAT_r32 1473 +#define _POP_TOP_INT_r00 1474 +#define _POP_TOP_INT_r10 1475 +#define _POP_TOP_INT_r21 1476 +#define _POP_TOP_INT_r32 1477 +#define _POP_TOP_NOP_r00 1478 +#define _POP_TOP_NOP_r10 1479 +#define _POP_TOP_NOP_r21 1480 +#define _POP_TOP_NOP_r32 1481 +#define _POP_TOP_OPARG_r00 1482 +#define _POP_TOP_UNICODE_r00 1483 +#define _POP_TOP_UNICODE_r10 1484 +#define _POP_TOP_UNICODE_r21 1485 +#define _POP_TOP_UNICODE_r32 1486 +#define _PUSH_EXC_INFO_r02 1487 +#define _PUSH_EXC_INFO_r12 1488 +#define _PUSH_EXC_INFO_r23 1489 +#define _PUSH_FRAME_r10 1490 +#define _PUSH_NULL_r01 1491 +#define _PUSH_NULL_r12 1492 +#define _PUSH_NULL_r23 1493 +#define _PUSH_NULL_CONDITIONAL_r00 1494 +#define _PUSH_TAGGED_ZERO_r01 1495 +#define _PUSH_TAGGED_ZERO_r12 1496 +#define _PUSH_TAGGED_ZERO_r23 1497 +#define _PY_FRAME_EX_r31 1498 +#define _PY_FRAME_GENERAL_r01 1499 +#define _PY_FRAME_KW_r11 1500 +#define _REPLACE_WITH_TRUE_r02 1501 +#define _REPLACE_WITH_TRUE_r12 1502 +#define _REPLACE_WITH_TRUE_r23 1503 +#define _RESUME_CHECK_r00 1504 +#define _RESUME_CHECK_r11 1505 +#define _RESUME_CHECK_r22 1506 +#define _RESUME_CHECK_r33 1507 +#define _RETURN_GENERATOR_r01 1508 +#define _RETURN_VALUE_r11 1509 +#define _RROT_3_r03 1510 +#define _RROT_3_r13 1511 +#define _RROT_3_r23 1512 +#define _RROT_3_r33 1513 +#define _SAVE_RETURN_OFFSET_r00 1514 +#define _SAVE_RETURN_OFFSET_r11 1515 +#define _SAVE_RETURN_OFFSET_r22 1516 +#define _SAVE_RETURN_OFFSET_r33 1517 +#define _SEND_ASYNC_GEN_r33 1518 +#define _SEND_ASYNC_GEN_TIER_TWO_r33 1519 +#define _SEND_GEN_FRAME_r33 1520 +#define _SEND_VIRTUAL_r33 1521 +#define _SEND_VIRTUAL_TIER_TWO_r03 1522 +#define _SEND_VIRTUAL_TIER_TWO_r13 1523 +#define _SEND_VIRTUAL_TIER_TWO_r23 1524 +#define _SEND_VIRTUAL_TIER_TWO_r33 1525 +#define _SETUP_ANNOTATIONS_r00 1526 +#define _SET_ADD_r10 1527 +#define _SET_FUNCTION_ATTRIBUTE_r01 1528 +#define _SET_FUNCTION_ATTRIBUTE_r11 1529 +#define _SET_FUNCTION_ATTRIBUTE_r21 1530 +#define _SET_FUNCTION_ATTRIBUTE_r32 1531 +#define _SET_IP_r00 1532 +#define _SET_IP_r11 1533 +#define _SET_IP_r22 1534 +#define _SET_IP_r33 1535 +#define _SET_UPDATE_r11 1536 +#define _SPILL_OR_RELOAD_r01 1537 +#define _SPILL_OR_RELOAD_r02 1538 +#define _SPILL_OR_RELOAD_r03 1539 +#define _SPILL_OR_RELOAD_r10 1540 +#define _SPILL_OR_RELOAD_r12 1541 +#define _SPILL_OR_RELOAD_r13 1542 +#define _SPILL_OR_RELOAD_r20 1543 +#define _SPILL_OR_RELOAD_r21 1544 +#define _SPILL_OR_RELOAD_r23 1545 +#define _SPILL_OR_RELOAD_r30 1546 +#define _SPILL_OR_RELOAD_r31 1547 +#define _SPILL_OR_RELOAD_r32 1548 +#define _START_EXECUTOR_r00 1549 +#define _STORE_ATTR_r20 1550 +#define _STORE_ATTR_INSTANCE_VALUE_r21 1551 +#define _STORE_ATTR_SLOT_r21 1552 +#define _STORE_ATTR_WITH_HINT_r21 1553 +#define _STORE_DEREF_r10 1554 +#define _STORE_FAST_LOAD_FAST_r11 1555 +#define _STORE_FAST_STORE_FAST_r20 1556 +#define _STORE_GLOBAL_r10 1557 +#define _STORE_NAME_r10 1558 +#define _STORE_SLICE_r30 1559 +#define _STORE_SUBSCR_r30 1560 +#define _STORE_SUBSCR_DICT_r31 1561 +#define _STORE_SUBSCR_DICT_KNOWN_HASH_r31 1562 +#define _STORE_SUBSCR_LIST_INT_r32 1563 +#define _SWAP_r11 1564 +#define _SWAP_2_r02 1565 +#define _SWAP_2_r12 1566 +#define _SWAP_2_r22 1567 +#define _SWAP_2_r33 1568 +#define _SWAP_3_r03 1569 +#define _SWAP_3_r13 1570 +#define _SWAP_3_r23 1571 +#define _SWAP_3_r33 1572 +#define _SWAP_FAST_r01 1573 +#define _SWAP_FAST_r11 1574 +#define _SWAP_FAST_r22 1575 +#define _SWAP_FAST_r33 1576 +#define _SWAP_FAST_0_r01 1577 +#define _SWAP_FAST_0_r11 1578 +#define _SWAP_FAST_0_r22 1579 +#define _SWAP_FAST_0_r33 1580 +#define _SWAP_FAST_1_r01 1581 +#define _SWAP_FAST_1_r11 1582 +#define _SWAP_FAST_1_r22 1583 +#define _SWAP_FAST_1_r33 1584 +#define _SWAP_FAST_2_r01 1585 +#define _SWAP_FAST_2_r11 1586 +#define _SWAP_FAST_2_r22 1587 +#define _SWAP_FAST_2_r33 1588 +#define _SWAP_FAST_3_r01 1589 +#define _SWAP_FAST_3_r11 1590 +#define _SWAP_FAST_3_r22 1591 +#define _SWAP_FAST_3_r33 1592 +#define _SWAP_FAST_4_r01 1593 +#define _SWAP_FAST_4_r11 1594 +#define _SWAP_FAST_4_r22 1595 +#define _SWAP_FAST_4_r33 1596 +#define _SWAP_FAST_5_r01 1597 +#define _SWAP_FAST_5_r11 1598 +#define _SWAP_FAST_5_r22 1599 +#define _SWAP_FAST_5_r33 1600 +#define _SWAP_FAST_6_r01 1601 +#define _SWAP_FAST_6_r11 1602 +#define _SWAP_FAST_6_r22 1603 +#define _SWAP_FAST_6_r33 1604 +#define _SWAP_FAST_7_r01 1605 +#define _SWAP_FAST_7_r11 1606 +#define _SWAP_FAST_7_r22 1607 +#define _SWAP_FAST_7_r33 1608 +#define _TIER2_RESUME_CHECK_r00 1609 +#define _TIER2_RESUME_CHECK_r11 1610 +#define _TIER2_RESUME_CHECK_r22 1611 +#define _TIER2_RESUME_CHECK_r33 1612 +#define _TO_BOOL_r11 1613 +#define _TO_BOOL_BOOL_r01 1614 +#define _TO_BOOL_BOOL_r11 1615 +#define _TO_BOOL_BOOL_r22 1616 +#define _TO_BOOL_BOOL_r33 1617 +#define _TO_BOOL_INT_r02 1618 +#define _TO_BOOL_INT_r12 1619 +#define _TO_BOOL_INT_r23 1620 +#define _TO_BOOL_LIST_r02 1621 +#define _TO_BOOL_LIST_r12 1622 +#define _TO_BOOL_LIST_r23 1623 +#define _TO_BOOL_NONE_r01 1624 +#define _TO_BOOL_NONE_r11 1625 +#define _TO_BOOL_NONE_r22 1626 +#define _TO_BOOL_NONE_r33 1627 +#define _TO_BOOL_STR_r02 1628 +#define _TO_BOOL_STR_r12 1629 +#define _TO_BOOL_STR_r23 1630 +#define _TRACE_RECORD_r00 1631 +#define _UNARY_INVERT_r12 1632 +#define _UNARY_NEGATIVE_r12 1633 +#define _UNARY_NEGATIVE_FLOAT_INPLACE_r02 1634 +#define _UNARY_NEGATIVE_FLOAT_INPLACE_r12 1635 +#define _UNARY_NEGATIVE_FLOAT_INPLACE_r23 1636 +#define _UNARY_NOT_r01 1637 +#define _UNARY_NOT_r11 1638 +#define _UNARY_NOT_r22 1639 +#define _UNARY_NOT_r33 1640 +#define _UNPACK_EX_r10 1641 +#define _UNPACK_SEQUENCE_r10 1642 +#define _UNPACK_SEQUENCE_LIST_r10 1643 +#define _UNPACK_SEQUENCE_TUPLE_r10 1644 +#define _UNPACK_SEQUENCE_TWO_TUPLE_r12 1645 +#define _UNPACK_SEQUENCE_UNIQUE_THREE_TUPLE_r03 1646 +#define _UNPACK_SEQUENCE_UNIQUE_THREE_TUPLE_r13 1647 +#define _UNPACK_SEQUENCE_UNIQUE_TUPLE_r10 1648 +#define _UNPACK_SEQUENCE_UNIQUE_TWO_TUPLE_r02 1649 +#define _UNPACK_SEQUENCE_UNIQUE_TWO_TUPLE_r12 1650 +#define _UNPACK_SEQUENCE_UNIQUE_TWO_TUPLE_r23 1651 +#define _WITH_EXCEPT_START_r33 1652 +#define _YIELD_VALUE_r11 1653 +#define MAX_UOP_REGS_ID 1653 #ifdef __cplusplus } diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index 6713e9bc95f942d..45f407dae3ddf57 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -287,7 +287,6 @@ const uint32_t _PyUop_Flags[MAX_UOP_ID+1] = { [_WITH_EXCEPT_START] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_PUSH_EXC_INFO] = 0, [_GUARD_DORV_VALUES_INST_ATTR_FROM_DICT] = HAS_EXIT_FLAG, - [_GUARD_KEYS_VERSION] = HAS_EXIT_FLAG, [_LOAD_ATTR_METHOD_WITH_VALUES] = HAS_ARG_FLAG, [_LOAD_ATTR_METHOD_NO_DICT] = HAS_ARG_FLAG, [_LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG, @@ -2717,15 +2716,6 @@ const _PyUopCachingInfo _PyUop_Caching[MAX_UOP_ID+1] = { { 3, 3, _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_r33 }, }, }, - [_GUARD_KEYS_VERSION] = { - .best = { 0, 1, 2, 3 }, - .entries = { - { 1, 0, _GUARD_KEYS_VERSION_r01 }, - { 1, 1, _GUARD_KEYS_VERSION_r11 }, - { 2, 2, _GUARD_KEYS_VERSION_r22 }, - { 3, 3, _GUARD_KEYS_VERSION_r33 }, - }, - }, [_LOAD_ATTR_METHOD_WITH_VALUES] = { .best = { 0, 1, 2, 2 }, .entries = { @@ -4543,10 +4533,6 @@ const uint16_t _PyUop_Uncached[MAX_UOP_REGS_ID+1] = { [_GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_r11] = _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT, [_GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_r22] = _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT, [_GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_r33] = _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT, - [_GUARD_KEYS_VERSION_r01] = _GUARD_KEYS_VERSION, - [_GUARD_KEYS_VERSION_r11] = _GUARD_KEYS_VERSION, - [_GUARD_KEYS_VERSION_r22] = _GUARD_KEYS_VERSION, - [_GUARD_KEYS_VERSION_r33] = _GUARD_KEYS_VERSION, [_LOAD_ATTR_METHOD_WITH_VALUES_r02] = _LOAD_ATTR_METHOD_WITH_VALUES, [_LOAD_ATTR_METHOD_WITH_VALUES_r12] = _LOAD_ATTR_METHOD_WITH_VALUES, [_LOAD_ATTR_METHOD_WITH_VALUES_r23] = _LOAD_ATTR_METHOD_WITH_VALUES, @@ -5473,11 +5459,6 @@ const char *const _PyOpcode_uop_name[MAX_UOP_REGS_ID+1] = { [_GUARD_ITER_VIRTUAL_r11] = "_GUARD_ITER_VIRTUAL_r11", [_GUARD_ITER_VIRTUAL_r22] = "_GUARD_ITER_VIRTUAL_r22", [_GUARD_ITER_VIRTUAL_r33] = "_GUARD_ITER_VIRTUAL_r33", - [_GUARD_KEYS_VERSION] = "_GUARD_KEYS_VERSION", - [_GUARD_KEYS_VERSION_r01] = "_GUARD_KEYS_VERSION_r01", - [_GUARD_KEYS_VERSION_r11] = "_GUARD_KEYS_VERSION_r11", - [_GUARD_KEYS_VERSION_r22] = "_GUARD_KEYS_VERSION_r22", - [_GUARD_KEYS_VERSION_r33] = "_GUARD_KEYS_VERSION_r33", [_GUARD_LOAD_SUPER_ATTR_METHOD] = "_GUARD_LOAD_SUPER_ATTR_METHOD", [_GUARD_LOAD_SUPER_ATTR_METHOD_r03] = "_GUARD_LOAD_SUPER_ATTR_METHOD_r03", [_GUARD_LOAD_SUPER_ATTR_METHOD_r13] = "_GUARD_LOAD_SUPER_ATTR_METHOD_r13", @@ -6728,8 +6709,6 @@ int _PyUop_num_popped(int opcode, int oparg) return 1; case _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT: return 0; - case _GUARD_KEYS_VERSION: - return 0; case _LOAD_ATTR_METHOD_WITH_VALUES: return 1; case _LOAD_ATTR_METHOD_NO_DICT: diff --git a/Modules/_testinternalcapi/test_cases.c.h b/Modules/_testinternalcapi/test_cases.c.h index 11dfcc68eb2dacd..503f566c9ae86a5 100644 --- a/Modules/_testinternalcapi/test_cases.c.h +++ b/Modules/_testinternalcapi/test_cases.c.h @@ -8773,18 +8773,7 @@ JUMP_TO_PREDICTED(LOAD_ATTR); } } - // _GUARD_KEYS_VERSION - { - uint32_t keys_version = read_u32(&this_instr[4].cache); - PyTypeObject *owner_cls = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); - PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls; - PyDictKeysObject *keys = owner_heap_type->ht_cached_keys; - if (FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != keys_version) { - UPDATE_MISS_STATS(LOAD_ATTR); - assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); - JUMP_TO_PREDICTED(LOAD_ATTR); - } - } + /* Skip 2 cache entries */ // _LOAD_ATTR_METHOD_WITH_VALUES { PyObject *descr = read_obj(&this_instr[6].cache); @@ -8967,18 +8956,7 @@ JUMP_TO_PREDICTED(LOAD_ATTR); } } - // _GUARD_KEYS_VERSION - { - uint32_t keys_version = read_u32(&this_instr[4].cache); - PyTypeObject *owner_cls = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); - PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls; - PyDictKeysObject *keys = owner_heap_type->ht_cached_keys; - if (FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != keys_version) { - UPDATE_MISS_STATS(LOAD_ATTR); - assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); - JUMP_TO_PREDICTED(LOAD_ATTR); - } - } + /* Skip 2 cache entries */ // _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES { PyObject *descr = read_obj(&this_instr[6].cache); diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 25dc81f726f59cb..0a6d0f9199641ee 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -784,16 +784,12 @@ _PyDict_CheckConsistency(PyObject *op, int check_content) } -static PyDictKeysObject* -new_keys_object(uint8_t log2_size, bool unicode) +static inline int +get_log2_bytes(uint8_t log2_size) { - Py_ssize_t usable; int log2_bytes; - size_t entry_size = unicode ? sizeof(PyDictUnicodeEntry) : sizeof(PyDictKeyEntry); - assert(log2_size >= PyDict_LOG_MINSIZE); - usable = USABLE_FRACTION((size_t)1<dk_refcnt = 1; dk->dk_log2_size = log2_size; dk->dk_log2_index_bytes = log2_bytes; - dk->dk_kind = unicode ? DICT_KEYS_UNICODE : DICT_KEYS_GENERAL; + dk->dk_kind = kind; #ifdef Py_GIL_DISABLED dk->dk_mutex = (PyMutex){0}; #endif @@ -837,15 +827,51 @@ new_keys_object(uint8_t log2_size, bool unicode) dk->dk_version = 0; memset(&dk->dk_indices[0], 0xff, ((size_t)1 << log2_bytes)); memset(&dk->dk_indices[(size_t)1 << log2_bytes], 0, entry_size * usable); +} + +static PyDictKeysObject* +new_keys_object(uint8_t log2_size, bool unicode) +{ + Py_ssize_t usable = USABLE_FRACTION((size_t)1<dk_kind == DICT_KEYS_SPLIT) { + ptr = _PyDictKeys_AsSharedKeys(keys); +#ifdef Py_GIL_DISABLED + size += offsetof(struct _instancekeysobject, dsk_keys); +#endif + } #ifdef Py_GIL_DISABLED if (use_qsbr) { - _PyMem_FreeDelayed(keys, _PyDict_KeysSize(keys)); + _PyMem_FreeDelayed(ptr, size); return; } #endif @@ -853,7 +879,7 @@ free_keys_object(PyDictKeysObject *keys, bool use_qsbr) _Py_FREELIST_FREE(dictkeys, keys, PyMem_Free); } else { - PyMem_Free(keys); + PyMem_Free(ptr); } } @@ -1901,6 +1927,7 @@ insert_split_key(PyDictKeysObject *keys, PyObject *key, Py_hash_t hash) if (ix == DKIX_EMPTY && keys->dk_usable > 0) { // Insert into new slot FT_ATOMIC_STORE_UINT32_RELAXED(keys->dk_version, 0); + _PyDict_SplitKeysInvalidated(keys); Py_ssize_t hashpos = find_empty_slot(keys, hash); ix = keys->dk_nentries; dictkeys_set_index(keys, hashpos, ix); @@ -7099,16 +7126,24 @@ dictvalues_reversed(PyObject *self, PyObject *Py_UNUSED(ignored)) PyDictKeysObject * _PyDict_NewKeysForClass(PyHeapTypeObject *cls) { - PyDictKeysObject *keys = new_keys_object(NEXT_LOG2_SHARED_KEYS_MAX_SIZE, 1); - if (keys == NULL) { + int log2_bytes = get_log2_bytes(NEXT_LOG2_SHARED_KEYS_MAX_SIZE); + Py_ssize_t usable = USABLE_FRACTION((size_t)1<dk_nentries == 0); - /* Set to max size+1 as it will shrink by one before each new object */ - keys->dk_usable = SHARED_KEYS_MAX_SIZE; - keys->dk_kind = DICT_KEYS_SPLIT; - } + + shared_keys->dsk_owning_type = (PyTypeObject *)cls; + PyDictKeysObject* keys = &shared_keys->dsk_keys; + init_keys_object(keys, NEXT_LOG2_SHARED_KEYS_MAX_SIZE, log2_bytes, DICT_KEYS_SPLIT, + SHARED_KEYS_MAX_SIZE, sizeof(PyDictUnicodeEntry)); + assert(keys->dk_nentries == 0); + /* Set to max size+1 as it will shrink by one before each new object */ if (cls->ht_type.tp_dict) { PyObject *attrs = PyDict_GetItem(cls->ht_type.tp_dict, &_Py_ID(__static_attributes__)); if (attrs != NULL && PyTuple_Check(attrs)) { @@ -7126,6 +7161,25 @@ _PyDict_NewKeysForClass(PyHeapTypeObject *cls) return keys; } +void +_PyDict_RemoveKeysForClass(PyHeapTypeObject *cls) +{ + struct _instancekeysobject *shared_keys = _PyDictKeys_AsSharedKeys(cls->ht_cached_keys); + FT_ATOMIC_STORE_PTR_RELEASE(shared_keys->dsk_owning_type, NULL); + + _PyDictKeys_DecRef(cls->ht_cached_keys); +} + +void +_PyDict_SplitKeysInvalidated(PyDictKeysObject* keys) +{ + struct _instancekeysobject *shared_keys = _PyDictKeys_AsSharedKeys(keys); + PyTypeObject *type = FT_ATOMIC_LOAD_PTR_ACQUIRE(shared_keys->dsk_owning_type); + if (type) { + PyType_Modified(type); + } +} + void _PyObject_InitInlineValues(PyObject *obj, PyTypeObject *tp) { diff --git a/Objects/typeobject.c b/Objects/typeobject.c index de5bb5f1f2a0df0..388e8891c9c7399 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -7023,7 +7023,7 @@ type_dealloc(PyObject *self) Py_XDECREF(et->ht_qualname); Py_XDECREF(et->ht_slots); if (et->ht_cached_keys) { - _PyDictKeys_DecRef(et->ht_cached_keys); + _PyDict_RemoveKeysForClass(et); } Py_XDECREF(et->ht_module); PyMem_Free(et->_ht_tpname); diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 993d231751409ba..e368092b300f864 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -4265,13 +4265,6 @@ dummy_func( EXIT_IF(!FT_ATOMIC_LOAD_UINT8(ivs->valid)); } - op(_GUARD_KEYS_VERSION, (keys_version/2, owner -- owner)) { - PyTypeObject *owner_cls = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); - PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls; - PyDictKeysObject *keys = owner_heap_type->ht_cached_keys; - EXIT_IF(FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != keys_version); - } - op(_LOAD_ATTR_METHOD_WITH_VALUES, (descr/4, owner -- attr, self)) { assert(oparg & 1); /* Cached method object */ @@ -4288,7 +4281,7 @@ dummy_func( _RECORD_TOS_TYPE + _GUARD_TYPE_VERSION + _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT + - _GUARD_KEYS_VERSION + + unused/2 + _LOAD_ATTR_METHOD_WITH_VALUES; op(_LOAD_ATTR_METHOD_NO_DICT, (descr/4, owner -- attr, self)) { @@ -4322,7 +4315,7 @@ dummy_func( _RECORD_TOS_TYPE + _GUARD_TYPE_VERSION + _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT + - _GUARD_KEYS_VERSION + + unused/2 + _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES; op(_LOAD_ATTR_NONDESCRIPTOR_NO_DICT, (descr/4, owner -- attr)) { diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 3d27fd6709013bf..7973c75e1a60ad2 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -16231,103 +16231,6 @@ break; } - case _GUARD_KEYS_VERSION_r01: { - CHECK_CURRENT_CACHED_VALUES(0); - ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); - _PyStackRef owner; - owner = stack_pointer[-1]; - uint32_t keys_version = (uint32_t)CURRENT_OPERAND0_32(); - PyTypeObject *owner_cls = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); - PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls; - PyDictKeysObject *keys = owner_heap_type->ht_cached_keys; - if (FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != keys_version) { - UOP_STAT_INC(uopcode, miss); - SET_CURRENT_CACHED_VALUES(0); - JUMP_TO_JUMP_TARGET(); - } - _tos_cache0 = owner; - SET_CURRENT_CACHED_VALUES(1); - stack_pointer += -1; - ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); - ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); - break; - } - - case _GUARD_KEYS_VERSION_r11: { - CHECK_CURRENT_CACHED_VALUES(1); - ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); - _PyStackRef owner; - _PyStackRef _stack_item_0 = _tos_cache0; - owner = _stack_item_0; - uint32_t keys_version = (uint32_t)CURRENT_OPERAND0_32(); - PyTypeObject *owner_cls = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); - PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls; - PyDictKeysObject *keys = owner_heap_type->ht_cached_keys; - if (FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != keys_version) { - UOP_STAT_INC(uopcode, miss); - _tos_cache0 = owner; - SET_CURRENT_CACHED_VALUES(1); - JUMP_TO_JUMP_TARGET(); - } - _tos_cache0 = owner; - SET_CURRENT_CACHED_VALUES(1); - ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); - break; - } - - case _GUARD_KEYS_VERSION_r22: { - CHECK_CURRENT_CACHED_VALUES(2); - ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); - _PyStackRef owner; - _PyStackRef _stack_item_0 = _tos_cache0; - _PyStackRef _stack_item_1 = _tos_cache1; - owner = _stack_item_1; - uint32_t keys_version = (uint32_t)CURRENT_OPERAND0_32(); - PyTypeObject *owner_cls = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); - PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls; - PyDictKeysObject *keys = owner_heap_type->ht_cached_keys; - if (FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != keys_version) { - UOP_STAT_INC(uopcode, miss); - _tos_cache1 = owner; - _tos_cache0 = _stack_item_0; - SET_CURRENT_CACHED_VALUES(2); - JUMP_TO_JUMP_TARGET(); - } - _tos_cache1 = owner; - _tos_cache0 = _stack_item_0; - SET_CURRENT_CACHED_VALUES(2); - ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); - break; - } - - case _GUARD_KEYS_VERSION_r33: { - CHECK_CURRENT_CACHED_VALUES(3); - ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); - _PyStackRef owner; - _PyStackRef _stack_item_0 = _tos_cache0; - _PyStackRef _stack_item_1 = _tos_cache1; - _PyStackRef _stack_item_2 = _tos_cache2; - owner = _stack_item_2; - uint32_t keys_version = (uint32_t)CURRENT_OPERAND0_32(); - PyTypeObject *owner_cls = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); - PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls; - PyDictKeysObject *keys = owner_heap_type->ht_cached_keys; - if (FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != keys_version) { - UOP_STAT_INC(uopcode, miss); - _tos_cache2 = owner; - _tos_cache1 = _stack_item_1; - _tos_cache0 = _stack_item_0; - SET_CURRENT_CACHED_VALUES(3); - JUMP_TO_JUMP_TARGET(); - } - _tos_cache2 = owner; - _tos_cache1 = _stack_item_1; - _tos_cache0 = _stack_item_0; - SET_CURRENT_CACHED_VALUES(3); - ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); - break; - } - case _LOAD_ATTR_METHOD_WITH_VALUES_r02: { CHECK_CURRENT_CACHED_VALUES(0); ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 94384d5db3c107f..5adcdcb4521baf5 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -8772,18 +8772,7 @@ JUMP_TO_PREDICTED(LOAD_ATTR); } } - // _GUARD_KEYS_VERSION - { - uint32_t keys_version = read_u32(&this_instr[4].cache); - PyTypeObject *owner_cls = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); - PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls; - PyDictKeysObject *keys = owner_heap_type->ht_cached_keys; - if (FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != keys_version) { - UPDATE_MISS_STATS(LOAD_ATTR); - assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); - JUMP_TO_PREDICTED(LOAD_ATTR); - } - } + /* Skip 2 cache entries */ // _LOAD_ATTR_METHOD_WITH_VALUES { PyObject *descr = read_obj(&this_instr[6].cache); @@ -8966,18 +8955,7 @@ JUMP_TO_PREDICTED(LOAD_ATTR); } } - // _GUARD_KEYS_VERSION - { - uint32_t keys_version = read_u32(&this_instr[4].cache); - PyTypeObject *owner_cls = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); - PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls; - PyDictKeysObject *keys = owner_heap_type->ht_cached_keys; - if (FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != keys_version) { - UPDATE_MISS_STATS(LOAD_ATTR); - assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); - JUMP_TO_PREDICTED(LOAD_ATTR); - } - } + /* Skip 2 cache entries */ // _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES { PyObject *descr = read_obj(&this_instr[6].cache); diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 8895e02d47b1693..102b95279514141 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -3941,10 +3941,6 @@ break; } - case _GUARD_KEYS_VERSION: { - break; - } - case _LOAD_ATTR_METHOD_WITH_VALUES: { JitOptRef owner; JitOptRef attr; diff --git a/Python/specialize.c b/Python/specialize.c index 2ff0a9d0072cec3..04aeb4a76d4046a 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -1285,7 +1285,6 @@ specialize_attr_loadclassattr(PyObject *owner, _Py_CODEUNIT *instr, SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OUT_OF_VERSIONS); return 0; } - write_u32(cache->keys_version, shared_keys_version); specialize(instr, is_method ? LOAD_ATTR_METHOD_WITH_VALUES : LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES); } else { From 67ee8cd4f855700f1d66e293f5bb9136ba690e1e Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Thu, 11 Jun 2026 18:38:49 +0200 Subject: [PATCH 132/185] gh-150942: Speed up json.loads array and object decoding (GH-150945) Append parsed values to the result list with _PyList_AppendTakeRef and insert key/value pairs with _PyDict_SetItem_Take2, which take ownership of the references instead of incref-ing on insert and then decref-ing the local. This removes a reference-count round-trip per element (and, on the free-threaded build, a per-append lock). Co-authored-by: Claude Opus 4.8 (1M context) --- ...05-31-10-00-00.gh-issue-150942.Tk9aRef.rst | 3 ++ Modules/_json.c | 37 +++++++++---------- 2 files changed, 21 insertions(+), 19 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-05-31-10-00-00.gh-issue-150942.Tk9aRef.rst diff --git a/Misc/NEWS.d/next/Library/2026-05-31-10-00-00.gh-issue-150942.Tk9aRef.rst b/Misc/NEWS.d/next/Library/2026-05-31-10-00-00.gh-issue-150942.Tk9aRef.rst new file mode 100644 index 000000000000000..ba0de8fd434d303 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-31-10-00-00.gh-issue-150942.Tk9aRef.rst @@ -0,0 +1,3 @@ +Speed up :func:`json.loads` decoding of arrays and objects by storing +parsed values into the result list/dict without an extra reference-count +round-trip (using the internal reference-stealing append/insert helpers). diff --git a/Modules/_json.c b/Modules/_json.c index 6c4f38834631d30..b057b56b2f9f8d9 100644 --- a/Modules/_json.c +++ b/Modules/_json.c @@ -11,6 +11,8 @@ #include "Python.h" #include "pycore_ceval.h" // _Py_EnterRecursiveCall() #include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION_SEQUENCE_FAST() +#include "pycore_dict.h" // _PyDict_SetItem_Take2() +#include "pycore_list.h" // _PyList_AppendTakeRef() #include "pycore_global_strings.h" // _Py_ID() #include "pycore_pyerrors.h" // _PyErr_FormatNote #include "pycore_runtime.h" // _PyRuntime @@ -752,7 +754,6 @@ _parse_object_unicode(PyScannerObject *s, PyObject *memo, PyObject *pystr, Py_ss const void *str; int kind; Py_ssize_t end_idx; - PyObject *val = NULL; PyObject *rval = NULL; PyObject *key = NULL; int has_pairs_hook = (s->object_pairs_hook != Py_None); @@ -802,13 +803,16 @@ _parse_object_unicode(PyScannerObject *s, PyObject *memo, PyObject *pystr, Py_ss while (idx <= end_idx && IS_WHITESPACE(PyUnicode_READ(kind, str, idx))) idx++; /* read any JSON term */ - val = scan_once_unicode(s, memo, pystr, idx, &next_idx); + PyObject *val = scan_once_unicode(s, memo, pystr, idx, &next_idx); if (val == NULL) goto bail; + /* The steal below takes our references to both key and val + (releasing them on failure). Only key is reset for the bail + path; val is never live there, so it needs no cleanup. */ if (has_pairs_hook) { PyObject *item = _PyTuple_FromPairSteal(key, val); - key = val = NULL; + key = NULL; if (item == NULL) goto bail; if (PyList_Append(rval, item) == -1) { @@ -818,10 +822,10 @@ _parse_object_unicode(PyScannerObject *s, PyObject *memo, PyObject *pystr, Py_ss Py_DECREF(item); } else { - if (PyDict_SetItem(rval, key, val) < 0) + int err = _PyDict_SetItem_Take2((PyDictObject *)rval, key, val); + key = NULL; + if (err < 0) goto bail; - Py_CLEAR(key); - Py_CLEAR(val); } idx = next_idx; @@ -851,21 +855,20 @@ _parse_object_unicode(PyScannerObject *s, PyObject *memo, PyObject *pystr, Py_ss *next_idx_ptr = idx + 1; if (has_pairs_hook) { - val = PyObject_CallOneArg(s->object_pairs_hook, rval); + PyObject *res = PyObject_CallOneArg(s->object_pairs_hook, rval); Py_DECREF(rval); - return val; + return res; } /* if object_hook is not None: rval = object_hook(rval) */ if (s->object_hook != Py_None) { - val = PyObject_CallOneArg(s->object_hook, rval); + PyObject *res = PyObject_CallOneArg(s->object_hook, rval); Py_DECREF(rval); - return val; + return res; } return rval; bail: Py_XDECREF(key); - Py_XDECREF(val); Py_XDECREF(rval); return NULL; } @@ -882,7 +885,6 @@ _parse_array_unicode(PyScannerObject *s, PyObject *memo, PyObject *pystr, Py_ssi const void *str; int kind; Py_ssize_t end_idx; - PyObject *val = NULL; PyObject *rval; Py_ssize_t next_idx; Py_ssize_t comma_idx; @@ -903,14 +905,12 @@ _parse_array_unicode(PyScannerObject *s, PyObject *memo, PyObject *pystr, Py_ssi while (1) { /* read any JSON term */ - val = scan_once_unicode(s, memo, pystr, idx, &next_idx); + PyObject *val = scan_once_unicode(s, memo, pystr, idx, &next_idx); if (val == NULL) goto bail; - if (PyList_Append(rval, val) == -1) + if (_PyList_AppendTakeRef((PyListObject *)rval, val) < 0) goto bail; - - Py_CLEAR(val); idx = next_idx; /* skip whitespace between term and , */ @@ -944,13 +944,12 @@ _parse_array_unicode(PyScannerObject *s, PyObject *memo, PyObject *pystr, Py_ssi *next_idx_ptr = idx + 1; /* if array_hook is not None: return array_hook(rval) */ if (!Py_IsNone(s->array_hook)) { - val = PyObject_CallOneArg(s->array_hook, rval); + PyObject *res = PyObject_CallOneArg(s->array_hook, rval); Py_DECREF(rval); - return val; + return res; } return rval; bail: - Py_XDECREF(val); Py_DECREF(rval); return NULL; } From 9cf40d94e6fa9659188a6215fffa096f321c6cea Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 11 Jun 2026 19:52:58 +0300 Subject: [PATCH 133/185] gh-150285: Fix too long docstrings in GenericAlias and __class_getitem__ (GH-151354) --- Objects/genericaliasobject.c | 3 ++- Objects/tupleobject.c | 6 ++++-- Objects/typevarobject.c | 5 +++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Objects/genericaliasobject.c b/Objects/genericaliasobject.c index 9c797e8dd6fd2cc..c2083e6fcb77f47 100644 --- a/Objects/genericaliasobject.c +++ b/Objects/genericaliasobject.c @@ -572,7 +572,8 @@ PyDoc_STRVAR(genericalias__doc__, "--\n\n" "Represent a PEP 585 generic type\n" "\n" -"E.g. for t = list[int], t.__origin__ is list and t.__args__ is (int,)."); +"For example, for t = list[int], t.__origin__ is list and t.__args__\n" +"is (int,)."); static PyObject * ga_getitem(PyObject *self, PyObject *item) diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c index 7ad83e37bd732dd..bb5e18cb790acf0 100644 --- a/Objects/tupleobject.c +++ b/Objects/tupleobject.c @@ -957,8 +957,10 @@ tuple___getnewargs___impl(PyTupleObject *self) PyDoc_STRVAR(tuple_class_getitem_doc, "Tuples are generic over the types of their contents.\n\n\ -For example, use ``tuple[int, str]`` for a pair whose first element is an int and second element is a string.\n\n\ -Tuples also support the form ``tuple[T, ...]`` to indicate an arbitrary length tuple of elements of type T."); +For example, use ``tuple[int, str]`` for a pair whose first element\n\ +is an int and second element is a string.\n\n\ +Tuples also support the form ``tuple[T, ...]`` to indicate\n\ +an arbitrary length tuple of elements of type T."); static PyMethodDef tuple_methods[] = { TUPLE___GETNEWARGS___METHODDEF diff --git a/Objects/typevarobject.c b/Objects/typevarobject.c index 8ad590cc6e60936..78750a955d2a492 100644 --- a/Objects/typevarobject.c +++ b/Objects/typevarobject.c @@ -2311,8 +2311,9 @@ PyDoc_STRVAR(generic_class_getitem_doc, "Parameterizes a generic class.\n\ \n\ At least, parameterizing a generic class is the *main* thing this\n\ -method does. For example, for some generic class `Foo`, this is called\n\ -when we do `Foo[int]` - there, with `cls=Foo` and `params=int`.\n\ +method does. For example, for some generic class `Foo`, this is\n\ +called when we do `Foo[int]` - there, with `cls=Foo` and\n\ +`params=int`.\n\ \n\ However, note that this method is also called when defining generic\n\ classes in the first place with `class Foo[T]: ...`.\n\ From 5245553db72979674dc8683030c657ae62538e48 Mon Sep 17 00:00:00 2001 From: Aniket <148300120+Aniketsy@users.noreply.github.com> Date: Thu, 11 Jun 2026 22:45:29 +0530 Subject: [PATCH 134/185] gh-138991: Update dataclass documentation for new eq behavior in Python 3.13 (#139007) And add tests. Co-authored-by: Victor Stinner --- Doc/library/dataclasses.rst | 23 ++++++++++--- Lib/test/test_dataclasses/__init__.py | 49 +++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 5 deletions(-) diff --git a/Doc/library/dataclasses.rst b/Doc/library/dataclasses.rst index 954edc4506df1a3..ce59d89843e761d 100644 --- a/Doc/library/dataclasses.rst +++ b/Doc/library/dataclasses.rst @@ -100,12 +100,25 @@ Module contents ignored. - *eq*: If true (the default), an :meth:`~object.__eq__` method will be - generated. This method compares the class as if it were a tuple - of its fields, in order. Both instances in the comparison must - be of the identical type. + generated. - If the class already defines :meth:`!__eq__`, this parameter is - ignored. + This method compares the class by comparing each field in order. Both + instances in the comparison must be of the identical type. + + If the class already defines :meth:`!__eq__`, this parameter is ignored. + + .. versionchanged:: 3.13 + The generated ``__eq__`` method now compares each field individually + (for example, ``self.a == other.a and self.b == other.b``), rather than + comparing tuples of fields as in previous versions. + + This change makes the comparison faster but it may alter results in cases + where attributes compare equal by identity but not by value (such as + ``float('nan')``). + + In Python 3.12 and earlier, the comparison was performed by creating + tuples of the fields and comparing them (for example, + ``(self.a, self.b) == (other.a, other.b)``). - *order*: If true (the default is ``False``), :meth:`~object.__lt__`, :meth:`~object.__le__`, :meth:`~object.__gt__`, and :meth:`~object.__ge__` methods will be diff --git a/Lib/test/test_dataclasses/__init__.py b/Lib/test/test_dataclasses/__init__.py index 2468e3e64dd621c..423247c92ce3c29 100644 --- a/Lib/test/test_dataclasses/__init__.py +++ b/Lib/test/test_dataclasses/__init__.py @@ -2792,6 +2792,55 @@ def __eq__(self, other): self.assertEqual(C(1), 5) self.assertNotEqual(C(1), 1) + def test_eq_field_by_field(self): + @dataclasses.dataclass + class Point: + x: int + y: int + + p1 = Point(1, 2) + p2 = Point(1, 2) + p3 = Point(2, 1) + self.assertEqual(p1, p2) + self.assertNotEqual(p1, p3) + + def test_eq_type_check(self): + @dataclasses.dataclass + class A: + x: int + + @dataclasses.dataclass + class B: + x: int + + a = A(1) + b = B(1) + self.assertNotEqual(a, b) + + def test_eq_custom_field(self): + class AlwaysEqual(int): + def __eq__(self, other): + return True + + @dataclasses.dataclass + class Foo: + x: AlwaysEqual + y: int + + f1 = Foo(AlwaysEqual(1), 2) + f2 = Foo(AlwaysEqual(2), 2) + self.assertEqual(f1, f2) + + def test_eq_nan_field(self): + @dataclasses.dataclass + class D: + x: float + + nan = float('nan') + d1 = D(nan) + d2 = D(nan) + self.assertNotEqual(d1, d2) + class TestOrdering(unittest.TestCase): def test_functools_total_ordering(self): From 33e87164504c7ef9ace654ef8831a17c7ad7f7c5 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 11 Jun 2026 20:50:00 +0300 Subject: [PATCH 135/185] gh-150285: Fix overflow in too long lines for class data in pydoc (GH-151366) Use all available space (80 columns) for formatting reprs of module and class data, but ensure that they do not overflow. --- Lib/pydoc.py | 6 +++--- .../Library/2026-06-11-19-46-16.gh-issue-150285.wuhAsL.rst | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-06-11-19-46-16.gh-issue-150285.wuhAsL.rst diff --git a/Lib/pydoc.py b/Lib/pydoc.py index ca4eb1001981681..fe42592530c2cfd 100644 --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -1355,7 +1355,7 @@ def docmodule(self, object, name=None, mod=None, *ignored): if data: contents = [] for key, value in data: - contents.append(self.docother(value, key, name, maxlen=70)) + contents.append(self.docother(value, key, name, maxlen=76)) result = result + self.section('DATA', '\n'.join(contents)) if version := self._get_version(object): @@ -1478,7 +1478,7 @@ def spilldata(msg, attrs, predicate): obj = getattr(object, name) except AttributeError: obj = homecls.__dict__[name] - push(self.docother(obj, name, mod, maxlen=70, doc=doc) + + push(self.docother(obj, name, mod, maxlen=72, doc=doc) + '\n') return attrs @@ -1629,7 +1629,7 @@ def docother(self, object, name=None, mod=None, parent=None, *ignored, if maxlen: line = (name and name + ' = ' or '') + repr chop = maxlen - len(line) - if chop < 0: repr = repr[:chop] + '...' + if chop < 0: repr = repr[:chop-3] + '...' line = (name and self.bold(name) + ' = ' or '') + repr if not doc: doc = getdoc(object) diff --git a/Misc/NEWS.d/next/Library/2026-06-11-19-46-16.gh-issue-150285.wuhAsL.rst b/Misc/NEWS.d/next/Library/2026-06-11-19-46-16.gh-issue-150285.wuhAsL.rst new file mode 100644 index 000000000000000..3f19150488f3131 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-11-19-46-16.gh-issue-150285.wuhAsL.rst @@ -0,0 +1,2 @@ +:mod:`pydoc` now uses all available space (80 columns) for formatting reprs +of module and class data, but ensure that they do not overflow. From 367f9d6fc9bc533f01559245054b19a49206f3f0 Mon Sep 17 00:00:00 2001 From: Ivy Xu Date: Fri, 12 Jun 2026 02:29:20 +0800 Subject: [PATCH 136/185] Correct the docstring of `logging.config` (GH-151353) --- Lib/logging/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/logging/config.py b/Lib/logging/config.py index e39dac432f6ab5c..35233e731eb42d1 100644 --- a/Lib/logging/config.py +++ b/Lib/logging/config.py @@ -21,7 +21,7 @@ Copyright (C) 2001-2022 Vinay Sajip. All Rights Reserved. -To use, simply 'import logging' and log away! +To use, simply 'import logging.config' and log away! """ import errno From 9a5764c898aa7c748011cc755cbf8db15ccea312 Mon Sep 17 00:00:00 2001 From: Ivy Xu Date: Fri, 12 Jun 2026 03:55:11 +0800 Subject: [PATCH 137/185] gh-151337: Avoid possible memory leak in `_tkinter.c` on Windows. (GH-151340) --- ...2026-06-11-21-43-24.gh-issue-151337.JSVV18.rst | 1 + Modules/_tkinter.c | 15 ++++++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-06-11-21-43-24.gh-issue-151337.JSVV18.rst diff --git a/Misc/NEWS.d/next/Library/2026-06-11-21-43-24.gh-issue-151337.JSVV18.rst b/Misc/NEWS.d/next/Library/2026-06-11-21-43-24.gh-issue-151337.JSVV18.rst new file mode 100644 index 000000000000000..0344eee9471d292 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-11-21-43-24.gh-issue-151337.JSVV18.rst @@ -0,0 +1 @@ +Avoid possible memory leak in ``tkinter.c`` on Windows. diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c index 58fdabecf16ada7..6eca98a3c8033fa 100644 --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -128,18 +128,20 @@ _get_tcl_lib_path(void) } /* Check expected location for an installed Python first */ - tcl_library_path = PyUnicode_FromString("\\tcl\\tcl" TCL_VERSION); - if (tcl_library_path == NULL) { + PyObject* tmp_tcl_library_path = PyUnicode_FromString("\\tcl\\tcl" TCL_VERSION); + if (tmp_tcl_library_path == NULL) { Py_DECREF(prefix); return NULL; } - tcl_library_path = PyUnicode_Concat(prefix, tcl_library_path); + tcl_library_path = PyUnicode_Concat(prefix, tmp_tcl_library_path); + Py_DECREF(tmp_tcl_library_path); Py_DECREF(prefix); if (tcl_library_path == NULL) { return NULL; } stat_return_value = _Py_stat(tcl_library_path, &stat_buf); if (stat_return_value == -2) { + Py_DECREF(tcl_library_path); return NULL; } if (stat_return_value == -1) { @@ -154,16 +156,17 @@ _get_tcl_lib_path(void) } stat_return_value = _Py_stat(tcl_library_path, &stat_buf); if (stat_return_value == -2) { + Py_DECREF(tcl_library_path); return NULL; } if (stat_return_value == -1) { /* tcltkDir for a repository build doesn't exist either, reset errno and leave Tcl to its own devices */ errno = 0; - tcl_library_path = NULL; + Py_CLEAR(tcl_library_path); } #else - tcl_library_path = NULL; + Py_CLEAR(tcl_library_path); #endif } already_checked = 1; @@ -707,11 +710,13 @@ Tkapp_New(const char *screenName, const char *className, if (!ret && GetLastError() == ERROR_ENVVAR_NOT_FOUND) { str_path = _get_tcl_lib_path(); if (str_path == NULL && PyErr_Occurred()) { + Py_DECREF(v); return NULL; } if (str_path != NULL) { utf8_path = PyUnicode_AsUTF8String(str_path); if (utf8_path == NULL) { + Py_DECREF(v); return NULL; } Tcl_SetVar(v->interp, From 365c8ce24cf5e9ad3a57dfdcb0387841f8650604 Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Thu, 11 Jun 2026 18:11:52 -0400 Subject: [PATCH 138/185] gh-151065: Copy fix for memory leak from mimalloc upstream (GH-151066) Applies https://github.com/microsoft/mimalloc/commit/d7a72c4912943e8aaf135e465ca5ea229ea96646 to our copy of mimalloc. --- .../2026-06-08-05-31-22.gh-issue-151065._o_31F.rst | 1 + Objects/mimalloc/init.c | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-06-08-05-31-22.gh-issue-151065._o_31F.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-08-05-31-22.gh-issue-151065._o_31F.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-08-05-31-22.gh-issue-151065._o_31F.rst new file mode 100644 index 000000000000000..e46c96ef784cc9e --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-08-05-31-22.gh-issue-151065._o_31F.rst @@ -0,0 +1 @@ +Fix memory leak when using the :ref:`mimalloc memory allocator `. diff --git a/Objects/mimalloc/init.c b/Objects/mimalloc/init.c index 81b241063ff40fc..7711c827a58b1ca 100644 --- a/Objects/mimalloc/init.c +++ b/Objects/mimalloc/init.c @@ -183,9 +183,9 @@ mi_heap_t* _mi_heap_main_get(void) { // note: in x64 in release build `sizeof(mi_thread_data_t)` is under 4KiB (= OS page size). typedef struct mi_thread_data_s { - mi_heap_t heap; // must come first due to cast in `_mi_heap_done` + mi_heap_t heap; // must come first due to cast in `_mi_heap_done` mi_tld_t tld; - mi_memid_t memid; + mi_memid_t memid; // must come last due to zero'ing } mi_thread_data_t; @@ -231,7 +231,7 @@ static mi_thread_data_t* mi_thread_data_zalloc(void) { } if (td != NULL && !is_zero) { - _mi_memzero_aligned(td, sizeof(*td)); + _mi_memzero_aligned(td, offsetof(mi_thread_data_t,memid)); } return td; } From 7adb0ef0bf66b8e07787b145cd6bdbafe5afc4db Mon Sep 17 00:00:00 2001 From: Matt Wozniski Date: Thu, 11 Jun 2026 21:21:04 -0400 Subject: [PATCH 139/185] gh-151297: Fix undefined behavior in `_PyObject_MiRealloc` (GH-151358) The standard says that a call to `memcpy` must pass a valid source and destination pointer even if the size is 0, so we must avoid calling `memcpy` when our source pointer is NULL. If we don't, an optimizing compiler can decide that the pointer must be non-NULL based on the presence of UB, and optimize out checks for null pointers. Specifically, note that the standard says: Where an argument declared as size_t n specifies the length of the array for a function, n can have the value zero on a call to that function. Unless explicitly stated otherwise in the description of a particular function in this subclause, pointer arguments on such a call shall still have valid values, as described in 7.1.4. And section 7.1.4 says: If an argument to a function has an invalid value (such as a value outside the domain of the function, or a pointer outside the address space of the program, or a null pointer, or a pointer to non-modifiable storage when the corresponding parameter is not const-qualified) or a type (after default argument promotion) not expected by a function with a variable number of arguments, the behavior is undefined. The specification for `memcpy` doesn't state that it's allowed to be called with null pointers, and Linux's `/usr/include/string.h` declares `memcpy` as `__nonnull ((1, 2))`. --- ...-06-11-16-03-23.gh-issue-151297.NGPkUM.rst | 1 + Modules/_testcapi/mem.c | 47 +++++++++++++++++++ Objects/obmalloc.c | 5 +- 3 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-06-11-16-03-23.gh-issue-151297.NGPkUM.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-11-16-03-23.gh-issue-151297.NGPkUM.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-11-16-03-23.gh-issue-151297.NGPkUM.rst new file mode 100644 index 000000000000000..288d726e0f1004d --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-11-16-03-23.gh-issue-151297.NGPkUM.rst @@ -0,0 +1 @@ +Fix an invalid pointer dereference that could occur when calling :c:func:`PyObject_Realloc` with a NULL pointer in :term:`free-threaded builds ` or with :envvar:`PYTHONMALLOC` set to ``mimalloc``. diff --git a/Modules/_testcapi/mem.c b/Modules/_testcapi/mem.c index 7909476ac11aa6b..28e89275d61705e 100644 --- a/Modules/_testcapi/mem.c +++ b/Modules/_testcapi/mem.c @@ -345,6 +345,53 @@ test_setallocators(PyMemAllocatorDomain domain) goto fail; } + /* realloc(NULL, size) should behave like malloc(size) */ + size_t size3 = 100; + void *ptr3; + switch(domain) { + case PYMEM_DOMAIN_RAW: + ptr3 = PyMem_RawRealloc(NULL, size3); + break; + case PYMEM_DOMAIN_MEM: + ptr3 = PyMem_Realloc(NULL, size3); + break; + case PYMEM_DOMAIN_OBJ: + ptr3 = PyObject_Realloc(NULL, size3); + break; + default: + ptr3 = NULL; + break; + } + + CHECK_CTX("realloc(NULL, size)"); + if (ptr3 == NULL) { + error_msg = "realloc(NULL, size) failed"; + goto fail; + } + if (hook.realloc_ptr != NULL || hook.realloc_new_size != size3) { + error_msg = "realloc(NULL, size) invalid parameters"; + goto fail; + } + + hook.free_ptr = NULL; + switch(domain) { + case PYMEM_DOMAIN_RAW: + PyMem_RawFree(ptr3); + break; + case PYMEM_DOMAIN_MEM: + PyMem_Free(ptr3); + break; + case PYMEM_DOMAIN_OBJ: + PyObject_Free(ptr3); + break; + } + + CHECK_CTX("realloc(NULL, size) free"); + if (hook.free_ptr != ptr3) { + error_msg = "unexpected pointer passed to free"; + goto fail; + } + res = Py_NewRef(Py_None); goto finally; diff --git a/Objects/obmalloc.c b/Objects/obmalloc.c index 1809bd30451327b..0947d47c8a55582 100644 --- a/Objects/obmalloc.c +++ b/Objects/obmalloc.c @@ -363,7 +363,10 @@ _PyObject_MiRealloc(void *ctx, void *ptr, size_t nbytes) _mi_memcpy((char*)newp + offset, (char*)ptr + offset, copy_size - offset); } else { - _mi_memcpy(newp, ptr, copy_size); + // memcpy(dst, NULL, 0) is undefined behavior. See gh-151297. + if mi_likely(ptr) { + _mi_memcpy(newp, ptr, copy_size); + } } mi_free(ptr); return newp; From b5fe8fdc149617d84fb2ac7945db55f31e340434 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Fri, 12 Jun 2026 14:03:21 +0300 Subject: [PATCH 140/185] gh-151126: Fix crash on unset memory error in `ctypes.get_errno` (#151382) --- .../Library/2026-06-12-00-04-34.gh-issue-151126.aHaBYq.rst | 2 ++ Modules/_ctypes/callproc.c | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-06-12-00-04-34.gh-issue-151126.aHaBYq.rst diff --git a/Misc/NEWS.d/next/Library/2026-06-12-00-04-34.gh-issue-151126.aHaBYq.rst b/Misc/NEWS.d/next/Library/2026-06-12-00-04-34.gh-issue-151126.aHaBYq.rst new file mode 100644 index 000000000000000..20ef69d5de5ac55 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-12-00-04-34.gh-issue-151126.aHaBYq.rst @@ -0,0 +1,2 @@ +Fix crash on unset :exc:`MemoryError` on allocation failure in +:func:`ctypes.get_errno`. diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index e453cfeec9cc8ca..ccc57e347b07acf 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -168,8 +168,9 @@ _ctypes_get_errobj(ctypes_state *st, int **pspace) } else { void *space = PyMem_Calloc(2, sizeof(int)); - if (space == NULL) - return NULL; + if (space == NULL) { + return PyErr_NoMemory(); + } errobj = PyCapsule_New(space, CTYPES_CAPSULE_NAME_PYMEM, pymem_destructor); if (errobj == NULL) { PyMem_Free(space); From 4dc0415688f57c5b1d4d89e861c15b14cae77bd7 Mon Sep 17 00:00:00 2001 From: Maciej Olko Date: Fri, 12 Jun 2026 16:53:06 +0200 Subject: [PATCH 141/185] Docs: avoid repetitions of class references in functions.rst (#150891) --- Doc/library/functions.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index ebf2dea1c1f80c1..a4d37beb436899a 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -485,7 +485,7 @@ are always available. They are listed here in alphabetical order. :noindex: Create a new dictionary. The :class:`dict` object is the dictionary class. - See :class:`dict` and :ref:`typesmapping` for documentation about this class. + See also :ref:`typesmapping` for documentation about this class. For other containers see the built-in :class:`frozendict`, :class:`list`, :class:`set`, and :class:`tuple` classes, as well as the :mod:`collections` module. @@ -873,7 +873,7 @@ are always available. They are listed here in alphabetical order. :noindex: Create a new frozen dictionary. The :class:`frozendict` object is a built-in class. - See :class:`frozendict` and :ref:`typesmapping` for documentation about this class. + See also :ref:`typesmapping` for documentation about this class. For other containers see the built-in :class:`dict`, :class:`list`, :class:`set`, and :class:`tuple` classes, as well as the :mod:`collections` module. @@ -886,7 +886,7 @@ are always available. They are listed here in alphabetical order. :noindex: Return a new :class:`frozenset` object, optionally with elements taken from - *iterable*. ``frozenset`` is a built-in class. See :class:`frozenset` and + *iterable*. :class:`frozenset` is a built-in class. See also :ref:`types-set` for documentation about this class. For other containers see the built-in :class:`set`, :class:`list`, @@ -1814,7 +1814,7 @@ are always available. They are listed here in alphabetical order. :noindex: Return a new :class:`set` object, optionally with elements taken from - *iterable*. ``set`` is a built-in class. See :class:`set` and + *iterable*. :class:`set` is a built-in class. See also :ref:`types-set` for documentation about this class. For other containers see the built-in :class:`frozenset`, :class:`list`, From de3ff8f061dc9e0b8059b0a96d84d25a90aca9cc Mon Sep 17 00:00:00 2001 From: Rafael Fontenelle Date: Fri, 12 Jun 2026 13:02:33 -0300 Subject: [PATCH 142/185] Fix typos in the `curses.ascii` module documentation (#129300) Co-authored-by: Stan Ulbrych --- Doc/library/curses.ascii.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/curses.ascii.rst b/Doc/library/curses.ascii.rst index 9ae82c144655383..8f8e3ddda8ef52a 100644 --- a/Doc/library/curses.ascii.rst +++ b/Doc/library/curses.ascii.rst @@ -130,7 +130,7 @@ C library: .. function:: isgraph(c) - Checks for ASCII any printable character except space. + Checks for any ASCII printable character except space. .. function:: islower(c) @@ -145,7 +145,7 @@ C library: .. function:: ispunct(c) - Checks for any printable ASCII character which is not a space or an alphanumeric + Checks for any ASCII printable character which is not a space or an alphanumeric character. From 64053cea0439e1db8d683a9407e790574eb8df0c Mon Sep 17 00:00:00 2001 From: Daniel Shields Date: Sat, 13 Jun 2026 03:23:39 -0500 Subject: [PATCH 143/185] gh-101267: ProcessPoolExecutor no longer shares 1 BrokenProcessPool exception among all failed futures (GH-101268) Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> Co-authored-by: Gregory P. Smith --- Lib/concurrent/futures/process.py | 16 ++++++----- .../test_process_pool.py | 27 +++++++++++++++++++ Misc/ACKS | 1 + ...-01-23-21-23-50.gh-issue-101267._f-cFH.rst | 7 +++++ 4 files changed, 44 insertions(+), 7 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-01-23-21-23-50.gh-issue-101267._f-cFH.rst diff --git a/Lib/concurrent/futures/process.py b/Lib/concurrent/futures/process.py index ed24cc250434130..10d4ac89d725713 100644 --- a/Lib/concurrent/futures/process.py +++ b/Lib/concurrent/futures/process.py @@ -469,11 +469,9 @@ def _terminate_broken(self, cause): executor._shutdown_thread = True executor = None - # All pending tasks are to be marked failed with the following - # BrokenProcessPool error - bpe = BrokenProcessPool("A process in the process pool was " - "terminated abruptly while the future was " - "running or pending.") + # All pending tasks are to be marked failed with a + # BrokenProcessPool error, as separate instances to avoid sharing + # a traceback (gh-101267). cause_str = None if cause is not None: cause_str = ''.join(cause) @@ -489,11 +487,15 @@ def _terminate_broken(self, cause): f"with exit code {p.exitcode}") if errors: cause_str = "\n".join(errors) - if cause_str: - bpe.__cause__ = _RemoteTraceback(f"\n'''\n{cause_str}'''") + cause_tb = f"\n'''\n{cause_str}'''" if cause_str else None # Mark pending tasks as failed. for work_id, work_item in self.pending_work_items.items(): + bpe = BrokenProcessPool("A process in the process pool was " + "terminated abruptly while the future was " + "running or pending.") + if cause_tb is not None: + bpe.__cause__ = _RemoteTraceback(cause_tb) try: work_item.future.set_exception(bpe) except _base.InvalidStateError: diff --git a/Lib/test/test_concurrent_futures/test_process_pool.py b/Lib/test/test_concurrent_futures/test_process_pool.py index da70d910dc35614..45cfd46fb7befbf 100644 --- a/Lib/test/test_concurrent_futures/test_process_pool.py +++ b/Lib/test/test_concurrent_futures/test_process_pool.py @@ -3,6 +3,7 @@ import sys import threading import time +import traceback import unittest import unittest.mock from concurrent import futures @@ -62,6 +63,32 @@ def test_killed_child(self): # Submitting other jobs fails as well. self.assertRaises(BrokenProcessPool, self.executor.submit, pow, 2, 8) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() + def test_broken_process_pool_traceback(self): + # When a child process is abruptly terminated, the whole pool gets + # "broken", and a BrokenProcessPool exception should be created + # for each future instead of sharing one exception among all futures. + event = self.create_event() + futures = [self.executor.submit(event.wait) for _ in range(3)] + p = next(iter(self.executor._processes.values())) + p.terminate() + for fut in futures: + # Don't use assertRaises(): it clears the traceback off exc. + try: + fut.result() + except BrokenProcessPool as exc: + tb = exc.__traceback__ + else: + self.fail("BrokenProcessPool not raised") + count = sum( + 1 + for frame_summary in traceback.extract_tb(tb) + if frame_summary.filename == __file__ + ) + # This code file should appear exactly once in the traceback. + # A shared exception would accumulate a frame per result() call. + self.assertEqual(count, 1) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_map_chunksize(self): def bad_map(): diff --git a/Misc/ACKS b/Misc/ACKS index 71466e0804ae3c1..ee68d91f13c431f 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1763,6 +1763,7 @@ Charlie Shepherd Bruce Sherwood Gregory Shevchenko Hai Shi +Daniel Shields Alexander Shigin Pete Shinners Michael Shiplett diff --git a/Misc/NEWS.d/next/Library/2023-01-23-21-23-50.gh-issue-101267._f-cFH.rst b/Misc/NEWS.d/next/Library/2023-01-23-21-23-50.gh-issue-101267._f-cFH.rst new file mode 100644 index 000000000000000..901a3fb60ab5b9f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-23-21-23-50.gh-issue-101267._f-cFH.rst @@ -0,0 +1,7 @@ +When a worker process terminates unexpectedly, +:class:`concurrent.futures.ProcessPoolExecutor` now sets a separate +:exc:`~concurrent.futures.process.BrokenProcessPool` exception on each pending +future instead of sharing a single instance among them all. Sharing one +exception produced malformed tracebacks: each +:meth:`Future.result() ` call re-raised the +same object, appending another copy of the traceback to it. From c52561ee40cf26a44f3234b4b23d7a80897335fb Mon Sep 17 00:00:00 2001 From: Jonathan Dung Date: Sat, 13 Jun 2026 16:47:23 +0800 Subject: [PATCH 144/185] Docs: Fix method directive signatures in `enum` docs (#151429) --- Doc/library/enum.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst index 65bb0b483350c52..559a76bb3963e09 100644 --- a/Doc/library/enum.rst +++ b/Doc/library/enum.rst @@ -629,7 +629,7 @@ Data types >>> white in purple False - .. method:: __iter__(self): + .. method:: __iter__(self) Returns all contained non-alias members:: @@ -640,7 +640,7 @@ Data types .. versionadded:: 3.11 - .. method:: __len__(self): + .. method:: __len__(self) Returns number of members in flag:: @@ -651,7 +651,7 @@ Data types .. versionadded:: 3.11 - .. method:: __bool__(self): + .. method:: __bool__(self) Returns *True* if any members in flag, *False* otherwise:: @@ -688,7 +688,7 @@ Data types >>> purple ^ Color.GREEN - .. method:: __invert__(self): + .. method:: __invert__(self) Returns all the flags in *type(self)* that are not in *self*:: From e6cd6b6ec0b0da591bbd01908318d791c09d700e Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Sat, 13 Jun 2026 13:21:54 +0100 Subject: [PATCH 145/185] gh-77328: Update `base64` module RFC references to RFC 4648 (#151275) --- Lib/base64.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/base64.py b/Lib/base64.py index 4a0e9d446edb0bc..fa562f74a810345 100644 --- a/Lib/base64.py +++ b/Lib/base64.py @@ -1,4 +1,4 @@ -"""Base16, Base32, Base64 (RFC 3548), Base85 and Ascii85 data encodings""" +"""Base16, Base32, Base64 (RFC 4648), Base85 and Ascii85 data encodings""" # Modified 04-Oct-1995 by Jack Jansen to use binascii module # Modified 30-Dec-2003 by Barry Warsaw to add full RFC 3548 support @@ -219,7 +219,7 @@ def urlsafe_b64decode(s, *, padded=False): characters present in the input. ''' _B32_DECODE_MAP01_DOCSTRING = ''' -RFC 3548 allows for optional mapping of the digit 0 (zero) to the +RFC 4648 allows for optional mapping of the digit 0 (zero) to the letter O (oh), and for optional mapping of the digit 1 (one) to either the letter I (eye) or letter L (el). The optional argument map01 when not None, specifies which letter the digit 1 should be @@ -266,7 +266,7 @@ def b32hexdecode(s, casefold=False, *, padded=True, ignorechars=b'', extra_args='') -# RFC 3548, Base 16 Alphabet specifies uppercase, but hexlify() returns +# RFC 4648, Base 16 Alphabet specifies uppercase, but hexlify() returns # lowercase. The RFC also recommends against accepting input case # insensitively. def b16encode(s, *, wrapcol=0): From e0f4179465e6428d7283087d7fcd04af8415bf89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?tonghuaroot=20=28=E7=AB=A5=E8=AF=9D=29?= Date: Sun, 14 Jun 2026 01:23:44 +0800 Subject: [PATCH 146/185] gh-151403: Fix use-after-free when an argv item's __fspath__ mutates args (GH-151404) --------- Co-authored-by: tonghuaroot <23011166+tonghuaroot@users.noreply.github.com> --- .../2026-06-12-22-46-31.gh-issue-151403.DalZWh.rst | 3 +++ Modules/_posixsubprocess.c | 8 +++++++- 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2026-06-12-22-46-31.gh-issue-151403.DalZWh.rst diff --git a/Misc/NEWS.d/next/Library/2026-06-12-22-46-31.gh-issue-151403.DalZWh.rst b/Misc/NEWS.d/next/Library/2026-06-12-22-46-31.gh-issue-151403.DalZWh.rst new file mode 100644 index 000000000000000..ca779ed684e7616 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-12-22-46-31.gh-issue-151403.DalZWh.rst @@ -0,0 +1,3 @@ +Fixed a crash in :class:`subprocess.Popen` (and ``_posixsubprocess.fork_exec``) +when an ``argv`` item's :meth:`~os.PathLike.__fspath__` concurrently mutates the +``args`` sequence being converted. diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c index ddc27c4a5b7356a..2aa3923f68e66ad 100644 --- a/Modules/_posixsubprocess.c +++ b/Modules/_posixsubprocess.c @@ -1090,8 +1090,14 @@ subprocess_fork_exec_impl(PyObject *module, PyObject *process_args, goto cleanup; } borrowed_arg = PySequence_Fast_GET_ITEM(fast_args, arg_num); - if (PyUnicode_FSConverter(borrowed_arg, &converted_arg) == 0) + /* borrowed_arg is only borrowed; its __fspath__() may run Python + that drops fast_args' last reference to it. */ + Py_INCREF(borrowed_arg); + if (PyUnicode_FSConverter(borrowed_arg, &converted_arg) == 0) { + Py_DECREF(borrowed_arg); goto cleanup; + } + Py_DECREF(borrowed_arg); PyTuple_SET_ITEM(converted_args, arg_num, converted_arg); } From 1af7a40bf2fc4158d383d50f09bed0fde0ba73b8 Mon Sep 17 00:00:00 2001 From: Shardul Deshpande Date: Sun, 14 Jun 2026 00:01:03 +0530 Subject: [PATCH 147/185] gh-151443: Fix documented default of `unittest.mock.mock_open`'s `read_data` parameter (#151444) --- Doc/library/unittest.mock.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/unittest.mock.rst b/Doc/library/unittest.mock.rst index 6ba1b87ea942a90..5e28a8ada595ef1 100644 --- a/Doc/library/unittest.mock.rst +++ b/Doc/library/unittest.mock.rst @@ -2539,7 +2539,7 @@ Alternatively you can just use ``vars(my_mock)`` (instance members) and mock_open ~~~~~~~~~ -.. function:: mock_open(mock=None, read_data=None) +.. function:: mock_open(mock=None, read_data='') A helper function to create a mock to replace the use of :func:`open`. It works for :func:`open` called directly or used as a context manager. From d9ed7e2413281592f47cf023b86cfb538876ca17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maurycy=20Paw=C5=82owski-Wiero=C5=84ski?= Date: Sun, 14 Jun 2026 01:10:56 +0200 Subject: [PATCH 148/185] gh-151424: Fix impossible stack traces in `RemoteUnwinder(..., cache_frames=True)` by copying chunks on cache miss (#151426) --- .../2026-06-13-04-11-00.gh-issue-151426.f2V67e.rst | 4 ++++ Modules/_remote_debugging/frames.c | 8 ++++++++ 2 files changed, 12 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2026-06-13-04-11-00.gh-issue-151426.f2V67e.rst diff --git a/Misc/NEWS.d/next/Library/2026-06-13-04-11-00.gh-issue-151426.f2V67e.rst b/Misc/NEWS.d/next/Library/2026-06-13-04-11-00.gh-issue-151426.f2V67e.rst new file mode 100644 index 000000000000000..428302e5f847f36 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-13-04-11-00.gh-issue-151426.f2V67e.rst @@ -0,0 +1,4 @@ +Fix impossible stack traces (callers and callees cross called, orphans and +incorrect lines) in the Tachyon profiler when caching frames, by snapshotting +the stack chunks before walking the frame chain on a cache miss. Patch by +Maurycy Pawłowski-Wieroński. diff --git a/Modules/_remote_debugging/frames.c b/Modules/_remote_debugging/frames.c index d73cd080dc477f3..e7d2a2764390261 100644 --- a/Modules/_remote_debugging/frames.c +++ b/Modules/_remote_debugging/frames.c @@ -580,6 +580,14 @@ collect_frames_with_cache( return full_hit < 0 ? -1 : 0; } + assert(ctx->chunks != NULL); + + if (ctx->chunks->count == 0) { + if (copy_stack_chunks(unwinder, ctx->thread_state_addr, ctx->chunks) < 0) { + PyErr_Clear(); + } + } + Py_ssize_t frames_before = PyList_GET_SIZE(ctx->frame_info); if (process_frame_chain(unwinder, ctx) < 0) { From cded199e14819160d79256121d3efc6a3c960af2 Mon Sep 17 00:00:00 2001 From: Ivy Xu Date: Sun, 14 Jun 2026 20:00:28 +0800 Subject: [PATCH 149/185] gh-151428: Remove unused imports in the stdlib (#151440) --- Lib/asyncio/coroutines.py | 1 - Lib/unittest/mock.py | 1 - Lib/zipfile/__init__.py | 1 - 3 files changed, 3 deletions(-) diff --git a/Lib/asyncio/coroutines.py b/Lib/asyncio/coroutines.py index bfffb6da4b19a18..3a4246f6ccd4c24 100644 --- a/Lib/asyncio/coroutines.py +++ b/Lib/asyncio/coroutines.py @@ -1,7 +1,6 @@ __all__ = ('iscoroutine',) import collections.abc -import inspect import os import sys import types diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index 56cdc37942d65d8..14a490c16575857 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -24,7 +24,6 @@ ) -import asyncio import contextlib import io import inspect diff --git a/Lib/zipfile/__init__.py b/Lib/zipfile/__init__.py index 71e4dd4f6f625ce..005b6f4eb840448 100644 --- a/Lib/zipfile/__init__.py +++ b/Lib/zipfile/__init__.py @@ -4,7 +4,6 @@ XXX references to utf-8 need further investigation. """ import binascii -import importlib.util import io import os import shutil From 263e6ba00d7e00553b70bcd8b0b2ab946a96d364 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" <68491+gpshead@users.noreply.github.com> Date: Sun, 14 Jun 2026 05:29:26 -0700 Subject: [PATCH 150/185] Skip test_highly_nested_objects_decoding during the PGO profile task. (GH-151460) Since the recursion guard tracks real C-stack bounds (gh-91079), this test asserts that 500k nesting levels overflow the stack margin. On a 64 MiB stack (some Nix build envs use one that large), the optimized interpreter uses ~160 bytes/level (raises at ~420k levels) so the assertion holds with only ~16% margin; the PGO *instrumented* stage inlines less, its per-level scanner frames are smaller, and the 500k-deep decode completes -- "RecursionError not raised" fails the profile run and aborts `make profile-opt`. Upstream's skip_if_unlimited_stack_size (gh-143460) only covers RLIM_INFINITY, not large-finite stacks like ours. We could also keep playing whack a mole and raise the 500k to a much larger number... but there's little value in PGO training on this test anyways. --- Lib/test/test_json/test_recursion.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/test/test_json/test_recursion.py b/Lib/test/test_json/test_recursion.py index ffd3404e6f77a07..d732fc80cf1cf30 100644 --- a/Lib/test/test_json/test_recursion.py +++ b/Lib/test/test_json/test_recursion.py @@ -68,6 +68,7 @@ def default(self, o): self.fail("didn't raise ValueError on default recursion") + @support.skip_if_pgo_task # fails during PGO training w/ some stack sizes @support.skip_if_unlimited_stack_size @support.skip_emscripten_stack_overflow() @support.skip_wasi_stack_overflow() From 20525d35cf4cf0ff68693d43d6dda5723c476b5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20S=C5=82awecki?= Date: Sun, 14 Jun 2026 14:33:03 +0200 Subject: [PATCH 151/185] gh-151461: Fix encoding-related exception handling in file tokenizer (GH-151462) --- Lib/test/test_source_encoding.py | 17 ++++++- ...-06-14-05-05-15.gh-issue-151461.5q0s88.rst | 3 ++ Parser/pegen.c | 5 +- Parser/pegen.h | 1 - Parser/pegen_errors.c | 47 ----------------- Parser/tokenizer/helpers.c | 51 ++++++++++++++++++- Parser/tokenizer/helpers.h | 1 + 7 files changed, 72 insertions(+), 53 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-06-14-05-05-15.gh-issue-151461.5q0s88.rst diff --git a/Lib/test/test_source_encoding.py b/Lib/test/test_source_encoding.py index 8ac64b3105708f7..53fffe7cfb56d09 100644 --- a/Lib/test/test_source_encoding.py +++ b/Lib/test/test_source_encoding.py @@ -387,8 +387,7 @@ def test_utf8_non_utf8_third_line_error(self): b'#third\xa4\n' b'raise RuntimeError\n') self.check_script_error(src, - br"'utf-8' codec can't decode byte|" - br"encoding problem: utf8") + br"'utf-8' codec can't decode byte") def test_crlf(self): src = (b'print(ascii("""\r\n"""))\n') @@ -540,6 +539,20 @@ def check_script_error(self, src, expected, lineno=...): line = line.removeprefix('\ufeff') self.assertIn(line.encode(), err) + def test_coding_spec_unknown_encoding(self): + src = (b'# coding: c1252\n' + b'print("Hi!")\n') + self.check_script_error(src, br"unknown encoding: c1252") + + def test_coding_spec_decode_error(self): + src = (b'# coding: shift-jis\n' + b'print("\xc4\x85")\n') + self.check_script_error(src, br"'shift_jis' codec can't decode byte") + + def test_coding_spec_non_text_encoding(self): + src = (b'# coding: hex_codec\n' + b'print("eggs")\n') + self.check_script_error(src, br"'hex_codec' is not a text encoding") if __name__ == "__main__": diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-14-05-05-15.gh-issue-151461.5q0s88.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-14-05-05-15.gh-issue-151461.5q0s88.rst new file mode 100644 index 000000000000000..d76a9bc95278bcb --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-14-05-05-15.gh-issue-151461.5q0s88.rst @@ -0,0 +1,3 @@ +Fix direct execution of files with invalid source encodings to report the +underlying codec lookup or decoding error instead of the generic +``SyntaxError: encoding problem`` message. Patch by Bartosz Sławecki. diff --git a/Parser/pegen.c b/Parser/pegen.c index 569f5afb3120085..bb222b50fc095f2 100644 --- a/Parser/pegen.c +++ b/Parser/pegen.c @@ -9,6 +9,7 @@ #include "lexer/lexer.h" #include "tokenizer/tokenizer.h" +#include "tokenizer/helpers.h" #include "pegen.h" // Internal parser functions @@ -993,7 +994,7 @@ _PyPegen_run_parser_from_file_pointer(FILE *fp, int start_rule, PyObject *filena struct tok_state *tok = _PyTokenizer_FromFile(fp, enc, ps1, ps2); if (tok == NULL) { if (PyErr_Occurred()) { - _PyPegen_raise_tokenizer_init_error(filename_ob); + _PyTokenizer_raise_init_error(filename_ob); return NULL; } return NULL; @@ -1051,7 +1052,7 @@ _PyPegen_run_parser_from_string(const char *str, int start_rule, PyObject *filen } if (tok == NULL) { if (PyErr_Occurred()) { - _PyPegen_raise_tokenizer_init_error(filename_ob); + _PyTokenizer_raise_init_error(filename_ob); } return NULL; } diff --git a/Parser/pegen.h b/Parser/pegen.h index 85c9ada765d9bd4..5c461e82a7f0fa7 100644 --- a/Parser/pegen.h +++ b/Parser/pegen.h @@ -174,7 +174,6 @@ typedef enum { } TARGETS_TYPE; int _Pypegen_raise_decode_error(Parser *p); -void _PyPegen_raise_tokenizer_init_error(PyObject *filename); int _Pypegen_tokenizer_error(Parser *p); void *_PyPegen_raise_error(Parser *p, PyObject *errtype, int use_mark, const char *errmsg, ...); void *_PyPegen_raise_error_known_location(Parser *p, PyObject *errtype, diff --git a/Parser/pegen_errors.c b/Parser/pegen_errors.c index 312699415efd9af..b13e1c079220a92 100644 --- a/Parser/pegen_errors.c +++ b/Parser/pegen_errors.c @@ -10,53 +10,6 @@ // TOKENIZER ERRORS -void -_PyPegen_raise_tokenizer_init_error(PyObject *filename) -{ - if (!(PyErr_ExceptionMatches(PyExc_LookupError) - || PyErr_ExceptionMatches(PyExc_SyntaxError) - || PyErr_ExceptionMatches(PyExc_ValueError) - || PyErr_ExceptionMatches(PyExc_UnicodeDecodeError))) { - return; - } - PyObject *errstr = NULL; - PyObject *tuple = NULL; - PyObject *type; - PyObject *value; - PyObject *tback; - PyErr_Fetch(&type, &value, &tback); - if (PyErr_GivenExceptionMatches(value, PyExc_SyntaxError)) { - if (PyObject_SetAttr(value, &_Py_ID(filename), filename)) { - goto error; - } - PyErr_Restore(type, value, tback); - return; - } - errstr = PyObject_Str(value); - if (!errstr) { - goto error; - } - - PyObject *tmp = Py_BuildValue("(OiiO)", filename, 0, -1, Py_None); - if (!tmp) { - goto error; - } - - tuple = _PyTuple_FromPair(errstr, tmp); - Py_DECREF(tmp); - if (!tuple) { - goto error; - } - PyErr_SetObject(PyExc_SyntaxError, tuple); - -error: - Py_XDECREF(type); - Py_XDECREF(value); - Py_XDECREF(tback); - Py_XDECREF(errstr); - Py_XDECREF(tuple); -} - static inline void raise_unclosed_parentheses_error(Parser *p) { int error_lineno = p->tok->parenlinenostack[p->tok->level-1]; diff --git a/Parser/tokenizer/helpers.c b/Parser/tokenizer/helpers.c index c69e66d0ab9b7a8..62b0971d418c396 100644 --- a/Parser/tokenizer/helpers.c +++ b/Parser/tokenizer/helpers.c @@ -1,6 +1,8 @@ #include "Python.h" #include "errcode.h" +#include "pycore_runtime.h" // _Py_ID() #include "pycore_token.h" +#include "pycore_tuple.h" // _PyTuple_FromPair #include "../lexer/state.h" @@ -149,6 +151,53 @@ _PyTokenizer_warn_invalid_escape_sequence(struct tok_state *tok, int first_inval return 0; } +void +_PyTokenizer_raise_init_error(PyObject *filename) +{ + if (!(PyErr_ExceptionMatches(PyExc_LookupError) + || PyErr_ExceptionMatches(PyExc_SyntaxError) + || PyErr_ExceptionMatches(PyExc_ValueError) + || PyErr_ExceptionMatches(PyExc_UnicodeDecodeError))) { + return; + } + PyObject *errstr = NULL; + PyObject *tuple = NULL; + PyObject *type; + PyObject *value; + PyObject *tback; + PyErr_Fetch(&type, &value, &tback); + if (PyErr_GivenExceptionMatches(value, PyExc_SyntaxError)) { + if (PyObject_SetAttr(value, &_Py_ID(filename), filename)) { + goto error; + } + PyErr_Restore(type, value, tback); + return; + } + errstr = PyObject_Str(value); + if (!errstr) { + goto error; + } + + PyObject *tmp = Py_BuildValue("(OiiO)", filename, 0, -1, Py_None); + if (!tmp) { + goto error; + } + + tuple = _PyTuple_FromPair(errstr, tmp); + Py_DECREF(tmp); + if (!tuple) { + goto error; + } + PyErr_SetObject(PyExc_SyntaxError, tuple); + +error: + Py_XDECREF(type); + Py_XDECREF(value); + Py_XDECREF(tback); + Py_XDECREF(errstr); + Py_XDECREF(tuple); +} + int _PyTokenizer_parser_warn(struct tok_state *tok, PyObject *category, const char *format, ...) { @@ -418,8 +467,8 @@ _PyTokenizer_check_coding_spec(const char* line, Py_ssize_t size, struct tok_sta if (tok->encoding == NULL) { assert(tok->decoding_readline == NULL); if (strcmp(cs, "utf-8") != 0 && !set_readline(tok, cs)) { + _PyTokenizer_raise_init_error(tok->filename); _PyTokenizer_error_ret(tok); - PyErr_Format(PyExc_SyntaxError, "encoding problem: %s", cs); PyMem_Free(cs); return 0; } diff --git a/Parser/tokenizer/helpers.h b/Parser/tokenizer/helpers.h index 98f6445d5a3b40e..34303999a60aff7 100644 --- a/Parser/tokenizer/helpers.h +++ b/Parser/tokenizer/helpers.h @@ -15,6 +15,7 @@ int _PyTokenizer_indenterror(struct tok_state *tok); int _PyTokenizer_warn_invalid_escape_sequence(struct tok_state *tok, int first_invalid_escape_char); int _PyTokenizer_parser_warn(struct tok_state *tok, PyObject *category, const char *format, ...); char *_PyTokenizer_error_ret(struct tok_state *tok); +void _PyTokenizer_raise_init_error(PyObject *filename); char *_PyTokenizer_new_string(const char *s, Py_ssize_t len, struct tok_state *tok); char *_PyTokenizer_translate_newlines(const char *s, int exec_input, int preserve_crlf, struct tok_state *tok); From 8342134185ea3cba9c1d658c986f2606f41ad3f2 Mon Sep 17 00:00:00 2001 From: Aniket <148300120+Aniketsy@users.noreply.github.com> Date: Sun, 14 Jun 2026 18:09:33 +0530 Subject: [PATCH 152/185] gh-139837: Document attributes of objects recorded by warnings.catch_warnings (GH-139893) Co-authored-by: Victor Stinner --- Doc/library/warnings.rst | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/Doc/library/warnings.rst b/Doc/library/warnings.rst index b25384dbfce54bb..9063bea96ccb0ac 100644 --- a/Doc/library/warnings.rst +++ b/Doc/library/warnings.rst @@ -626,9 +626,28 @@ Available Context Managers If the *record* argument is :const:`False` (the default) the context manager returns :class:`None` on entry. If *record* is :const:`True`, a list is returned that is progressively populated with objects as seen by a custom - :func:`showwarning` function (which also suppresses output to ``sys.stdout``). - Each object in the list has attributes with the same names as the arguments to - :func:`showwarning`. + :func:`showwarning` function (which also suppresses output to ``sys.stderr``). + Each object in the list is guaranteed to have the following attributes: + + - ``message``: the warning message (an instance of :exc:`Warning`) + - ``category``: the warning category (a subclass of :exc:`Warning`) + - ``filename``: the file name where the warning occurred (:class:`str`) + - ``lineno``: the line number in the file (:class:`int`) + - ``file``: the file object used for output (if any), or ``None`` + - ``line``: the line of source code (if available), or ``None`` + - ``source``: the original object that generated the warning (if + available), or ``None`` + - ``module``: the module name where the warning occurred + (:class:`str`), or ``None`` + + .. versionchanged:: 3.6 + The ``source`` attribute was added. + + .. versionchanged:: 3.15 + The ``module`` attribute was added. + + The type of these objects is not specified and may change; only the + presence of these attributes is guaranteed. The *module* argument takes a module that will be used instead of the module returned when you import :mod:`!warnings` whose filter will be From 6442abd441b98cce40f0b738932f7ea46d82147b Mon Sep 17 00:00:00 2001 From: Ivy Xu Date: Sun, 14 Jun 2026 21:12:30 +0800 Subject: [PATCH 153/185] gh-151428: Remove unused imports from `Tools/` (#151442) --- Tools/c-analyzer/c_parser/preprocessor/clang.py | 1 - Tools/c-analyzer/distutils/util.py | 1 - Tools/cases_generator/generators_common.py | 2 -- Tools/cases_generator/opcode_metadata_generator.py | 1 - Tools/cases_generator/parser.py | 1 - Tools/cases_generator/tier1_generator.py | 6 ------ Tools/inspection/benchmark_external_inspection.py | 1 - Tools/jit/_stencils.py | 1 - Tools/picklebench/memory_dos_impact.py | 4 +--- 9 files changed, 1 insertion(+), 17 deletions(-) diff --git a/Tools/c-analyzer/c_parser/preprocessor/clang.py b/Tools/c-analyzer/c_parser/preprocessor/clang.py index 574a23f8f6d6f9f..738c261d1ecd80a 100644 --- a/Tools/c-analyzer/c_parser/preprocessor/clang.py +++ b/Tools/c-analyzer/c_parser/preprocessor/clang.py @@ -1,5 +1,4 @@ import os.path -import re, sys from . import common as _common from . import gcc as _gcc diff --git a/Tools/c-analyzer/distutils/util.py b/Tools/c-analyzer/distutils/util.py index f687a28ec2f40e8..c8e92658d953661 100644 --- a/Tools/c-analyzer/distutils/util.py +++ b/Tools/c-analyzer/distutils/util.py @@ -8,7 +8,6 @@ import re import string import sys -from distutils.errors import DistutilsPlatformError def get_host_platform(): """Return a string that identifies the current platform. This is used mainly to diff --git a/Tools/cases_generator/generators_common.py b/Tools/cases_generator/generators_common.py index bdc4324b6702006..8c66ad4885ccfc8 100644 --- a/Tools/cases_generator/generators_common.py +++ b/Tools/cases_generator/generators_common.py @@ -7,14 +7,12 @@ analysis_error, Label, CodeSection, - Uop, ) from cwriter import CWriter from typing import Callable, TextIO, Iterator, Iterable from lexer import Token from stack import Storage, StackError from parser import Stmt, SimpleStmt, BlockStmt, IfStmt, ForStmt, WhileStmt, MacroIfStmt -from stack import PRINT_STACKS DEBUG = False class TokenIterator: diff --git a/Tools/cases_generator/opcode_metadata_generator.py b/Tools/cases_generator/opcode_metadata_generator.py index 21407ad7df1e9a5..00f6804f1724b59 100644 --- a/Tools/cases_generator/opcode_metadata_generator.py +++ b/Tools/cases_generator/opcode_metadata_generator.py @@ -19,7 +19,6 @@ cflags, ) from cwriter import CWriter -from dataclasses import dataclass from typing import TextIO from stack import get_stack_effect diff --git a/Tools/cases_generator/parser.py b/Tools/cases_generator/parser.py index ccf8bf649520ff6..aa6c0b1446fb76b 100644 --- a/Tools/cases_generator/parser.py +++ b/Tools/cases_generator/parser.py @@ -20,7 +20,6 @@ MacroIfStmt, ) -import pprint CodeDef = InstDef | LabelDef diff --git a/Tools/cases_generator/tier1_generator.py b/Tools/cases_generator/tier1_generator.py index d2fa749e1417f51..ebc914a6837e2eb 100644 --- a/Tools/cases_generator/tier1_generator.py +++ b/Tools/cases_generator/tier1_generator.py @@ -9,8 +9,6 @@ Analysis, Instruction, Uop, - Label, - CodeSection, Part, analyze_files, Skip, @@ -24,13 +22,9 @@ write_header, type_and_null, Emitter, - TokenIterator, - always_true, - emit_to, ) from cwriter import CWriter from typing import TextIO -from lexer import Token from stack import Local, Stack, StackError, get_stack_effect, Storage DEFAULT_OUTPUT = ROOT / "Python/generated_cases.c.h" diff --git a/Tools/inspection/benchmark_external_inspection.py b/Tools/inspection/benchmark_external_inspection.py index 8e367422a961da2..b7aa0e5de7ed99b 100644 --- a/Tools/inspection/benchmark_external_inspection.py +++ b/Tools/inspection/benchmark_external_inspection.py @@ -4,7 +4,6 @@ import sys import contextlib import tempfile -import os import argparse from _colorize import get_colors, can_colorize diff --git a/Tools/jit/_stencils.py b/Tools/jit/_stencils.py index e2ae3d988fc7ac9..d4547dc8e8e3c12 100644 --- a/Tools/jit/_stencils.py +++ b/Tools/jit/_stencils.py @@ -2,7 +2,6 @@ import dataclasses import enum -import sys import typing import _schema diff --git a/Tools/picklebench/memory_dos_impact.py b/Tools/picklebench/memory_dos_impact.py index 3bad6586c469437..0a7cef8668565c8 100755 --- a/Tools/picklebench/memory_dos_impact.py +++ b/Tools/picklebench/memory_dos_impact.py @@ -24,7 +24,6 @@ import argparse import gc -import io import json import os import pickle @@ -32,11 +31,10 @@ import struct import subprocess import sys -import tempfile import tracemalloc from pathlib import Path from time import perf_counter -from typing import Any, Dict, List, Tuple, Optional +from typing import Any, Dict, List, Tuple # Configuration From 113e2c0edba7185940cd56869e8d9326fd7ea6c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20S=C5=82awecki?= Date: Sun, 14 Jun 2026 18:06:30 +0200 Subject: [PATCH 154/185] gh-151390: Colorize `match +` and `match -` in the REPL (#151391) --- Lib/_pyrepl/utils.py | 2 +- Lib/test/test_pyrepl/test_utils.py | 16 ++++++++++++++++ ...6-06-12-07-20-08.gh-issue-151390.CmYN9EeJ.rst | 1 + 3 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2026-06-12-07-20-08.gh-issue-151390.CmYN9EeJ.rst diff --git a/Lib/_pyrepl/utils.py b/Lib/_pyrepl/utils.py index f2837a1b8eb95e7..230dae35af665ab 100644 --- a/Lib/_pyrepl/utils.py +++ b/Lib/_pyrepl/utils.py @@ -259,7 +259,7 @@ def is_soft_keyword_used(*tokens: TI | None) -> bool: None | TI(T.NEWLINE) | TI(T.INDENT) | TI(string=":"), TI(string="match"), TI(T.NUMBER | T.STRING | T.FSTRING_START | T.TSTRING_START) - | TI(T.OP, string="(" | "*" | "[" | "{" | "~" | "...") + | TI(T.OP, string="(" | "*" | "-" | "+" | "[" | "{" | "~" | "...") ): return True case ( diff --git a/Lib/test/test_pyrepl/test_utils.py b/Lib/test/test_pyrepl/test_utils.py index 0b873d32b62b5d2..ebbd06213c69aff 100644 --- a/Lib/test/test_pyrepl/test_utils.py +++ b/Lib/test/test_pyrepl/test_utils.py @@ -133,6 +133,22 @@ def test_gen_colors_keyword_highlighting(self): ("1", "number"), ], ), + ( + "match +1", + [ + ("match", "soft_keyword"), + ("+", "op"), + ("1", "number"), + ], + ), + ( + "match -1", + [ + ("match", "soft_keyword"), + ("-", "op"), + ("1", "number"), + ], + ), ] for code, expected_highlights in cases: with self.subTest(code=code): diff --git a/Misc/NEWS.d/next/Library/2026-06-12-07-20-08.gh-issue-151390.CmYN9EeJ.rst b/Misc/NEWS.d/next/Library/2026-06-12-07-20-08.gh-issue-151390.CmYN9EeJ.rst new file mode 100644 index 000000000000000..ff8de30599c6ad5 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-12-07-20-08.gh-issue-151390.CmYN9EeJ.rst @@ -0,0 +1 @@ +Colorize ``match`` in the :term:`REPL` when followed by a unary ``+`` or ``-`` operator. Patch by Bartosz Sławecki. From 56445e691404e38dbb8c5e125af8eee929bf111e Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Sun, 14 Jun 2026 20:17:45 +0100 Subject: [PATCH 155/185] gh-130197: Test `pygettext`'s `--version` CLI option (#133022) --- Lib/test/test_tools/test_i18n.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Lib/test/test_tools/test_i18n.py b/Lib/test/test_tools/test_i18n.py index d1831d68f0281d9..7583b61480e3709 100644 --- a/Lib/test/test_tools/test_i18n.py +++ b/Lib/test/test_tools/test_i18n.py @@ -427,6 +427,11 @@ def test_help_text(self): self.assertEqual(res.out, b'') self.assertIn(b'pygettext -- Python equivalent of xgettext(1)', res.err) + def test_version_text(self): + """Test that the version text is displayed.""" + res = assert_python_ok(self.script, '--version') + self.assertIn(b'pygettext.py (xgettext for Python) 1.5', res.out) + def test_error_messages(self): """Test that pygettext outputs error messages to stderr.""" stderr = self.get_stderr(dedent('''\ From 60919ece1cc756bf1f1b87b1b6b63618edaa127b Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 15 Jun 2026 10:29:52 +0200 Subject: [PATCH 156/185] gh-149671: Restore compatibility with setuptools -nspkg.pth files in site module (#151319) Inject the "sitedir" variable in the frame which executes ".pth" code. --- Lib/site.py | 5 +++++ Lib/test/test_site.py | 14 ++++++++++++++ .../2026-06-11-11-52-23.gh-issue-149671.6Rpr5r.rst | 3 +++ 3 files changed, 22 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2026-06-11-11-52-23.gh-issue-149671.6Rpr5r.rst diff --git a/Lib/site.py b/Lib/site.py index b7f5c7f0246bc1b..d06549b8df800e3 100644 --- a/Lib/site.py +++ b/Lib/site.py @@ -505,6 +505,11 @@ def _exec_imports(self): # batch. In that case, PEP 829 says the import lines are # suppressed in favor of the .start's entry points. for filename, imports in self._importexecs.items(): + # Inject 'sitedir' local variable in the current frame for + # compatibility with Python 3.14. Especially, "-nspkg.pth" files + # generated by setuptools use: sys._getframe(1).f_locals['sitedir']. + sitedir = os.path.dirname(filename) + # Given "/path/to/foo.pth", check whether "/path/to/foo.start" was # registered in this same batch. name, dot, pth = filename.rpartition(".") diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index 5fd65ad999210e4..bcb51ed7d8476a8 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -233,6 +233,20 @@ def test_addsitedir_hidden_file_attribute(self): self.assertNotIn(site.makepath(pth_file.good_dir_path)[0], sys.path) self.assertIn(pth_file.base_dir, sys.path) + def test_sitedir_variable(self): + # gh-149671: setuptools use of `-nspkg.pth` files in Python < 3.15. + code = '; '.join(( + # Code used by "-nspkg.pth" files generated by setuptools. + "import sys", + "sitedir = sys._getframe(1).f_locals['sitedir']", + "print(sitedir)", + )) + pth_dir, pth_fn = self.make_pth(code) + with support.captured_stdout() as stdout: + known_paths = site.addpackage(pth_dir, pth_fn, set()) + sitedir = stdout.getvalue().rstrip() + self.assertEqual(sitedir, pth_dir) + # This tests _getuserbase, hence the double underline # to distinguish from a test for getuserbase def test__getuserbase(self): diff --git a/Misc/NEWS.d/next/Library/2026-06-11-11-52-23.gh-issue-149671.6Rpr5r.rst b/Misc/NEWS.d/next/Library/2026-06-11-11-52-23.gh-issue-149671.6Rpr5r.rst new file mode 100644 index 000000000000000..5c08828e5fd77ec --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-11-11-52-23.gh-issue-149671.6Rpr5r.rst @@ -0,0 +1,3 @@ +Restore compatibility with setuptools ``-nspkg.pth`` files in the :mod:`site` +module. Inject ``sitedir`` variable in the frame which executes pth code. +Patch by Victor Stinner. From fd03bdaad46129a906fc7857da6c1ac0873ef2d2 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Mon, 15 Jun 2026 12:14:11 +0300 Subject: [PATCH 157/185] Add `.toml` to `.editorconfig` (#151481) --- .editorconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.editorconfig b/.editorconfig index 25bc5935258bd14..ab1f7ce8425769e 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,11 +1,11 @@ root = true -[*.{py,c,cpp,h,js,rst,md,yml,yaml,gram}] +[*.{py,c,cpp,h,js,rst,md,yml,yaml,toml,gram}] trim_trailing_whitespace = true insert_final_newline = true indent_style = space -[*.{py,c,cpp,h,gram}] +[*.{py,c,cpp,h,toml,gram}] indent_size = 4 [*.rst] From 51f5834b260ba7873c41f8e2a3fa2c4e48838473 Mon Sep 17 00:00:00 2001 From: JasonMendoza2008 <56092290+JasonMendoza2008@users.noreply.github.com> Date: Mon, 15 Jun 2026 11:31:04 +0200 Subject: [PATCH 158/185] gh-137759: Replace _PyObject_HashFast() with PyObject_Hash() in setobject.c (#137828) Replace also _PyObject_HashFast() with PyObject_Hash() in _collections._count_elements(). Rename _PyObject_HashFast() to _PyObject_HashDictKey(), and mark it as Py_ALWAYS_INLINE. Only use _PyObject_HashDictKey() on dictionaries. --- Include/internal/pycore_object.h | 11 ++++++++--- Modules/_collectionsmodule.c | 2 +- Objects/dictobject.c | 30 +++++++++++++++--------------- Objects/setobject.c | 14 +++++++------- Objects/typeobject.c | 4 ++-- 5 files changed, 33 insertions(+), 28 deletions(-) diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h index dd7955c2770b997..98880cd83780dca 100644 --- a/Include/internal/pycore_object.h +++ b/Include/internal/pycore_object.h @@ -830,9 +830,14 @@ _PyObject_IS_GC(PyObject *obj) && (type->tp_is_gc == NULL || type->tp_is_gc(obj))); } -// Fast inlined version of PyObject_Hash() -static inline Py_hash_t -_PyObject_HashFast(PyObject *op) +// Fast inlined version of PyObject_Hash(). Dictionaries are very +// likely to include string keys (class and instance attributes, +// json, ...) so we include a fast path for strings. +// This function should not be used in a collection if str is not +// very likely, since it is slower than PyObject_Hash() on types +// other than str. See gh-137759. +static inline Py_ALWAYS_INLINE Py_hash_t +_PyObject_HashDictKey(PyObject *op) { if (PyUnicode_CheckExact(op)) { Py_hash_t hash = PyUnstable_Unicode_GET_CACHED_HASH(op); diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c index 5ca6362406a78b9..e96a546a818d3d7 100644 --- a/Modules/_collectionsmodule.c +++ b/Modules/_collectionsmodule.c @@ -2590,7 +2590,7 @@ _collections__count_elements_impl(PyObject *module, PyObject *mapping, if (key == NULL) break; - hash = _PyObject_HashFast(key); + hash = _PyObject_HashDictKey(key); if (hash == -1) { goto done; } diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 0a6d0f9199641ee..ac2f210d023487d 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -2384,7 +2384,7 @@ dict_getitem(PyObject *op, PyObject *key, const char *warnmsg) } PyDictObject *mp = (PyDictObject *)op; - Py_hash_t hash = _PyObject_HashFast(key); + Py_hash_t hash = _PyObject_HashDictKey(key); if (hash == -1) { PyErr_FormatUnraisable(warnmsg); return NULL; @@ -2456,7 +2456,7 @@ _PyDict_LookupIndexAndValue(PyDictObject *mp, PyObject *key, PyObject **value) assert(PyDict_CheckExact((PyObject*)mp)); assert(PyUnicode_CheckExact(key)); - Py_hash_t hash = _PyObject_HashFast(key); + Py_hash_t hash = _PyObject_HashDictKey(key); if (hash == -1) { dict_unhashable_type((PyObject*)mp, key); return -1; @@ -2560,7 +2560,7 @@ PyDict_GetItemRef(PyObject *op, PyObject *key, PyObject **result) return -1; } - Py_hash_t hash = _PyObject_HashFast(key); + Py_hash_t hash = _PyObject_HashDictKey(key); if (hash == -1) { dict_unhashable_type(op, key); *result = NULL; @@ -2576,7 +2576,7 @@ _PyDict_GetItemRef_Unicode_LockHeld(PyDictObject *op, PyObject *key, PyObject ** ASSERT_DICT_LOCKED(op); assert(PyUnicode_CheckExact(key)); - Py_hash_t hash = _PyObject_HashFast(key); + Py_hash_t hash = _PyObject_HashDictKey(key); if (hash == -1) { dict_unhashable_type((PyObject*)op, key); *result = NULL; @@ -2614,7 +2614,7 @@ PyDict_GetItemWithError(PyObject *op, PyObject *key) PyErr_BadInternalCall(); return NULL; } - hash = _PyObject_HashFast(key); + hash = _PyObject_HashDictKey(key); if (hash == -1) { dict_unhashable_type(op, key); return NULL; @@ -2673,7 +2673,7 @@ _PyDict_LoadGlobal(PyDictObject *globals, PyDictObject *builtins, PyObject *key) Py_hash_t hash; PyObject *value; - hash = _PyObject_HashFast(key); + hash = _PyObject_HashDictKey(key); if (hash == -1) { return NULL; } @@ -2697,7 +2697,7 @@ _PyDict_LoadGlobalStackRef(PyDictObject *globals, PyDictObject *builtins, PyObje Py_ssize_t ix; Py_hash_t hash; - hash = _PyObject_HashFast(key); + hash = _PyObject_HashDictKey(key); if (hash == -1) { *res = PyStackRef_NULL; return; @@ -2774,7 +2774,7 @@ setitem_take2_lock_held_known_hash(PyDictObject *mp, PyObject *key, PyObject *va static int setitem_take2_lock_held(PyDictObject *mp, PyObject *key, PyObject *value) { - Py_hash_t hash = _PyObject_HashFast(key); + Py_hash_t hash = _PyObject_HashDictKey(key); if (hash == -1) { dict_unhashable_type((PyObject*)mp, key); Py_DECREF(key); @@ -2952,7 +2952,7 @@ int PyDict_DelItem(PyObject *op, PyObject *key) { assert(key); - Py_hash_t hash = _PyObject_HashFast(key); + Py_hash_t hash = _PyObject_HashDictKey(key); if (hash == -1) { dict_unhashable_type(op, key); return -1; @@ -3296,7 +3296,7 @@ pop_lock_held(PyObject *op, PyObject *key, PyObject **result) return 0; } - Py_hash_t hash = _PyObject_HashFast(key); + Py_hash_t hash = _PyObject_HashDictKey(key); if (hash == -1) { dict_unhashable_type(op, key); if (result) { @@ -3734,7 +3734,7 @@ _PyDict_SubscriptKnownHash(PyObject *self, PyObject *key, Py_hash_t hash) PyObject * _PyDict_Subscript(PyObject *self, PyObject *key) { - Py_hash_t hash = _PyObject_HashFast(key); + Py_hash_t hash = _PyObject_HashDictKey(key); if (hash == -1) { dict_unhashable_type(self, key); return NULL; @@ -4686,7 +4686,7 @@ dict_get_impl(PyDictObject *self, PyObject *key, PyObject *default_value) Py_hash_t hash; Py_ssize_t ix; - hash = _PyObject_HashFast(key); + hash = _PyObject_HashDictKey(key); if (hash == -1) { dict_unhashable_type((PyObject*)self, key); return NULL; @@ -4723,7 +4723,7 @@ dict_setdefault_ref_lock_held(PyObject *d, PyObject *key, PyObject *default_valu Py_hash_t hash; Py_ssize_t ix; - hash = _PyObject_HashFast(key); + hash = _PyObject_HashDictKey(key); if (hash == -1) { dict_unhashable_type(d, key); if (result) { @@ -5165,7 +5165,7 @@ static PyMethodDef mapp_methods[] = { static int dict_contains(PyObject *op, PyObject *key) { - Py_hash_t hash = _PyObject_HashFast(key); + Py_hash_t hash = _PyObject_HashDictKey(key); if (hash == -1) { dict_unhashable_type(op, key); return -1; @@ -7298,7 +7298,7 @@ _PyDict_SetItem_LockHeld(PyDictObject *dict, PyObject *name, PyObject *value) } if (value == NULL) { - Py_hash_t hash = _PyObject_HashFast(name); + Py_hash_t hash = _PyObject_HashDictKey(name); if (hash == -1) { dict_unhashable_type((PyObject*)dict, name); return -1; diff --git a/Objects/setobject.c b/Objects/setobject.c index 50c5f66c2afc1b8..d198794849f0d15 100644 --- a/Objects/setobject.c +++ b/Objects/setobject.c @@ -361,7 +361,7 @@ set_unhashable_type(PyObject *key) int _PySet_AddTakeRef(PySetObject *so, PyObject *key) { - Py_hash_t hash = _PyObject_HashFast(key); + Py_hash_t hash = PyObject_Hash(key); if (hash == -1) { set_unhashable_type(key); Py_DECREF(key); @@ -600,7 +600,7 @@ set_discard_entry(PySetObject *so, PyObject *key, Py_hash_t hash) static int set_add_key(PySetObject *so, PyObject *key) { - Py_hash_t hash = _PyObject_HashFast(key); + Py_hash_t hash = PyObject_Hash(key); if (hash == -1) { set_unhashable_type(key); return -1; @@ -611,7 +611,7 @@ set_add_key(PySetObject *so, PyObject *key) static int set_contains_key(PySetObject *so, PyObject *key) { - Py_hash_t hash = _PyObject_HashFast(key); + Py_hash_t hash = PyObject_Hash(key); if (hash == -1) { set_unhashable_type(key); return -1; @@ -622,7 +622,7 @@ set_contains_key(PySetObject *so, PyObject *key) static int set_discard_key(PySetObject *so, PyObject *key) { - Py_hash_t hash = _PyObject_HashFast(key); + Py_hash_t hash = PyObject_Hash(key); if (hash == -1) { set_unhashable_type(key); return -1; @@ -2514,7 +2514,7 @@ _PySet_Contains(PySetObject *so, PyObject *key) { assert(so); - Py_hash_t hash = _PyObject_HashFast(key); + Py_hash_t hash = PyObject_Hash(key); if (hash == -1) { if (!PySet_Check(key) || !PyErr_ExceptionMatches(PyExc_TypeError)) { set_unhashable_type(key); @@ -2574,7 +2574,7 @@ static PyObject * frozenset___contains___impl(PySetObject *so, PyObject *key) /*[clinic end generated code: output=2301ed91bc3a6dd5 input=2f04922a98d8bab7]*/ { - Py_hash_t hash = _PyObject_HashFast(key); + Py_hash_t hash = PyObject_Hash(key); if (hash == -1) { if (!PySet_Check(key) || !PyErr_ExceptionMatches(PyExc_TypeError)) { set_unhashable_type(key); @@ -3039,7 +3039,7 @@ PySet_Contains(PyObject *anyset, PyObject *key) } PySetObject *so = (PySetObject *)anyset; - Py_hash_t hash = _PyObject_HashFast(key); + Py_hash_t hash = PyObject_Hash(key); if (hash == -1) { set_unhashable_type(key); return -1; diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 388e8891c9c7399..865c32f02b13b44 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -3793,7 +3793,7 @@ solid_base(PyTypeObject *type) // or when __bases__ is re-assigned. Since the slots are read without atomic // operations and without locking, we can only safely update them while the // world is stopped. However, with the world stopped, we are very limited on -// which APIs can be safely used. For example, calling _PyObject_HashFast() +// which APIs can be safely used. For example, calling _PyObject_HashDictKey() // or _PyDict_GetItemRef_KnownHash() are not safe and can potentially cause // deadlocks. Hashing can be re-entrant and _PyDict_GetItemRef_KnownHash can // acquire a lock if the dictionary is not owned by the current thread, to @@ -6134,7 +6134,7 @@ PyObject_GetItemData(PyObject *obj) static int find_name_in_mro(PyTypeObject *type, PyObject *name, _PyStackRef *out) { - Py_hash_t hash = _PyObject_HashFast(name); + Py_hash_t hash = _PyObject_HashDictKey(name); if (hash == -1) { PyErr_Clear(); return -1; From 8cd462efe3ed536af628ab48c27f93035e10b57d Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 15 Jun 2026 11:46:52 +0200 Subject: [PATCH 159/185] gh-146102: Don't clear exception on success in odictobject.c (#151347) Calling PyErr_Clear() on success can mask a pending exception. Replace it with an assertion checking that no exception is set. --- Objects/odictobject.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Objects/odictobject.c b/Objects/odictobject.c index 6f05395b18d781f..14c1b02405822cd 100644 --- a/Objects/odictobject.c +++ b/Objects/odictobject.c @@ -2206,13 +2206,14 @@ update __setitem__ static int mutablemapping_add_pairs(PyObject *self, PyObject *pairs) { + assert(!PyErr_Occurred()); + PyObject *pair, *iterator, *unexpected; int res = 0; iterator = PyObject_GetIter(pairs); if (iterator == NULL) return -1; - PyErr_Clear(); while ((pair = PyIter_Next(iterator)) != NULL) { /* could be more efficient (see UNPACK_SEQUENCE in ceval.c) */ From d440325921d46bf83fcb337b3378e2cbe1931d8e Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 15 Jun 2026 11:47:58 +0200 Subject: [PATCH 160/185] gh-146102: Fix type slot_bf_getbuffer() error handling (#151346) Call PyBuffer_Release() if PyObject_GC_New() fails. Fix also bytes_join(): only call Py_DECREF(item) after formatting the error message which uses item. --- Objects/stringlib/join.h | 2 +- Objects/typeobject.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Objects/stringlib/join.h b/Objects/stringlib/join.h index deebfeadc0f4fd8..fe460d41f6700f8 100644 --- a/Objects/stringlib/join.h +++ b/Objects/stringlib/join.h @@ -72,11 +72,11 @@ STRINGLIB(bytes_join)(PyObject *sep, PyObject *iterable) drops the sequence's last reference to it. */ Py_INCREF(item); if (PyObject_GetBuffer(item, &buffers[i], PyBUF_SIMPLE) != 0) { - Py_DECREF(item); PyErr_Format(PyExc_TypeError, "sequence item %zd: expected a bytes-like object, " "%.80s found", i, Py_TYPE(item)->tp_name); + Py_DECREF(item); goto error; } Py_DECREF(item); diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 865c32f02b13b44..881ed58d275ac7a 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -11332,6 +11332,7 @@ slot_bf_getbuffer(PyObject *self, Py_buffer *buffer, int flags) wrapper = PyObject_GC_New(PyBufferWrapper, &_PyBufferWrapper_Type); if (wrapper == NULL) { + PyBuffer_Release(buffer); goto fail; } wrapper->mv = ret; From 95ded16d33d77f76e9f6bb710a5ede981c08b95c Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 15 Jun 2026 12:23:04 +0200 Subject: [PATCH 161/185] gh-139227: Remove unused _PyRuntime.imports.pkgcontext (#151490) The global variable has been replaced by a new thread local variable "pkgcontext" in Python/import.c. --- Include/internal/pycore_interp_structs.h | 2 -- Python/import.c | 3 --- 2 files changed, 5 deletions(-) diff --git a/Include/internal/pycore_interp_structs.h b/Include/internal/pycore_interp_structs.h index 956fa290f0ad0e7..5500c70a3b0aad9 100644 --- a/Include/internal/pycore_interp_structs.h +++ b/Include/internal/pycore_interp_structs.h @@ -308,8 +308,6 @@ struct _import_runtime_state { Modules are added there and looked up in _imp.find_extension(). */ struct _Py_hashtable_t *hashtable; } extensions; - /* Package context -- the full module name for package imports */ - const char * pkgcontext; }; struct _import_state { diff --git a/Python/import.c b/Python/import.c index 42bfe15121f84f7..6da6faf5f28cc3b 100644 --- a/Python/import.c +++ b/Python/import.c @@ -81,8 +81,6 @@ static struct _inittab *inittab_copy = NULL; #define LAST_MODULE_INDEX _PyRuntime.imports.last_module_index #define EXTENSIONS _PyRuntime.imports.extensions -#define PKGCONTEXT (_PyRuntime.imports.pkgcontext) - /*******************************/ /* interpreter import state */ @@ -883,7 +881,6 @@ _PyImport_ClearModulesByIndex(PyInterpreterState *interp) */ static _Py_thread_local const char *pkgcontext = NULL; -# undef PKGCONTEXT # define PKGCONTEXT pkgcontext const char * From a05e6f790a822327284679ecdba50f1db844cd23 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 15 Jun 2026 13:11:26 +0200 Subject: [PATCH 162/185] Fix issues reported by cpython-review-toolkit in faulthandler (#151341) * snprintf() is not async-signal-safe: replace it with _Py_DumpDecimal(). * Fix tid type from 'long' to 'unsigned long'. * Replace PyLong_AsLong() with PyLong_AsInt(). * Avoid unnecessary narrowing cast on _Py_write_noraise() call. --- Modules/faulthandler.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/Modules/faulthandler.c b/Modules/faulthandler.c index 7c727d8c2d4ff0e..3b0647152ceffed 100644 --- a/Modules/faulthandler.c +++ b/Modules/faulthandler.c @@ -101,8 +101,6 @@ static int faulthandler_get_fileno(PyObject **file_ptr) { PyObject *result; - long fd_long; - int fd; PyObject *file = *file_ptr; if (file == NULL || file == Py_None) { @@ -124,7 +122,7 @@ faulthandler_get_fileno(PyObject **file_ptr) return -1; } } - fd = PyLong_AsInt(file); + int fd = PyLong_AsInt(file); if (fd == -1 && PyErr_Occurred()) return -1; if (fd < 0) { @@ -145,15 +143,16 @@ faulthandler_get_fileno(PyObject **file_ptr) return -1; } - fd = -1; + int fd; if (PyLong_Check(result)) { - fd_long = PyLong_AsLong(result); - if (0 <= fd_long && fd_long < INT_MAX) - fd = (int)fd_long; + fd = PyLong_AsInt(result); + } + else { + fd = -1; } Py_DECREF(result); - if (fd == -1) { + if (fd < 0) { PyErr_SetString(PyExc_RuntimeError, "file.fileno() is not a valid file descriptor"); Py_DECREF(file); @@ -407,10 +406,8 @@ faulthandler_fatal_error(int signum) PUTS(fd, "\n\n"); } else { - char unknown_signum[23] = {0,}; - snprintf(unknown_signum, 23, "%d", signum); PUTS(fd, "Fatal Python error from unexpected signum: "); - PUTS(fd, unknown_signum); + _Py_DumpDecimal(fd, signum); PUTS(fd, "\n\n"); } @@ -713,7 +710,7 @@ faulthandler_thread(void *unused) /* Timeout => dump traceback */ assert(st == PY_LOCK_FAILURE); - (void)_Py_write_noraise(thread.fd, thread.header, (int)thread.header_len); + (void)_Py_write_noraise(thread.fd, thread.header, thread.header_len); errmsg = PyUnstable_DumpTracebackThreads(thread.fd, thread.interp, NULL, thread.max_threads); @@ -1224,7 +1221,7 @@ static PyObject * faulthandler__fatal_error_c_thread_impl(PyObject *module) /*[clinic end generated code: output=101bc8aaf4a5eec1 input=fbdca6fffd639a39]*/ { - long tid; + unsigned long tid; PyThread_type_lock lock; faulthandler_suppress_crash_report(); @@ -1236,7 +1233,7 @@ faulthandler__fatal_error_c_thread_impl(PyObject *module) PyThread_acquire_lock(lock, WAIT_LOCK); tid = PyThread_start_new_thread(faulthandler_fatal_error_thread, lock); - if (tid == -1) { + if (tid == PYTHREAD_INVALID_THREAD_ID) { PyThread_free_lock(lock); PyErr_SetString(PyExc_RuntimeError, "unable to start the thread"); return NULL; From d572a3faa0b354a9a2151e73bf9101bcfcf4304d Mon Sep 17 00:00:00 2001 From: Nick Begg Date: Mon, 15 Jun 2026 16:25:08 +0200 Subject: [PATCH 163/185] gh-148853: Catch PermissionError in test in_systemd_nspawn_sync_suppressed() (#148854) /run/ on my FreeBSD install is not readable causing failing test. Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> --- Lib/test/support/__init__.py | 2 +- .../next/Tests/2026-04-24-01-38-56.gh-issue-148853._uM4_Q.rst | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Tests/2026-04-24-01-38-56.gh-issue-148853._uM4_Q.rst diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index cd85ef60a80f4bf..e9966e1f7a6d495 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -3159,7 +3159,7 @@ def in_systemd_nspawn_sync_suppressed() -> bool: with open("/run/systemd/container", "rb") as fp: if fp.read().rstrip() != b"systemd-nspawn": return False - except FileNotFoundError: + except (FileNotFoundError, PermissionError): return False # If systemd-nspawn is used, O_SYNC flag will immediately diff --git a/Misc/NEWS.d/next/Tests/2026-04-24-01-38-56.gh-issue-148853._uM4_Q.rst b/Misc/NEWS.d/next/Tests/2026-04-24-01-38-56.gh-issue-148853._uM4_Q.rst new file mode 100644 index 000000000000000..9d3fbc2590dc7a2 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2026-04-24-01-38-56.gh-issue-148853._uM4_Q.rst @@ -0,0 +1,2 @@ +Fix tests failing on FreeBSD in test.support's +in_systemd_nspawn_sync_suppressed() due to unreadable /run directory. From c35c9e94f0efafbbc503c45ced47894e3b3e93b6 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 15 Jun 2026 17:36:50 +0300 Subject: [PATCH 164/185] gh-149079: Optimize sorting in unicodedata.normalize() (GH-150782) Sort the Py_UCS4 buffer instead of PyUnicodeObject. This allows to avoid the use of PyUnicode_READ() and PyUnicode_WRITE(). --- Modules/unicodedata.c | 62 ++++++++++++++++--------------------------- 1 file changed, 23 insertions(+), 39 deletions(-) diff --git a/Modules/unicodedata.c b/Modules/unicodedata.c index bcdcc624e66f930..5f0e7ab6ec220d6 100644 --- a/Modules/unicodedata.c +++ b/Modules/unicodedata.c @@ -560,39 +560,36 @@ get_decomp_record(PyObject *self, Py_UCS4 code, #define CANONICAL_ORDERING_COUNTING_SORT_THRESHOLD 20 static void -canonical_ordering_sort_insertion(int kind, void *data, - Py_ssize_t start, Py_ssize_t end) +canonical_ordering_sort_insertion(Py_UCS4 *data, Py_ssize_t length) { - for (Py_ssize_t i = start + 1; i < end; i++) { - Py_UCS4 code = PyUnicode_READ(kind, data, i); + for (Py_ssize_t i = 1; i < length; i++) { + Py_UCS4 code = data[i]; unsigned char combining = _getrecord_ex(code)->combining; Py_ssize_t j = i; - while (j > start) { - Py_UCS4 previous = PyUnicode_READ(kind, data, j - 1); + while (j > 0) { + Py_UCS4 previous = data[j - 1]; if (_getrecord_ex(previous)->combining <= combining) { break; } - PyUnicode_WRITE(kind, data, j, previous); + data[j] = previous; j--; } if (j != i) { - PyUnicode_WRITE(kind, data, j, code); + data[j] = code; } } } static void -canonical_ordering_sort_counting(int kind, void *data, - Py_ssize_t start, Py_ssize_t end, +canonical_ordering_sort_counting(Py_UCS4 *data, Py_ssize_t length, Py_UCS4 *sortbuf) { Py_ssize_t counts[256] = {0}; - Py_ssize_t run_length = end - start; Py_ssize_t total = 0; - for (Py_ssize_t i = start; i < end; i++) { - Py_UCS4 code = PyUnicode_READ(kind, data, i); + for (Py_ssize_t i = 0; i < length; i++) { + Py_UCS4 code = data[i]; unsigned char combining = _getrecord_ex(code)->combining; counts[combining]++; } @@ -604,14 +601,12 @@ canonical_ordering_sort_counting(int kind, void *data, } /* Reuse counts[] as the next output slot for each CCC. */ - for (Py_ssize_t i = start; i < end; i++) { - Py_UCS4 code = PyUnicode_READ(kind, data, i); + for (Py_ssize_t i = 0; i < length; i++) { + Py_UCS4 code = data[i]; unsigned char combining = _getrecord_ex(code)->combining; sortbuf[counts[combining]++] = code; } - for (Py_ssize_t i = 0; i < run_length; i++) { - PyUnicode_WRITE(kind, data, start + i, sortbuf[i]); - } + memcpy(data, sortbuf, length * sizeof(Py_UCS4)); } static PyObject* @@ -620,9 +615,8 @@ nfd_nfkd(PyObject *self, PyObject *input, int k) PyObject *result; Py_UCS4 *output; Py_ssize_t i, o, osize; - int input_kind, result_kind; + int input_kind; const void *input_data; - void *result_data; /* Longest decomposition in Unicode 3.2: U+FDFA */ Py_UCS4 stack[20]; Py_ssize_t space, isize; @@ -715,22 +709,13 @@ nfd_nfkd(PyObject *self, PyObject *input, int k) } } - result = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, - output, o); - PyMem_Free(output); - if (!result) - return NULL; - - result_kind = PyUnicode_KIND(result); - result_data = PyUnicode_DATA(result); - /* Sort each consecutive combining-character run canonically. */ i = 0; while (i < o) { Py_ssize_t run_length, run_start; int needs_sort = 0; - Py_UCS4 ch = PyUnicode_READ(result_kind, result_data, i); + Py_UCS4 ch = output[i]; prev = _getrecord_ex(ch)->combining; if (prev == 0) { i++; @@ -739,7 +724,7 @@ nfd_nfkd(PyObject *self, PyObject *input, int k) run_start = i++; while (i < o) { - Py_UCS4 ch = PyUnicode_READ(result_kind, result_data, i); + Py_UCS4 ch = output[i]; cur = _getrecord_ex(ch)->combining; if (cur == 0) { break; @@ -756,29 +741,28 @@ nfd_nfkd(PyObject *self, PyObject *input, int k) run_length = i - run_start; if (run_length < CANONICAL_ORDERING_COUNTING_SORT_THRESHOLD) { - canonical_ordering_sort_insertion(result_kind, result_data, - run_start, i); + canonical_ordering_sort_insertion(output + run_start, run_length); continue; } if (run_length > sortbuflen) { - Py_UCS4 *new_sortbuf = PyMem_Resize(sortbuf, - Py_UCS4, - run_length); + Py_UCS4 *new_sortbuf = PyMem_Resize(sortbuf, Py_UCS4, run_length); if (new_sortbuf == NULL) { PyErr_NoMemory(); PyMem_Free(sortbuf); - Py_DECREF(result); + PyMem_Free(output); return NULL; } sortbuf = new_sortbuf; sortbuflen = run_length; } - canonical_ordering_sort_counting(result_kind, result_data, - run_start, i, sortbuf); + canonical_ordering_sort_counting(output + run_start, run_length, + sortbuf); } PyMem_Free(sortbuf); + result = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, output, o); + PyMem_Free(output); return result; } From 9c9ad0f56a548f247c99f0adf556ede6f964ece0 Mon Sep 17 00:00:00 2001 From: Xiao Yuan Date: Mon, 15 Jun 2026 18:05:29 +0300 Subject: [PATCH 165/185] gh-92455: Respect case-sensitive mimetype suffixes (GH-148782) --- Doc/library/mimetypes.rst | 8 +++- Lib/mimetypes.py | 22 ++++++++-- Lib/test/test_mimetypes.py | 44 +++++++++++++++++++ ...6-04-20-01-24-22.gh-issue-92455.vXhmad.rst | 3 ++ 4 files changed, 72 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-04-20-01-24-22.gh-issue-92455.vXhmad.rst diff --git a/Doc/library/mimetypes.rst b/Doc/library/mimetypes.rst index f33098faf7d8a77..5c29fff146eef00 100644 --- a/Doc/library/mimetypes.rst +++ b/Doc/library/mimetypes.rst @@ -39,8 +39,8 @@ the information :func:`init` sets up. (e.g. :program:`compress` or :program:`gzip`). The encoding is suitable for use as a :mailheader:`Content-Encoding` header, **not** as a :mailheader:`Content-Transfer-Encoding` header. The mappings are table driven. - Encoding suffixes are case sensitive; type suffixes are first tried case - sensitively, then case insensitively. + Encoding suffixes are case-sensitive. Suffix mappings and type suffixes are + first tried case-sensitively, then case-insensitively. The optional *strict* argument is a flag specifying whether the list of known MIME types is limited to only the official types `registered with IANA @@ -131,6 +131,8 @@ behavior of the module. is already known the extension will be added to the list of known extensions. Valid extensions are empty or start with a ``'.'``. + Registered lower-case extensions are matched case-insensitively. + When *strict* is ``True`` (the default), the mapping will be added to the official MIME types, otherwise to the non-standard ones. @@ -312,6 +314,8 @@ than one MIME-type database; it provides an interface similar to the one of the extension is already known, the new type will replace the old one. When the type is already known the extension will be added to the list of known extensions. + Registered lower-case extensions are matched case-insensitively. + When *strict* is ``True`` (the default), the mapping will be added to the official MIME types, otherwise to the non-standard ones. diff --git a/Lib/mimetypes.py b/Lib/mimetypes.py index 15e8c0a437bfd93..4339ef5a61397dd 100644 --- a/Lib/mimetypes.py +++ b/Lib/mimetypes.py @@ -86,6 +86,9 @@ def add_type(self, type, ext, strict=True): is already known the extension will be added to the list of known extensions. + Registered lower-case extensions are matched + case-insensitively. + If strict is true, information will be added to list of standard types, else to the list of non-standard types. @@ -172,23 +175,33 @@ def guess_file_type(self, path, *, strict=True): def _guess_file_type(self, path, strict, splitext): base, ext = splitext(path) - while (ext_lower := ext.lower()) in self.suffix_map: - base, ext = splitext(base + self.suffix_map[ext_lower]) + while True: + if ext in self.suffix_map: + suffix = self.suffix_map[ext] + elif (ext_lower := ext.lower()) in self.suffix_map: + suffix = self.suffix_map[ext_lower] + else: + break + base, ext = splitext(base + suffix) # encodings_map is case sensitive if ext in self.encodings_map: encoding = self.encodings_map[ext] base, ext = splitext(base) else: encoding = None - ext = ext.lower() + ext_lower = ext.lower() types_map = self.types_map[True] if ext in types_map: return types_map[ext], encoding + if ext_lower in types_map: + return types_map[ext_lower], encoding elif strict: return None, encoding types_map = self.types_map[False] if ext in types_map: return types_map[ext], encoding + if ext_lower in types_map: + return types_map[ext_lower], encoding else: return None, encoding @@ -386,6 +399,9 @@ def add_type(type, ext, strict=True): is already known the extension will be added to the list of known extensions. + Registered lower-case extensions are matched + case-insensitively. + If strict is true, information will be added to list of standard types, else to the list of non-standard types. diff --git a/Lib/test/test_mimetypes.py b/Lib/test/test_mimetypes.py index 1a3b49b87b121f2..19983fa3fa7628d 100644 --- a/Lib/test/test_mimetypes.py +++ b/Lib/test/test_mimetypes.py @@ -287,6 +287,50 @@ def test_case_sensitivity(self): eq(self.db.guess_file_type("foobar.tar.z"), (None, None)) eq(self.db.guess_type("scheme:foobar.tar.z"), (None, None)) + def test_suffix_map_case_sensitive_preferred(self): + self.db.suffix_map[".TEST-SUFFIX"] = ".tar.gz" + self.db.suffix_map[".test-suffix"] = ".tar.xz" + self.assertEqual( + self.db.guess_file_type("example.TEST-SUFFIX"), + ("application/x-tar", "gzip"), + ) + self.assertEqual( + self.db.guess_file_type("example.test-suffix"), + ("application/x-tar", "xz"), + ) + + def test_added_types_case_sensitive_preferred(self): + self.db.add_type("text/x-test-uppercase-r", ".R") + self.db.add_type("text/x-test-lowercase-r", ".r") + self.assertEqual( + self.db.guess_file_type("example.R"), + ("text/x-test-uppercase-r", None), + ) + self.assertEqual( + self.db.guess_file_type("example.r"), + ("text/x-test-lowercase-r", None), + ) + self.db.add_type("text/x-test-uppercase-non-strict", + ".NON-STRICT-EXT", strict=False) + self.db.add_type("text/x-test-lowercase-non-strict", + ".non-strict-ext", strict=False) + self.assertEqual( + self.db.guess_file_type("example.NON-STRICT-EXT"), + (None, None), + ) + self.assertEqual( + self.db.guess_file_type("example.non-strict-ext"), + (None, None), + ) + self.assertEqual( + self.db.guess_file_type("example.NON-STRICT-EXT", strict=False), + ("text/x-test-uppercase-non-strict", None), + ) + self.assertEqual( + self.db.guess_file_type("example.non-strict-ext", strict=False), + ("text/x-test-lowercase-non-strict", None), + ) + def test_default_data(self): eq = self.assertEqual eq(self.db.guess_file_type("foo.html"), ("text/html", None)) diff --git a/Misc/NEWS.d/next/Library/2026-04-20-01-24-22.gh-issue-92455.vXhmad.rst b/Misc/NEWS.d/next/Library/2026-04-20-01-24-22.gh-issue-92455.vXhmad.rst new file mode 100644 index 000000000000000..8d2a11cb7761377 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-04-20-01-24-22.gh-issue-92455.vXhmad.rst @@ -0,0 +1,3 @@ +Fix :mod:`mimetypes` to prefer case-sensitive matches for suffix mappings and +MIME type suffixes before falling back to case-insensitive matches. +Contributed by Xiao Yuan. From 4c88ab8983a8efa09860b46e2f594e682281ac00 Mon Sep 17 00:00:00 2001 From: Weilin Du <1372449351@qq.com> Date: Tue, 16 Jun 2026 00:11:10 +0800 Subject: [PATCH 166/185] gh-140145: Use repr of the key in `zoneinfo` "No time zone found" error (#140433) Co-authored-by: Stan Ulbrych --- Lib/zoneinfo/_common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/zoneinfo/_common.py b/Lib/zoneinfo/_common.py index 98668c15d8bf94b..caa3a5b583bab32 100644 --- a/Lib/zoneinfo/_common.py +++ b/Lib/zoneinfo/_common.py @@ -26,7 +26,7 @@ def load_tzdata(key): # UnicodeEncodeError: If package_name or resource_name are not UTF-8, # such as keys containing a surrogate character. # IsADirectoryError: If package_name without a resource_name specified. - raise ZoneInfoNotFoundError(f"No time zone found with key {key}") + raise ZoneInfoNotFoundError(f"No time zone found with key {key!r}") def load_data(fobj): From d2453e99183ac70fd98894cd1bfa28768d5d4507 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Mon, 15 Jun 2026 22:09:49 +0530 Subject: [PATCH 167/185] gh-151223: fix tsan data races in load global specializations (#151393) --- Modules/_testinternalcapi/test_cases.c.h | 6 +++--- Python/bytecodes.c | 6 +++--- Python/executor_cases.c.h | 6 +++--- Python/generated_cases.c.h | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Modules/_testinternalcapi/test_cases.c.h b/Modules/_testinternalcapi/test_cases.c.h index 503f566c9ae86a5..62d08826a2faea4 100644 --- a/Modules/_testinternalcapi/test_cases.c.h +++ b/Modules/_testinternalcapi/test_cases.c.h @@ -8830,7 +8830,7 @@ assert(keys->dk_kind == DICT_KEYS_UNICODE); assert(index < FT_ATOMIC_LOAD_SSIZE_RELAXED(keys->dk_nentries)); PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(keys) + index; - PyObject *attr_o = FT_ATOMIC_LOAD_PTR_RELAXED(ep->me_value); + PyObject *attr_o = FT_ATOMIC_LOAD_PTR_CONSUME(ep->me_value); if (attr_o == NULL) { UPDATE_MISS_STATS(LOAD_ATTR); assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); @@ -9707,7 +9707,7 @@ } assert(keys->dk_kind == DICT_KEYS_UNICODE); PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(keys); - PyObject *res_o = FT_ATOMIC_LOAD_PTR_RELAXED(entries[index].me_value); + PyObject *res_o = FT_ATOMIC_LOAD_PTR_CONSUME(entries[index].me_value); if (res_o == NULL) { UPDATE_MISS_STATS(LOAD_GLOBAL); assert(_PyOpcode_Deopt[opcode] == (LOAD_GLOBAL)); @@ -9774,7 +9774,7 @@ assert(keys->dk_kind == DICT_KEYS_UNICODE); PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(keys); assert(index < DK_SIZE(keys)); - PyObject *res_o = FT_ATOMIC_LOAD_PTR_RELAXED(entries[index].me_value); + PyObject *res_o = FT_ATOMIC_LOAD_PTR_CONSUME(entries[index].me_value); if (res_o == NULL) { UPDATE_MISS_STATS(LOAD_GLOBAL); assert(_PyOpcode_Deopt[opcode] == (LOAD_GLOBAL)); diff --git a/Python/bytecodes.c b/Python/bytecodes.c index e368092b300f864..beaf6752b87ea2d 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2349,7 +2349,7 @@ dummy_func( assert(keys->dk_kind == DICT_KEYS_UNICODE); PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(keys); assert(index < DK_SIZE(keys)); - PyObject *res_o = FT_ATOMIC_LOAD_PTR_RELAXED(entries[index].me_value); + PyObject *res_o = FT_ATOMIC_LOAD_PTR_CONSUME(entries[index].me_value); DEOPT_IF(res_o == NULL); #if Py_GIL_DISABLED int increfed = _Py_TryIncrefCompareStackRef(&entries[index].me_value, res_o, &res); @@ -2368,7 +2368,7 @@ dummy_func( DEOPT_IF(FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != version); assert(keys->dk_kind == DICT_KEYS_UNICODE); PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(keys); - PyObject *res_o = FT_ATOMIC_LOAD_PTR_RELAXED(entries[index].me_value); + PyObject *res_o = FT_ATOMIC_LOAD_PTR_CONSUME(entries[index].me_value); DEOPT_IF(res_o == NULL); #if Py_GIL_DISABLED int increfed = _Py_TryIncrefCompareStackRef(&entries[index].me_value, res_o, &res); @@ -2958,7 +2958,7 @@ dummy_func( assert(keys->dk_kind == DICT_KEYS_UNICODE); assert(index < FT_ATOMIC_LOAD_SSIZE_RELAXED(keys->dk_nentries)); PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(keys) + index; - PyObject *attr_o = FT_ATOMIC_LOAD_PTR_RELAXED(ep->me_value); + PyObject *attr_o = FT_ATOMIC_LOAD_PTR_CONSUME(ep->me_value); EXIT_IF(attr_o == NULL); #ifdef Py_GIL_DISABLED int increfed = _Py_TryIncrefCompareStackRef(&ep->me_value, attr_o, &attr); diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 7973c75e1a60ad2..d5bfe60cd234737 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -10301,7 +10301,7 @@ assert(keys->dk_kind == DICT_KEYS_UNICODE); PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(keys); assert(index < DK_SIZE(keys)); - PyObject *res_o = FT_ATOMIC_LOAD_PTR_RELAXED(entries[index].me_value); + PyObject *res_o = FT_ATOMIC_LOAD_PTR_CONSUME(entries[index].me_value); if (res_o == NULL) { UOP_STAT_INC(uopcode, miss); SET_CURRENT_CACHED_VALUES(0); @@ -10346,7 +10346,7 @@ } assert(keys->dk_kind == DICT_KEYS_UNICODE); PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(keys); - PyObject *res_o = FT_ATOMIC_LOAD_PTR_RELAXED(entries[index].me_value); + PyObject *res_o = FT_ATOMIC_LOAD_PTR_CONSUME(entries[index].me_value); if (res_o == NULL) { UOP_STAT_INC(uopcode, miss); SET_CURRENT_CACHED_VALUES(0); @@ -12152,7 +12152,7 @@ assert(keys->dk_kind == DICT_KEYS_UNICODE); assert(index < FT_ATOMIC_LOAD_SSIZE_RELAXED(keys->dk_nentries)); PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(keys) + index; - PyObject *attr_o = FT_ATOMIC_LOAD_PTR_RELAXED(ep->me_value); + PyObject *attr_o = FT_ATOMIC_LOAD_PTR_CONSUME(ep->me_value); if (attr_o == NULL) { UOP_STAT_INC(uopcode, miss); _tos_cache0 = owner; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 5adcdcb4521baf5..a6e0f90d8c1ce2a 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -8829,7 +8829,7 @@ assert(keys->dk_kind == DICT_KEYS_UNICODE); assert(index < FT_ATOMIC_LOAD_SSIZE_RELAXED(keys->dk_nentries)); PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(keys) + index; - PyObject *attr_o = FT_ATOMIC_LOAD_PTR_RELAXED(ep->me_value); + PyObject *attr_o = FT_ATOMIC_LOAD_PTR_CONSUME(ep->me_value); if (attr_o == NULL) { UPDATE_MISS_STATS(LOAD_ATTR); assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); @@ -9705,7 +9705,7 @@ } assert(keys->dk_kind == DICT_KEYS_UNICODE); PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(keys); - PyObject *res_o = FT_ATOMIC_LOAD_PTR_RELAXED(entries[index].me_value); + PyObject *res_o = FT_ATOMIC_LOAD_PTR_CONSUME(entries[index].me_value); if (res_o == NULL) { UPDATE_MISS_STATS(LOAD_GLOBAL); assert(_PyOpcode_Deopt[opcode] == (LOAD_GLOBAL)); @@ -9772,7 +9772,7 @@ assert(keys->dk_kind == DICT_KEYS_UNICODE); PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(keys); assert(index < DK_SIZE(keys)); - PyObject *res_o = FT_ATOMIC_LOAD_PTR_RELAXED(entries[index].me_value); + PyObject *res_o = FT_ATOMIC_LOAD_PTR_CONSUME(entries[index].me_value); if (res_o == NULL) { UPDATE_MISS_STATS(LOAD_GLOBAL); assert(_PyOpcode_Deopt[opcode] == (LOAD_GLOBAL)); From 82394ff42dc02527acefe8ea8e7bd3c2318b96c2 Mon Sep 17 00:00:00 2001 From: Langyan Date: Tue, 16 Jun 2026 01:55:57 +0800 Subject: [PATCH 168/185] gh-151128: Improve SyntaxError message for cross language keywords (GH-151129) --- Lib/test/test_traceback.py | 12 +++++++ Lib/traceback.py | 32 +++++++++++++++++-- ...-06-12-00-17-29.gh-issue-151128.-LYO3a.rst | 3 ++ 3 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-06-12-00-17-29.gh-issue-151128.-LYO3a.rst diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 2c6324a14a8e2fc..e38d0942e463e9c 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -1815,6 +1815,18 @@ class TestKeywordTypoSuggestions(unittest.TestCase): ("[x for x\nin range(3)\nof x]", "if"), ("[123 fur x\nin range(3)\nif x]", "for"), ("for x im n:\n pass", "in"), + ("mach x:", "match"), + ("math x:", "match"), + ("match 1:\n cse 1:", "case"), + ("typ x = int", "type"), + ("typed x = int", "type"), + ("lazi import x", "lazy"), + ("lezi import x", "lazy"), + ("switch x:\n case:", "match"), + ("delete x", "del"), + ("function f():", "def"), + ("func f():", "def"), + ("void f():", "def"), ] def test_keyword_suggestions_from_file(self): diff --git a/Lib/traceback.py b/Lib/traceback.py index 614a12f69b32e40..dcdab1f12e9a168 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -1485,11 +1485,22 @@ def _find_keyword_typos(self): # Limit the number of possible matches to try max_matches = 3 matches = [] + + hint = _get_cross_language_keyword_hint(wrong_name) + if hint: + matches.append(hint) if _suggestions is not None: - suggestion = _suggestions._generate_suggestions(keyword.kwlist, wrong_name) + suggestion = _suggestions._generate_suggestions(keyword.kwlist + keyword.softkwlist, wrong_name) if suggestion: matches.append(suggestion) - matches.extend(difflib.get_close_matches(wrong_name, keyword.kwlist, n=max_matches, cutoff=0.5)) + matches.extend( + difflib.get_close_matches( + wrong_name, + keyword.kwlist + keyword.softkwlist, + n=max_matches, + cutoff=0.5 + ) + ) matches = matches[:max_matches] for suggestion in matches: if not suggestion or suggestion == wrong_name: @@ -1787,6 +1798,17 @@ def print(self, *, file=None, chain=True, **kwargs): }) +# Cross-language keyword suggestions. +_CROSS_LANGUAGE_KEYWORD_HINTS = frozendict({ + # C/C++ equivalents + 'switch': 'match', + 'delete': 'del', + # function define equivalents + 'function': 'def', + 'func': 'def', + 'void': 'def', +}) + def _substitution_cost(ch_a, ch_b): if ch_a == ch_b: return 0 @@ -1866,6 +1888,12 @@ def _get_cross_language_hint(obj, wrong_name): return None +def _get_cross_language_keyword_hint(wrong_name): + """Check if wrong_name is a common keyword from another language + """ + return _CROSS_LANGUAGE_KEYWORD_HINTS.get(wrong_name) + + def _get_safe___dir__(obj): # Use obj.__dir__() to avoid a TypeError when calling dir(obj). # See gh-131001 and gh-139933. diff --git a/Misc/NEWS.d/next/Library/2026-06-12-00-17-29.gh-issue-151128.-LYO3a.rst b/Misc/NEWS.d/next/Library/2026-06-12-00-17-29.gh-issue-151128.-LYO3a.rst new file mode 100644 index 000000000000000..6e760686af2b01f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-12-00-17-29.gh-issue-151128.-LYO3a.rst @@ -0,0 +1,3 @@ +Cross-language keyword suggestions are now shown for :exc:`SyntaxError` messages. +For example, ``switch x:`` suggests ``match``, ``delete x`` suggests ``del``, +``function f():`` suggests ``def``. Contributed by Zang Langyan. From 0140462086628ce1cfa18882e3a1e8187a15fee1 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Mon, 15 Jun 2026 15:16:16 -0700 Subject: [PATCH 169/185] gh-151422: Don't link libffi into _ctypes_test.so (#151423) _ctypes_test doesn't use libffi directly, and linking it into the module causes emscripten tests to fail. --- Lib/test/test_ctypes/test_as_parameter.py | 3 ++- Lib/test/test_ctypes/test_structures.py | 7 +++++++ configure | 4 ++-- configure.ac | 4 +++- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_ctypes/test_as_parameter.py b/Lib/test/test_ctypes/test_as_parameter.py index 2da1acfcf2989ee..c9d728e9ae2f9cb 100644 --- a/Lib/test/test_ctypes/test_as_parameter.py +++ b/Lib/test/test_ctypes/test_as_parameter.py @@ -5,7 +5,7 @@ c_short, c_int, c_long, c_longlong, c_byte, c_wchar, c_float, c_double, ArgumentError) -from test.support import import_helper, skip_if_sanitizer +from test.support import import_helper, skip_if_sanitizer, skip_emscripten_stack_overflow _ctypes_test = import_helper.import_module("_ctypes_test") @@ -193,6 +193,7 @@ class S8I(Structure): (9*2, 8*3, 7*4, 6*5, 5*6, 4*7, 3*8, 2*9)) @skip_if_sanitizer('requires deep stack', thread=True) + @skip_emscripten_stack_overflow() def test_recursive_as_param(self): class A: pass diff --git a/Lib/test/test_ctypes/test_structures.py b/Lib/test/test_ctypes/test_structures.py index 92d4851d739d47b..65ccc12625f72be 100644 --- a/Lib/test/test_ctypes/test_structures.py +++ b/Lib/test/test_ctypes/test_structures.py @@ -299,9 +299,16 @@ class X(Structure): self.assertEqual(s.first, got.first) self.assertEqual(s.second, got.second) + @unittest.skipIf(support.is_wasm32, "wasm ABI is incompatible with test expectations") def _test_issue18060(self, Vector): # Regression tests for gh-62260 + # This test passes a struct of two doubles by value to atan2(), whose C + # signature is atan2(double, double), so it only works on platforms + # where the abi of a function that takes a struct with two doubles + # matches the abi of a function that takes two doubles. The wasm32 ABI + # does not satisfy this condition and the test breaks. + # The call to atan2() should succeed if the # class fields were correctly cloned in the # subclasses. Otherwise, it will segfault. diff --git a/configure b/configure index 0cd98a193030dd5..7608a0493b0ee62 100755 --- a/configure +++ b/configure @@ -35116,8 +35116,8 @@ fi if test "x$py_cv_module__ctypes_test" = xyes then : - as_fn_append MODULE_BLOCK "MODULE__CTYPES_TEST_CFLAGS=$LIBFFI_CFLAGS$as_nl" - as_fn_append MODULE_BLOCK "MODULE__CTYPES_TEST_LDFLAGS=$LIBFFI_LIBS $LIBM$as_nl" + + as_fn_append MODULE_BLOCK "MODULE__CTYPES_TEST_LDFLAGS=$LIBM$as_nl" fi if test "$py_cv_module__ctypes_test" = yes; then diff --git a/configure.ac b/configure.ac index d3320f0e45fe146..020861b4c823759 100644 --- a/configure.ac +++ b/configure.ac @@ -8447,9 +8447,11 @@ PY_STDLIB_MOD([_testmultiphase], [test "$TEST_MODULES" = yes], [test "$ac_cv_fun PY_STDLIB_MOD([_testsinglephase], [test "$TEST_MODULES" = yes], [test "$ac_cv_func_dlopen" = yes]) PY_STDLIB_MOD([xxsubtype], [test "$TEST_MODULES" = yes]) PY_STDLIB_MOD([_xxtestfuzz], [test "$TEST_MODULES" = yes]) +dnl Check have_libffi so _ctypes_test is only built if _ctypes is built. +dnl _ctypes_test doesn't use libffi directly. PY_STDLIB_MOD([_ctypes_test], [test "$TEST_MODULES" = yes], [test "$have_libffi" = yes -a "$ac_cv_func_dlopen" = yes], - [$LIBFFI_CFLAGS], [$LIBFFI_LIBS $LIBM]) + [], [$LIBM]) dnl Limited API template modules. dnl Emscripten does not support shared libraries yet. From aabaf46f010b5b889e52954817d2bcd3228b011f Mon Sep 17 00:00:00 2001 From: Itamar Oren Date: Mon, 15 Jun 2026 21:51:39 -0700 Subject: [PATCH 170/185] gh-151522: Guard against None transport in slow-socket SSL test (#151523) --- Lib/test/test_asyncio/test_ssl.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Lib/test/test_asyncio/test_ssl.py b/Lib/test/test_asyncio/test_ssl.py index ca15fc3bdd42dd7..784c784d261dc66 100644 --- a/Lib/test/test_asyncio/test_ssl.py +++ b/Lib/test/test_asyncio/test_ssl.py @@ -1544,6 +1544,9 @@ async def client(addr): # This triggers bug gh-115514, also tested using mocks in # test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_buffer_after_close socket_transport = writer.transport._ssl_protocol._transport + # connection_lost may have already cleared _transport. + if socket_transport is None: + return class SocketWrapper: def __init__(self, sock) -> None: From 6f4f0e31abd3f9fd954a72c0018e6b1db73b1e1a Mon Sep 17 00:00:00 2001 From: dev Date: Tue, 16 Jun 2026 17:19:54 +0530 Subject: [PATCH 171/185] gh-150771: Fix email serialization for shift_jis and euc-jp (GH-151120) Encode the payload with output_charset instead of input_charset. --- Lib/email/contentmanager.py | 3 +- Lib/test/test_email/test_contentmanager.py | 40 +++++++++++++++++++ ...-06-09-12-00-00.gh-issue-150771.K7mNx2.rst | 4 ++ 3 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2026-06-09-12-00-00.gh-issue-150771.K7mNx2.rst diff --git a/Lib/email/contentmanager.py b/Lib/email/contentmanager.py index faf2626bccce651..c0090af716575d7 100644 --- a/Lib/email/contentmanager.py +++ b/Lib/email/contentmanager.py @@ -174,7 +174,8 @@ def set_text_content(msg, string, subtype="plain", charset='utf-8', cte=None, params=None, headers=None): _prepare_set(msg, 'text', subtype, headers) - charset = email.charset.Charset(charset).input_charset + cs = email.charset.Charset(charset) + charset = cs.output_charset cte, payload = _encode_text(string, charset, cte, msg.policy) msg.set_payload(payload) msg.set_param('charset', charset, replace=True) diff --git a/Lib/test/test_email/test_contentmanager.py b/Lib/test/test_email/test_contentmanager.py index 0b1b6e89f8c9922..3115941f8703194 100644 --- a/Lib/test/test_email/test_contentmanager.py +++ b/Lib/test/test_email/test_contentmanager.py @@ -362,6 +362,46 @@ def test_set_text_charset_cp949(self): self.assertEqual(m.get_payload(decode=True), content.encode('ks_c_5601-1987')) self.assertEqual(m.get_content(), content) + def test_set_text_charset_shift_jis(self): + m = self._make_message() + content = "\u65e5\u672c\u8a9e\n" + raw_data_manager.set_content(m, content, charset='shift_jis') + self.assertEqual(m['Content-Type'], 'text/plain; charset="iso-2022-jp"') + self.assertEqual(m.get_payload(decode=True), content.encode('iso-2022-jp')) + self.assertEqual(m.get_content(), content) + self.assertEqual(str(m), textwrap.dedent("""\ + Content-Type: text/plain; charset="iso-2022-jp" + Content-Transfer-Encoding: 7bit + + \x1b$BF|K\\8l\x1b(B + """)) + self.assertEqual(bytes(m), textwrap.dedent("""\ + Content-Type: text/plain; charset="iso-2022-jp" + Content-Transfer-Encoding: 7bit + + \u65e5\u672c\u8a9e + """).encode('iso-2022-jp')) + + def test_set_text_charset_euc_jp(self): + m = self._make_message() + content = "\u65e5\u672c\u8a9e\n" + raw_data_manager.set_content(m, content, charset='euc-jp') + self.assertEqual(m['Content-Type'], 'text/plain; charset="iso-2022-jp"') + self.assertEqual(m.get_payload(decode=True), content.encode('iso-2022-jp')) + self.assertEqual(m.get_content(), content) + self.assertEqual(str(m), textwrap.dedent("""\ + Content-Type: text/plain; charset="iso-2022-jp" + Content-Transfer-Encoding: 7bit + + \x1b$BF|K\\8l\x1b(B + """)) + self.assertEqual(bytes(m), textwrap.dedent("""\ + Content-Type: text/plain; charset="iso-2022-jp" + Content-Transfer-Encoding: 7bit + + \u65e5\u672c\u8a9e + """).encode('iso-2022-jp')) + def test_set_text_plain_long_line_heuristics(self): m = self._make_message() content = ("Simple but long message that is over 78 characters" diff --git a/Misc/NEWS.d/next/Library/2026-06-09-12-00-00.gh-issue-150771.K7mNx2.rst b/Misc/NEWS.d/next/Library/2026-06-09-12-00-00.gh-issue-150771.K7mNx2.rst new file mode 100644 index 000000000000000..6535e5c48bf0360 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-09-12-00-00.gh-issue-150771.K7mNx2.rst @@ -0,0 +1,4 @@ +Fix :mod:`email` messages created with ``shift_jis`` or ``euc-jp`` charsets. +``set_content()`` now stores the payload using the output charset +(``iso-2022-jp``) so printing the message no longer raises +:exc:`UnicodeEncodeError`. From afae0aa5a6f023f6e53dcc38b2e3a0e0d70d5096 Mon Sep 17 00:00:00 2001 From: Itamar Oren Date: Tue, 16 Jun 2026 09:05:21 -0700 Subject: [PATCH 172/185] gh-151519: Check effective gid in `_test_all_chown_common` group-0 guard (#151521) The guard that skips the "chown to gid 0 should fail" assertion used only `os.getgroups()` (supplementary groups). The kernel also accepts the effective/filesystem gid for chown, so when a process runs with egid 0 and a non-zero uid (common in containers and user namespaces), chown(-1, 0) succeeds and the assertion spuriously fails. Add an `os.getegid() != 0` check alongside the existing `0 not in os.getgroups()` guard. --- Lib/test/test_os/test_posix.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_os/test_posix.py b/Lib/test/test_os/test_posix.py index 1395156539a1637..8e83fa21dae6e22 100644 --- a/Lib/test/test_os/test_posix.py +++ b/Lib/test/test_os/test_posix.py @@ -901,7 +901,9 @@ def check_stat(uid, gid): self.assertRaises(OSError, chown_func, first_param, 0, -1) check_stat(uid, gid) if hasattr(os, 'getgroups'): - if 0 not in os.getgroups(): + # Also check the effective gid, which the kernel + # accepts for chown even if not in getgroups(). + if 0 not in os.getgroups() and os.getegid() != 0: self.assertRaises(OSError, chown_func, first_param, -1, 0) check_stat(uid, gid) # test illegal types From 0171ccc6667ee38a8d03b799f888b8742a4251ef Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 16 Jun 2026 18:17:33 +0200 Subject: [PATCH 173/185] gh-151218: Replace sys.flags in PyConfig_Set() (#151402) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PyConfig_Set() and sys.set_int_max_str_digits() now replace sys.flags (create a new object), instead of modifying sys.flags in-place. Modifying sys.flags in-place can lead to data races when multiple threads are reading or writing sys.flags in parallel. Use _Py_atomic functions to get and set max_str_digits members. Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Doc/c-api/init_config.rst | 4 ++ Lib/test/test_capi/test_config.py | 2 + Lib/test/test_free_threading/test_sys.py | 28 ++++++++ Lib/test/test_sys.py | 20 ++++++ ...-06-12-15-30-25.gh-issue-151218.5M_nv8.rst | 3 + Objects/longobject.c | 6 +- Python/initconfig.c | 3 +- Python/pylifecycle.c | 3 +- Python/sysmodule.c | 65 ++++++++++++++----- 9 files changed, 111 insertions(+), 23 deletions(-) create mode 100644 Lib/test/test_free_threading/test_sys.py create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-06-12-15-30-25.gh-issue-151218.5M_nv8.rst diff --git a/Doc/c-api/init_config.rst b/Doc/c-api/init_config.rst index 209e48767ccfd6c..d6b9837987a3999 100644 --- a/Doc/c-api/init_config.rst +++ b/Doc/c-api/init_config.rst @@ -623,6 +623,10 @@ Some options are read from the :mod:`sys` attributes. For example, the option .. versionadded:: 3.14 + .. versionchanged:: next + The function now replaces :data:`sys.flags` (create a new object), + instead of modifying :data:`sys.flags` in-place. + .. _pyconfig_api: diff --git a/Lib/test/test_capi/test_config.py b/Lib/test/test_capi/test_config.py index f10ad50d3bea7e9..c750a6c2a477abd 100644 --- a/Lib/test/test_capi/test_config.py +++ b/Lib/test/test_capi/test_config.py @@ -356,9 +356,11 @@ def expect_bool_not(value): for value in new_values: expected, expect_flag = expect_func(value) + old_flags = sys.flags config_set(name, value) self.assertEqual(config_get(name), expected) self.assertEqual(getattr(sys.flags, sys_flag), expect_flag) + self.assertIsNot(sys.flags, old_flags) if name == "write_bytecode": self.assertEqual(getattr(sys, "dont_write_bytecode"), expect_flag) diff --git a/Lib/test/test_free_threading/test_sys.py b/Lib/test/test_free_threading/test_sys.py new file mode 100644 index 000000000000000..37b53bd723fd767 --- /dev/null +++ b/Lib/test/test_free_threading/test_sys.py @@ -0,0 +1,28 @@ +import sys +import unittest +from test.support import threading_helper + + +class SysModuleTest(unittest.TestCase): + def test_int_max_str_digits_thread(self): + # gh-151218: Check that it's safe to call get_int_max_str_digits() and + # set_int_max_str_digits() in parallel. Previously, this test triggered + # warnings in TSan on a free threaded build. + + old_limit = sys.get_int_max_str_digits() + self.addCleanup(sys.set_int_max_str_digits, old_limit) + + def worker(worker_id): + if not worker_id: + for i in range (20_000): + sys.get_int_max_str_digits() + else: + for i in range (20_000): + sys.set_int_max_str_digits(4300 + (i & 7)) + + workers = [lambda: worker(i) for i in range(5)] + threading_helper.run_concurrently(workers) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 02c70403185f60d..f40da0b79aa4790 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1349,6 +1349,26 @@ def test_pystats(self): def test_disable_gil_abi(self): self.assertEqual('t' in sys.abiflags, support.Py_GIL_DISABLED) + def test_int_max_str_digits(self): + old_limit = sys.get_int_max_str_digits() + self.assertIsInstance(old_limit, int) + self.assertGreaterEqual(old_limit, 0) + self.addCleanup(sys.set_int_max_str_digits, old_limit) + + sys.set_int_max_str_digits(0) + self.assertEqual(sys.get_int_max_str_digits(), 0) + + sys.set_int_max_str_digits(2_048) + self.assertEqual(sys.get_int_max_str_digits(), 2_048) + + with self.assertRaises(ValueError): + # the minimum is 640 digits + sys.set_int_max_str_digits(5) + with self.assertRaises(ValueError): + sys.set_int_max_str_digits(-2) + with self.assertRaises(TypeError): + sys.set_int_max_str_digits(2_048.0) + @test.support.cpython_only @test.support.force_not_colorized_test_class diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-12-15-30-25.gh-issue-151218.5M_nv8.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-12-15-30-25.gh-issue-151218.5M_nv8.rst new file mode 100644 index 000000000000000..46539efc373eb0d --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-12-15-30-25.gh-issue-151218.5M_nv8.rst @@ -0,0 +1,3 @@ +:c:func:`PyConfig_Set` and :func:`sys.set_int_max_str_digits` now replace +:data:`sys.flags` (create a new object), instead of modifying :data:`sys.flags` +in-place. Patch by Victor Stinner. diff --git a/Objects/longobject.c b/Objects/longobject.c index 6e6011cb19aab5f..7a38ae8ea5a36f0 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -2125,7 +2125,7 @@ long_to_decimal_string_internal(PyObject *aa, if (size_a >= 10 * _PY_LONG_MAX_STR_DIGITS_THRESHOLD / (3 * PyLong_SHIFT) + 2) { PyInterpreterState *interp = _PyInterpreterState_GET(); - int max_str_digits = interp->long_state.max_str_digits; + int max_str_digits = _Py_atomic_load_int(&interp->long_state.max_str_digits); if ((max_str_digits > 0) && (max_str_digits / (3 * PyLong_SHIFT) <= (size_a - 11) / 10)) { PyErr_Format(PyExc_ValueError, _MAX_STR_DIGITS_ERROR_FMT_TO_STR, @@ -2206,7 +2206,7 @@ long_to_decimal_string_internal(PyObject *aa, } if (strlen > _PY_LONG_MAX_STR_DIGITS_THRESHOLD) { PyInterpreterState *interp = _PyInterpreterState_GET(); - int max_str_digits = interp->long_state.max_str_digits; + int max_str_digits = _Py_atomic_load_int(&interp->long_state.max_str_digits); Py_ssize_t strlen_nosign = strlen - negative; if ((max_str_digits > 0) && (strlen_nosign > max_str_digits)) { Py_DECREF(scratch); @@ -3021,7 +3021,7 @@ long_from_string_base(const char **str, int base, PyLongObject **res) * quadratic algorithm. */ if (digits > _PY_LONG_MAX_STR_DIGITS_THRESHOLD) { PyInterpreterState *interp = _PyInterpreterState_GET(); - int max_str_digits = interp->long_state.max_str_digits; + int max_str_digits = _Py_atomic_load_int(&interp->long_state.max_str_digits); if ((max_str_digits > 0) && (digits > max_str_digits)) { PyErr_Format(PyExc_ValueError, _MAX_STR_DIGITS_ERROR_FMT_TO_INT, max_str_digits, digits); diff --git a/Python/initconfig.c b/Python/initconfig.c index eb0a5ef7aa01365..f653c00d1f0bed8 100644 --- a/Python/initconfig.c +++ b/Python/initconfig.c @@ -4641,7 +4641,8 @@ config_get(const PyConfig *config, const PyConfigSpec *spec, if (strcmp(spec->name, "int_max_str_digits") == 0) { PyInterpreterState *interp = _PyInterpreterState_GET(); - return PyLong_FromLong(interp->long_state.max_str_digits); + int maxdigits = _Py_atomic_load_int(&interp->long_state.max_str_digits); + return PyLong_FromLong(maxdigits); } } diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 2f7f10f523c2c1d..01ca459b2eb2b8f 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -429,7 +429,8 @@ interpreter_update_config(PyThreadState *tstate, int only_update_path_config) } } - tstate->interp->long_state.max_str_digits = config->int_max_str_digits; + _Py_atomic_store_int(&tstate->interp->long_state.max_str_digits, + config->int_max_str_digits); // Update the sys module for the new configuration if (_PySys_UpdateConfig(tstate) < 0) { diff --git a/Python/sysmodule.c b/Python/sysmodule.c index f7e28086d84fab9..d9f7b9c449cfb94 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -1874,7 +1874,8 @@ sys_get_int_max_str_digits_impl(PyObject *module) /*[clinic end generated code: output=0042f5e8ae0e8631 input=77fb74e987ba7ecb]*/ { PyInterpreterState *interp = _PyInterpreterState_GET(); - return PyLong_FromLong(interp->long_state.max_str_digits); + int maxdigits = _Py_atomic_load_int(&interp->long_state.max_str_digits); + return PyLong_FromLong(maxdigits); } @@ -3490,14 +3491,39 @@ sys_set_flag(PyObject *flags, Py_ssize_t pos, PyObject *value) int _PySys_SetFlagObj(Py_ssize_t pos, PyObject *value) { - PyObject *flags = PySys_GetAttrString("flags"); - if (flags == NULL) { - return -1; + PyObject *new_flags = NULL; + PyObject *flags_str = &_Py_ID(flags); // immortal ref + + PyObject *old_flags = PySys_GetAttr(flags_str); + if (old_flags == NULL) { + goto error; } - sys_set_flag(flags, pos, value); - Py_DECREF(flags); - return 0; + new_flags = PyStructSequence_New(&FlagsType); + if (new_flags == NULL) { + goto error; + } + + for (Py_ssize_t i = 0; i < (Py_ssize_t)(Py_ARRAY_LENGTH(flags_fields) - 1); i++) { + if (i != pos) { + PyObject *old_value; + old_value = PyStructSequence_GET_ITEM(old_flags, i); // borrowed ref + sys_set_flag(new_flags, i, old_value); + } + else { + sys_set_flag(new_flags, pos, value); + } + } + + int res = _PySys_SetAttr(flags_str, new_flags); + Py_DECREF(old_flags); + Py_DECREF(new_flags); + return res; + +error: + Py_XDECREF(old_flags); + Py_XDECREF(new_flags); + return -1; } @@ -3521,8 +3547,6 @@ set_flags_from_config(PyInterpreterState *interp, PyObject *flags) const PyPreConfig *preconfig = &interp->runtime->preconfig; const PyConfig *config = _PyInterpreterState_GetConfig(interp); - // _PySys_UpdateConfig() modifies sys.flags in-place: - // Py_XDECREF() is needed in this case. Py_ssize_t pos = 0; #define SetFlagObj(expr) \ do { \ @@ -4033,7 +4057,7 @@ _PySys_InitCore(PyThreadState *tstate, PyObject *sysdict) /* implementation */ SET_SYS("implementation", make_impl_info(version_info)); - // sys.flags: updated in-place later by _PySys_UpdateConfig() + // sys.flags: updated later by _PySys_UpdateConfig() ENSURE_INFO_TYPE(FlagsType, flags_desc); SET_SYS("flags", make_flags(tstate->interp)); @@ -4153,16 +4177,21 @@ _PySys_UpdateConfig(PyThreadState *tstate) #undef COPY_LIST #undef COPY_WSTR - // sys.flags - PyObject *flags = PySys_GetAttrString("flags"); - if (flags == NULL) { + // replace sys.flags + PyObject *new_flags = PyStructSequence_New(&FlagsType); + if (new_flags == NULL) { return -1; } - if (set_flags_from_config(interp, flags) < 0) { - Py_DECREF(flags); + if (set_flags_from_config(interp, new_flags) < 0) { + Py_DECREF(new_flags); + return -1; + } + + res = _PySys_SetAttr(&_Py_ID(flags), new_flags); + Py_DECREF(new_flags); + if (res < 0) { return -1; } - Py_DECREF(flags); SET_SYS("dont_write_bytecode", PyBool_FromLong(!config->write_bytecode)); @@ -4675,7 +4704,7 @@ _PySys_SetIntMaxStrDigits(int maxdigits) // Set PyInterpreterState.long_state.max_str_digits // and PyInterpreterState.config.int_max_str_digits. PyInterpreterState *interp = _PyInterpreterState_GET(); - interp->long_state.max_str_digits = maxdigits; - interp->config.int_max_str_digits = maxdigits; + _Py_atomic_store_int(&interp->long_state.max_str_digits, maxdigits); + _Py_atomic_store_int(&interp->config.int_max_str_digits, maxdigits); return 0; } From f7beda0a4eafc684865464a03b5717503545600c Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Tue, 16 Jun 2026 19:31:01 +0300 Subject: [PATCH 174/185] gh-151428: Remove unused imports from stdlib (#151478) --- .pre-commit-config.yaml | 12 ++++++++++- Lib/.ruff.toml | 21 +++++++++++++++++++ Lib/_pyrepl/completing_reader.py | 2 +- Lib/_pyrepl/reader.py | 2 +- Lib/_pyrepl/unix_console.py | 2 +- Lib/idlelib/idle_test/template.py | 1 - Lib/idlelib/idle_test/test_help.py | 1 - Lib/profiling/sampling/heatmap_collector.py | 2 -- .../sampling/live_collector/collector.py | 3 --- .../sampling/live_collector/display.py | 1 - .../sampling/live_collector/widgets.py | 13 +----------- Lib/profiling/sampling/sample.py | 4 ---- Tools/.ruff.toml | 14 +++++++++++++ 13 files changed, 50 insertions(+), 28 deletions(-) create mode 100644 Lib/.ruff.toml create mode 100644 Tools/.ruff.toml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6878a7d92e3beeb..6c7f1b93e66e879 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: e05c5c0818279e5ac248ac9e954431ba58865e61 # frozen: v0.15.7 + rev: 3b3f7c3f57fe9925356faf5fe6230835138be230 # frozen: v0.15.17 hooks: - id: ruff-check name: Run Ruff (lint) on Platforms/Apple/ @@ -10,6 +10,11 @@ repos: name: Run Ruff (lint) on Doc/ args: [--exit-non-zero-on-fix] files: ^Doc/ + - id: ruff-check + name: Run Ruff (lint) on Lib/ + args: [--exit-non-zero-on-fix] + files: ^Lib/ + exclude: ^Lib/test/ - id: ruff-check name: Run Ruff (lint) on Lib/test/ args: [--exit-non-zero-on-fix] @@ -18,6 +23,11 @@ repos: name: Run Ruff (lint) on Platforms/WASI/ args: [--exit-non-zero-on-fix, --config=Platforms/WASI/.ruff.toml] files: ^Platforms/WASI/ + - id: ruff-check + name: Run Ruff (lint) on Tools/ + args: [--exit-non-zero-on-fix] + files: ^Tools/ + exclude: ^Tools/(build|clinic|i18n|peg_generator|wasm)/ - id: ruff-check name: Run Ruff (lint) on Tools/build/ args: [--exit-non-zero-on-fix, --config=Tools/build/.ruff.toml] diff --git a/Lib/.ruff.toml b/Lib/.ruff.toml new file mode 100644 index 000000000000000..c9ae9b95641b318 --- /dev/null +++ b/Lib/.ruff.toml @@ -0,0 +1,21 @@ +extend = "../.ruff.toml" # Inherit the project-wide settings + +# Unlike Tools/, stdlib can use newer syntax than PYTHON_FOR_REGEN +target-version = "py315" + +[lint] +select = [ + "F401", # Unused import +] + +[lint.per-file-ignores] +"ctypes/__init__.py" = ["F401"] # Re-exports from _ctypes +"ensurepip/__init__.py" = ["F401"] # `import zlib` availability check +"idlelib/idle_test/htest.py" = ["F401"] # Import for Windows DPI side effect +"idlelib/idle_test/test_iomenu.py" = ["F401"] # Imports checked for existence +"importlib/_abc.py" = ["F401"] # Bootstrap-sensitive _bootstrap import +"importlib/machinery.py" = ["F401"] # NamespacePath re-export +"importlib/metadata/__init__.py" = ["F401"] # Synced from importlib_metadata +"profiling/sampling/sample.py" = ["F401"] # Re-exports PROFILING_MODE_* constants +"ssl.py" = ["F401"] # Re-exports from _ssl +"warnings.py" = ["F401"] # Re-exports from _py_warnings diff --git a/Lib/_pyrepl/completing_reader.py b/Lib/_pyrepl/completing_reader.py index f783e8db36b0287..7eb3ebac84124b5 100644 --- a/Lib/_pyrepl/completing_reader.py +++ b/Lib/_pyrepl/completing_reader.py @@ -32,7 +32,7 @@ # types Command = commands.Command if TYPE_CHECKING: - from .types import CommandName, CompletionAction, Keymap, KeySpec + from .types import CompletionAction, Keymap def prefix(wordlist: list[str], j: int = 0) -> str: diff --git a/Lib/_pyrepl/reader.py b/Lib/_pyrepl/reader.py index 7e4dd801c84d5c2..832e67b534f2969 100644 --- a/Lib/_pyrepl/reader.py +++ b/Lib/_pyrepl/reader.py @@ -38,7 +38,7 @@ ) from .layout import LayoutMap, LayoutResult, LayoutRow, WrappedRow, layout_content_lines from .render import RenderCell, RenderLine, RenderedScreen, ScreenOverlay -from .utils import ANSI_ESCAPE_SEQUENCE, ColorSpan, THEME, StyleRef, wlen, gen_colors +from .utils import ANSI_ESCAPE_SEQUENCE, ColorSpan, THEME, StyleRef, gen_colors from .trace import trace diff --git a/Lib/_pyrepl/unix_console.py b/Lib/_pyrepl/unix_console.py index 9c4644db53e3436..9a4f8e0c623abfa 100644 --- a/Lib/_pyrepl/unix_console.py +++ b/Lib/_pyrepl/unix_console.py @@ -34,7 +34,7 @@ from collections.abc import Callable from dataclasses import dataclass from fcntl import ioctl -from typing import TYPE_CHECKING, cast, overload +from typing import TYPE_CHECKING, overload from . import terminfo from .console import Console, Event diff --git a/Lib/idlelib/idle_test/template.py b/Lib/idlelib/idle_test/template.py index 725a55b9c47230c..6e98556458ec588 100644 --- a/Lib/idlelib/idle_test/template.py +++ b/Lib/idlelib/idle_test/template.py @@ -1,6 +1,5 @@ "Test , coverage %." -from idlelib import zzdummy import unittest from test.support import requires from tkinter import Tk diff --git a/Lib/idlelib/idle_test/test_help.py b/Lib/idlelib/idle_test/test_help.py index ebb02b5c0d8356a..ebbaceb2eb7bbfd 100644 --- a/Lib/idlelib/idle_test/test_help.py +++ b/Lib/idlelib/idle_test/test_help.py @@ -4,7 +4,6 @@ import unittest from test.support import requires requires('gui') -from os.path import abspath, dirname, join from tkinter import Tk diff --git a/Lib/profiling/sampling/heatmap_collector.py b/Lib/profiling/sampling/heatmap_collector.py index 6e650ec08f410bc..78f1e39f6a002d0 100644 --- a/Lib/profiling/sampling/heatmap_collector.py +++ b/Lib/profiling/sampling/heatmap_collector.py @@ -5,11 +5,9 @@ import html import importlib.resources import json -import locale import math import os import platform -import site import sys from dataclasses import dataclass, field from pathlib import Path diff --git a/Lib/profiling/sampling/live_collector/collector.py b/Lib/profiling/sampling/live_collector/collector.py index a53cfc6b719a105..2805134f239eb38 100644 --- a/Lib/profiling/sampling/live_collector/collector.py +++ b/Lib/profiling/sampling/live_collector/collector.py @@ -18,9 +18,6 @@ THREAD_STATUS_UNKNOWN, THREAD_STATUS_GIL_REQUESTED, THREAD_STATUS_HAS_EXCEPTION, - PROFILING_MODE_CPU, - PROFILING_MODE_GIL, - PROFILING_MODE_WALL, ) from .constants import ( MICROSECONDS_PER_SECOND, diff --git a/Lib/profiling/sampling/live_collector/display.py b/Lib/profiling/sampling/live_collector/display.py index f5324421b102111..ec7e39c02963056 100644 --- a/Lib/profiling/sampling/live_collector/display.py +++ b/Lib/profiling/sampling/live_collector/display.py @@ -1,6 +1,5 @@ """Display interface abstractions for the live profiling collector.""" -import contextlib import curses from abc import ABC, abstractmethod diff --git a/Lib/profiling/sampling/live_collector/widgets.py b/Lib/profiling/sampling/live_collector/widgets.py index 86d2649f875e622..7535d0c753b0465 100644 --- a/Lib/profiling/sampling/live_collector/widgets.py +++ b/Lib/profiling/sampling/live_collector/widgets.py @@ -1,7 +1,6 @@ """Widget classes for the live profiling collector UI.""" import curses -import time from abc import ABC, abstractmethod from .constants import ( @@ -13,24 +12,14 @@ WIDTH_THRESHOLD_CUMUL_PCT, WIDTH_THRESHOLD_CUMTIME, MICROSECONDS_PER_SECOND, - DISPLAY_UPDATE_INTERVAL_SEC, MIN_BAR_WIDTH, MAX_SAMPLE_RATE_BAR_WIDTH, MAX_EFFICIENCY_BAR_WIDTH, MIN_SAMPLE_RATE_FOR_SCALING, FOOTER_LINES, - FINISHED_BANNER_EXTRA_LINES, OPCODE_PANEL_HEIGHT, ) -from ..constants import ( - THREAD_STATUS_HAS_GIL, - THREAD_STATUS_ON_CPU, - THREAD_STATUS_UNKNOWN, - THREAD_STATUS_GIL_REQUESTED, - PROFILING_MODE_CPU, - PROFILING_MODE_GIL, - PROFILING_MODE_WALL, -) +from ..constants import PROFILING_MODE_GIL from ..opcode_utils import get_opcode_info, format_opcode diff --git a/Lib/profiling/sampling/sample.py b/Lib/profiling/sampling/sample.py index 50ccc57566d70d3..6cb6dd483520887 100644 --- a/Lib/profiling/sampling/sample.py +++ b/Lib/profiling/sampling/sample.py @@ -8,10 +8,6 @@ from collections import deque lazy from _colorize import ANSIColors -from .pstats_collector import PstatsCollector -from .stack_collector import CollapsedStackCollector, FlamegraphCollector -from .heatmap_collector import HeatmapCollector -from .gecko_collector import GeckoCollector from .binary_collector import BinaryCollector diff --git a/Tools/.ruff.toml b/Tools/.ruff.toml new file mode 100644 index 000000000000000..fd9cde5e6cad650 --- /dev/null +++ b/Tools/.ruff.toml @@ -0,0 +1,14 @@ +extend = "../.ruff.toml" # Inherit the project-wide settings + +[per-file-target-version] +"jit/*.py" = "py312" # Reused-quote f-strings (PEP 701) + +[lint] +select = [ + "F401", # Unused import +] + +[lint.per-file-ignores] +"c-analyzer/c_parser/__init__.py" = ["F401"] # Re-exports from submodules +"c-analyzer/c_parser/preprocessor/__init__.py" = ["F401"] # Re-exports from submodules +"c-analyzer/c_parser/preprocessor/common.py" = ["F401"] # Re-exported by preprocessor/__init__.py From 1e09adbe76be90523c5208c40c38bd80f9839db5 Mon Sep 17 00:00:00 2001 From: Xiao Yuan Date: Tue, 16 Jun 2026 19:41:52 +0300 Subject: [PATCH 175/185] gh-118158: Fix missing newline in py_compile CLI error output (#149008) Restore trailing newline in error messages from `python -m py_compile`, lost when `main()` was refactored to use argparse. --- Lib/py_compile.py | 4 ++-- Lib/test/test_py_compile.py | 6 ++++++ .../Library/2026-04-26-11-25-38.gh-issue-118158.xIy05H.rst | 1 + 3 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-04-26-11-25-38.gh-issue-118158.xIy05H.rst diff --git a/Lib/py_compile.py b/Lib/py_compile.py index 7ca479141e01e4e..b22446e2f098dee 100644 --- a/Lib/py_compile.py +++ b/Lib/py_compile.py @@ -202,12 +202,12 @@ def main(): if args.quiet: parser.exit(1) else: - parser.exit(1, error.msg) + parser.exit(1, error.msg + '\n') except OSError as error: if args.quiet: parser.exit(1) else: - parser.exit(1, str(error)) + parser.exit(1, str(error) + '\n') if __name__ == "__main__": diff --git a/Lib/test/test_py_compile.py b/Lib/test/test_py_compile.py index da2d630d7ace7bb..09113e983fcd94d 100644 --- a/Lib/test/test_py_compile.py +++ b/Lib/test/test_py_compile.py @@ -327,6 +327,12 @@ def test_bad_syntax_with_quiet(self): self.assertEqual(stdout, b'') self.assertEqual(stderr, b'') + def test_bad_syntax_stderr_ends_with_newline(self): + with open(self.source_path, 'w') as file: + file.write(' print("hello world")\n') + rc, stdout, stderr = self.pycompilecmd_failure(self.source_path) + self.assertTrue(stderr.endswith(b'\n')) + def test_file_not_exists(self): should_not_exists = os.path.join(os.path.dirname(__file__), 'should_not_exists.py') rc, stdout, stderr = self.pycompilecmd_failure(self.source_path, should_not_exists) diff --git a/Misc/NEWS.d/next/Library/2026-04-26-11-25-38.gh-issue-118158.xIy05H.rst b/Misc/NEWS.d/next/Library/2026-04-26-11-25-38.gh-issue-118158.xIy05H.rst new file mode 100644 index 000000000000000..bd22871c1c0c172 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-04-26-11-25-38.gh-issue-118158.xIy05H.rst @@ -0,0 +1 @@ +Ensure py_compile CLI error messages end with a newline. Contributed by Xiao Yuan. From 70e202997715026e7c83e8774b739e43b28e330f Mon Sep 17 00:00:00 2001 From: Daniele Parmeggiani <8658291+dpdani@users.noreply.github.com> Date: Tue, 16 Jun 2026 17:56:37 +0100 Subject: [PATCH 176/185] gh-150902: Optimize PyCriticalSection2 to skip locking the same locks held by the current CS2 This mimics an optimization already present for the single-mutex critical section. --- Include/internal/pycore_critical_section.h | 7 +++---- ...026-06-08-13-14-42.gh-issue-150902.-CWZ66.rst | 1 + Python/critical_section.c | 16 ++++++++++++++++ 3 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-06-08-13-14-42.gh-issue-150902.-CWZ66.rst diff --git a/Include/internal/pycore_critical_section.h b/Include/internal/pycore_critical_section.h index 2a2846b1296b901..51d99d74ca159f6 100644 --- a/Include/internal/pycore_critical_section.h +++ b/Include/internal/pycore_critical_section.h @@ -196,10 +196,9 @@ _PyCriticalSection2_Begin(PyThreadState *tstate, PyCriticalSection2 *c, PyObject static inline void _PyCriticalSection2_End(PyThreadState *tstate, PyCriticalSection2 *c) { - // if mutex1 is NULL, we used the fast path in - // _PyCriticalSection_BeginSlow for mutexes that are already held, - // which should only happen when mutex1 and mutex2 were the same mutex, - // and mutex2 should also be NULL. + // if mutex1 is NULL, we used the fast path in either + // _PyCriticalSection_BeginSlow or _PyCriticalSection2_BeginSlow for mutexes + // that are already held, and mutex2 should also be NULL. if (c->_cs_base._cs_mutex == NULL) { assert(c->_cs_mutex2 == NULL); return; diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-08-13-14-42.gh-issue-150902.-CWZ66.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-08-13-14-42.gh-issue-150902.-CWZ66.rst new file mode 100644 index 000000000000000..e3b7cd387b439fe --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-08-13-14-42.gh-issue-150902.-CWZ66.rst @@ -0,0 +1 @@ +Apply an existing optimization of PyCriticalSection (single mutex) to PyCriticalSection2: avoid acquiring the same locks that the current CS has already acquired. diff --git a/Python/critical_section.c b/Python/critical_section.c index 98e23eda7cdd77e..dbee6f236a73bea 100644 --- a/Python/critical_section.c +++ b/Python/critical_section.c @@ -72,6 +72,22 @@ _PyCriticalSection2_BeginSlow(PyThreadState *tstate, PyCriticalSection2 *c, PyMu c->_cs_base._cs_prev = 0; return; } + // Same optimization as in _PyCriticalSection_BeginSlow: skip locking when + // recursively acquiring the same locks. + if (tstate->critical_section && + tstate->critical_section & _Py_CRITICAL_SECTION_TWO_MUTEXES) { + PyCriticalSection2 *prev2 = (PyCriticalSection2 *) + untag_critical_section(tstate->critical_section); + assert((uintptr_t)m1 < (uintptr_t)m2); + assert((uintptr_t)prev2->_cs_base._cs_mutex < + (uintptr_t)prev2->_cs_mutex2); + if (prev2->_cs_base._cs_mutex == m1 && prev2->_cs_mutex2 == m2) { + c->_cs_base._cs_mutex = NULL; + c->_cs_mutex2 = NULL; + c->_cs_base._cs_prev = 0; + return; + } + } c->_cs_base._cs_mutex = NULL; c->_cs_mutex2 = NULL; c->_cs_base._cs_prev = tstate->critical_section; From bfb5e11b9171a50d79355395689766e679e99716 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Wed, 17 Jun 2026 00:16:06 +0100 Subject: [PATCH 177/185] gh-151544: Fixes CVE-2026-12003 by removing the fallback to %VPATH%/Modules/Setup.local for discovering sources in getpath.py (GH-151545) --- Makefile.pre.in | 2 ++ ...2026-06-16-14-58-02.gh-issue-151544._bexVy.rst | 4 ++++ Modules/getpath.py | 15 ++++----------- 3 files changed, 10 insertions(+), 11 deletions(-) create mode 100644 Misc/NEWS.d/next/Security/2026-06-16-14-58-02.gh-issue-151544._bexVy.rst diff --git a/Makefile.pre.in b/Makefile.pre.in index 2b34b009fd745a0..cf700e9f2340901 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1616,6 +1616,8 @@ Programs/_bootstrap_python.o: Programs/_bootstrap_python.c $(BOOTSTRAP_HEADERS) _bootstrap_python: $(LIBRARY_OBJS_OMIT_FROZEN) Programs/_bootstrap_python.o Modules/getpath.o Modules/Setup.local $(LINKCC) $(PY_LDFLAGS_NOLTO) -o $@ $(LIBRARY_OBJS_OMIT_FROZEN) \ Programs/_bootstrap_python.o Modules/getpath.o $(LIBS) $(MODLIBS) $(SYSLIBS) + # Dummy pybuilddir.txt is needed for _bootstrap_python to be runnable + @echo "none" > ./pybuilddir.txt ############################################################################ diff --git a/Misc/NEWS.d/next/Security/2026-06-16-14-58-02.gh-issue-151544._bexVy.rst b/Misc/NEWS.d/next/Security/2026-06-16-14-58-02.gh-issue-151544._bexVy.rst new file mode 100644 index 000000000000000..418e3b4b9677943 --- /dev/null +++ b/Misc/NEWS.d/next/Security/2026-06-16-14-58-02.gh-issue-151544._bexVy.rst @@ -0,0 +1,4 @@ +:file:`Modules/Setup.local` is no longer used as a landmark to discover +whether Python is running in a source tree, as it could potentially affect +actual installs. The :file:`pybuilddir.txt` file is now the sole indicator +of running in a source tree. diff --git a/Modules/getpath.py b/Modules/getpath.py index 4dceb5cdc8dfcf0..6199567bd777aa0 100644 --- a/Modules/getpath.py +++ b/Modules/getpath.py @@ -129,8 +129,7 @@ # checked by looking for the BUILDDIR_TXT file, which contains the # relative path to the platlib dir. The executable_dir value is # derived from joining the VPATH preprocessor variable to the -# directory containing pybuilddir.txt. If it is not found, the -# BUILD_LANDMARK file is found, which is part of the source tree. +# directory containing pybuilddir.txt. # prefix is then found by searching up for a file that should only # exist in the source tree, and the stdlib dir is set to prefix/Lib. @@ -177,7 +176,6 @@ if os_name == 'posix' or os_name == 'darwin': BUILDDIR_TXT = 'pybuilddir.txt' - BUILD_LANDMARK = 'Modules/Setup.local' DEFAULT_PROGRAM_NAME = f'python{VERSION_MAJOR}' STDLIB_SUBDIR = f'{platlibdir}/python{VERSION_MAJOR}.{VERSION_MINOR}{ABI_THREAD}' STDLIB_LANDMARKS = [f'{STDLIB_SUBDIR}/os.py', f'{STDLIB_SUBDIR}/os.pyc'] @@ -190,7 +188,6 @@ elif os_name == 'nt': BUILDDIR_TXT = 'pybuilddir.txt' - BUILD_LANDMARK = f'{VPATH}\\Modules\\Setup.local' DEFAULT_PROGRAM_NAME = f'python' STDLIB_SUBDIR = 'Lib' STDLIB_LANDMARKS = [f'{STDLIB_SUBDIR}\\os.py', f'{STDLIB_SUBDIR}\\os.pyc'] @@ -513,13 +510,9 @@ def search_up(prefix, *landmarks, test=isfile): platstdlib_dir = real_executable_dir build_prefix = joinpath(real_executable_dir, VPATH) except (FileNotFoundError, PermissionError): - if isfile(joinpath(real_executable_dir, BUILD_LANDMARK)): - build_prefix = joinpath(real_executable_dir, VPATH) - if os_name == 'nt': - # QUIRK: Windows builds need platstdlib_dir to be the executable - # dir. Normally the builddir marker handles this, but in this - # case we need to correct manually. - platstdlib_dir = real_executable_dir + # We used to check for an alternate landmark here, but now we require + # BUILDDIR_TXT to exist. (gh-151544; CVE-2026-12003) + pass if build_prefix: if os_name == 'nt': From 93d723ed5a5dd871988c26b87d0d2e6f9e0fe67c Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 17 Jun 2026 10:23:57 +0200 Subject: [PATCH 178/185] gh-151546: Fix stack limits on musl (#151548) If the thread stack size is set by linker flags, pass the stack size to Python/ceval.c via the new _Py_LINKER_THREAD_STACK_SIZE variable to set Py_C_STACK_SIZE macro. --- .../2026-06-16-17-23-37.gh-issue-151546.LhiaZz.rst | 3 +++ Python/ceval.c | 4 +++- configure | 4 ++++ configure.ac | 3 +++ pyconfig.h.in | 3 +++ 5 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-06-16-17-23-37.gh-issue-151546.LhiaZz.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-16-17-23-37.gh-issue-151546.LhiaZz.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-16-17-23-37.gh-issue-151546.LhiaZz.rst new file mode 100644 index 000000000000000..af1c23bd50355f2 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-16-17-23-37.gh-issue-151546.LhiaZz.rst @@ -0,0 +1,3 @@ +Fix the stack limit check if Python is linked to musl (ex: Alpine Linux). +Use the stack size set by the linker to compute the stack limits. Patch by +Victor Stinner. diff --git a/Python/ceval.c b/Python/ceval.c index a9b31affca9890a..d6dd7f9a82c4314 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -49,7 +49,9 @@ _Py_ReachedRecursionLimitWithMargin(PyThreadState *tstate, int margin_count) #endif } -#if defined(__s390x__) +#if defined(_Py_LINKER_THREAD_STACK_SIZE) +# define Py_C_STACK_SIZE _Py_LINKER_THREAD_STACK_SIZE +#elif defined(__s390x__) # define Py_C_STACK_SIZE 320000 #elif defined(_WIN32) // Don't define Py_C_STACK_SIZE, ask the O/S diff --git a/configure b/configure index 7608a0493b0ee62..7408fac738b8a39 100755 --- a/configure +++ b/configure @@ -9927,6 +9927,10 @@ printf "%s\n" "$ac_cv_thread_stack_size" >&6; } if test "$ac_cv_thread_stack_size" != "default" -a "$ac_cv_thread_stack_size" != "unknown"; then LDFLAGS="$LDFLAGS -Wl,-z,stack-size=$ac_cv_thread_stack_size" + # Stack size used by Python/ceval.c to set Py_C_STACK_SIZE + +printf "%s\n" "#define _Py_LINKER_THREAD_STACK_SIZE $ac_cv_thread_stack_size" >>confdefs.h + fi fi diff --git a/configure.ac b/configure.ac index 020861b4c823759..dbc781b545dd362 100644 --- a/configure.ac +++ b/configure.ac @@ -2507,6 +2507,9 @@ EOF if test "$ac_cv_thread_stack_size" != "default" -a "$ac_cv_thread_stack_size" != "unknown"; then LDFLAGS="$LDFLAGS -Wl,-z,stack-size=$ac_cv_thread_stack_size" + # Stack size used by Python/ceval.c to set Py_C_STACK_SIZE + AC_DEFINE_UNQUOTED([_Py_LINKER_THREAD_STACK_SIZE], [$ac_cv_thread_stack_size], + [Thread stack size set by the linker (in bytes).]) fi fi diff --git a/pyconfig.h.in b/pyconfig.h.in index dc2404fe85fa232..999a55a5efd0fb0 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -2088,6 +2088,9 @@ /* Define if you have the 'PR_SET_VMA_ANON_NAME' constant. */ #undef _Py_HAVE_PR_SET_VMA_ANON_NAME +/* Thread stack size set by the linker (in bytes). */ +#undef _Py_LINKER_THREAD_STACK_SIZE + /* Define to 1 if the machine stack grows down (default); 0 if it grows up. */ #undef _Py_STACK_GROWS_DOWN From f33d8f50ef18d99bf3cde1e0d02896c4374b6b28 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 17 Jun 2026 10:58:22 +0200 Subject: [PATCH 179/185] gh-151496: Use process groups in test_dtrace (#151512) Create a new process group to run bpftrace commands, so it's possible to kill also child processes on timeout. --- Lib/test/test_dtrace.py | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_dtrace.py b/Lib/test/test_dtrace.py index 6286b6d21b572e3..592f59d77f92217 100644 --- a/Lib/test/test_dtrace.py +++ b/Lib/test/test_dtrace.py @@ -1,6 +1,7 @@ import dis import os.path import re +import signal import subprocess import sys import sysconfig @@ -50,6 +51,24 @@ def normalize_trace_output(output): ) +USE_PROCESS_GROUP = (hasattr(os, "setsid") and hasattr(os, "killpg")) + +def create_process_group(*args, **kwargs): + if USE_PROCESS_GROUP: + kwargs['start_new_session'] = True + return subprocess.Popen(*args, **kwargs) + +def kill_process_group(proc): + if USE_PROCESS_GROUP: + try: + os.killpg(proc.pid, signal.SIGKILL) + except ProcessLookupError: + pass + else: + proc.kill() + proc.communicate() # Clean up + + class TraceBackend: EXTENSION = None COMMAND = None @@ -205,7 +224,7 @@ def run_case(self, name, optimize_python=None): program = self.PROGRAMS[name].format(python=sys.executable) try: - proc = subprocess.Popen( + proc = create_process_group( ["bpftrace", "-e", program, "-c", " ".join(subcommand)], stdout=subprocess.PIPE, stderr=subprocess.PIPE, @@ -213,7 +232,7 @@ def run_case(self, name, optimize_python=None): ) stdout, stderr = proc.communicate(timeout=60) except subprocess.TimeoutExpired: - proc.kill() + kill_process_group(proc) raise AssertionError("bpftrace timed out") except (FileNotFoundError, PermissionError) as e: raise unittest.SkipTest(f"bpftrace not available: {e}") @@ -243,7 +262,7 @@ def assert_usable(self): # Check if bpftrace is available and can attach to USDT probes program = f'usdt:{sys.executable}:python:function__entry {{ printf("probe: success\\n"); exit(); }}' try: - proc = subprocess.Popen( + proc = create_process_group( ["bpftrace", "-e", program, "-c", f"{sys.executable} -c pass"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, @@ -251,8 +270,7 @@ def assert_usable(self): ) stdout, stderr = proc.communicate(timeout=10) except subprocess.TimeoutExpired: - proc.kill() - proc.communicate() # Clean up + kill_process_group(proc) raise unittest.SkipTest("bpftrace timed out during usability check") except OSError as e: raise unittest.SkipTest(f"bpftrace not available: {e}") From 50d31ad50650b86685b2554114df97b56f357251 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 17 Jun 2026 13:02:22 +0200 Subject: [PATCH 180/185] gh-151593: Use timeout on GitHub Action TSan jobs (#151594) Use a timeout of 15 minutes for --tsan command and a timeout of 10 minutes for --tsan-parallel command. Display also the slowest tests to help adjusting these timeouts later if needed. --- .github/workflows/reusable-san.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/reusable-san.yml b/.github/workflows/reusable-san.yml index 2ceb1000e8a5a9c..ef36447964cf418 100644 --- a/.github/workflows/reusable-san.yml +++ b/.github/workflows/reusable-san.yml @@ -86,12 +86,12 @@ jobs: run: >- ./python -m test ${{ inputs.sanitizer == 'TSan' && '--tsan' || '' }} - -j4 -W + -j4 -W --timeout=900 --slowest - name: Parallel tests if: >- inputs.sanitizer == 'TSan' && fromJSON(inputs.free-threading) - run: ./python -m test --tsan-parallel --parallel-threads=4 -j4 -W + run: ./python -m test --tsan-parallel --parallel-threads=4 -j4 -W --timeout=600 --slowest - name: Display logs if: always() run: find "${GITHUB_WORKSPACE}" -name 'san_log.*' | xargs head -n 1000 From 79d6078e37e2ba853c8ddf10c71ebf62ee47f330 Mon Sep 17 00:00:00 2001 From: blhsing Date: Wed, 17 Jun 2026 04:48:09 -0700 Subject: [PATCH 181/185] gh-120665: make unittest loaders avoid loading test cases that are abstract base classes (#120666) --- Lib/test/test_unittest/test_loader.py | 51 +++++++++++++++++++ Lib/unittest/loader.py | 11 +++- ...-06-18-04-08-37.gh-issue-120665.x7T1hV.rst | 1 + 3 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-06-18-04-08-37.gh-issue-120665.x7T1hV.rst diff --git a/Lib/test/test_unittest/test_loader.py b/Lib/test/test_unittest/test_loader.py index cdff6d1a20c8dfd..f4e50a3dccb2bc2 100644 --- a/Lib/test/test_unittest/test_loader.py +++ b/Lib/test/test_unittest/test_loader.py @@ -1,3 +1,4 @@ +import abc import functools import sys import types @@ -98,6 +99,22 @@ def test_loadTestsFromTestCase__from_FunctionTestCase(self): self.assertIsInstance(suite, loader.suiteClass) self.assertEqual(list(suite), []) + # "Do not load any tests from a TestCase-derived class that is an abstract + # base class." + def test_loadTestsFromTestCase__from_abc_TestCase(self): + class FooBase(unittest.TestCase, metaclass=abc.ABCMeta): + @abc.abstractmethod + def test(self): ... + class Foo(FooBase): + def test(self): pass + + empty_suite = unittest.TestSuite() + + loader = unittest.TestLoader() + suite = loader.loadTestsFromTestCase(Foo) + self.assertEqual(loader.loadTestsFromTestCase(FooBase), empty_suite) + self.assertEqual(list(suite), [Foo('test')]) + ################################################################ ### /Tests for TestLoader.loadTestsFromTestCase @@ -252,6 +269,24 @@ def load_tests(loader, tests, pattern): self.assertRaisesRegex(TypeError, "some failure", test.m) + # Check that loadTestsFromModule skips abstract base classes derived from + # TestCase, which can't be instantiated. + def test_loadTestsFromModule__skip_abc_TestCase(self): + m = types.ModuleType('m') + class MyTestCaseBase(unittest.TestCase, metaclass=abc.ABCMeta): + @abc.abstractmethod + def test(self): + ... + class MyTestCase(MyTestCaseBase): + def test(self): + pass + m.testcase_1 = MyTestCaseBase + m.testcase_2 = MyTestCase + loader = unittest.TestLoader() + suite = loader.loadTestsFromModule(m) + expected = [loader.suiteClass([MyTestCase('test')])] + self.assertEqual(list(suite), expected) + ################################################################ ### /Tests for TestLoader.loadTestsFromModule() @@ -1052,6 +1087,22 @@ def test_loadTestsFromNames__module_not_loaded(self): if module_name in sys.modules: del sys.modules[module_name] + # "The specifier should not refer to a test method in a TestCase-derived + # subclass that is an abstract base class" + def test_loadTestsFromNames__testmethod_in_abc_TestCase(self): + m = types.ModuleType('m') + class Foo(unittest.TestCase, metaclass=abc.ABCMeta): + @abc.abstractmethod + def test_1(self): ... + def test_2(self): pass + m.Foo = Foo + + loader = unittest.TestLoader() + for name in 'Foo.test_1', 'Foo.test_2': + with self.subTest(name=name), self.assertRaisesRegex(TypeError, + "Cannot instantiate abstract test case Foo"): + loader.loadTestsFromNames([name], m) + ################################################################ ### /Tests for TestLoader.loadTestsFromNames() diff --git a/Lib/unittest/loader.py b/Lib/unittest/loader.py index a52950dad224ee7..697520246f0e3c6 100644 --- a/Lib/unittest/loader.py +++ b/Lib/unittest/loader.py @@ -1,5 +1,6 @@ """Loading unittests.""" +import inspect import os import re import sys @@ -84,8 +85,10 @@ def loadTestsFromTestCase(self, testCaseClass): raise TypeError("Test cases should not be derived from " "TestSuite. Maybe you meant to derive from " "TestCase?") - if testCaseClass in (case.TestCase, case.FunctionTestCase): - # We don't load any tests from base types that should not be loaded. + if (testCaseClass in (case.TestCase, case.FunctionTestCase) or + inspect.isabstract(testCaseClass)): + # We don't load any tests from base types that should not be loaded, + # and abstract base classes that can't be instantiated testCaseNames = [] else: testCaseNames = self.getTestCaseNames(testCaseClass) @@ -103,6 +106,7 @@ def loadTestsFromModule(self, module, *, pattern=None): isinstance(obj, type) and issubclass(obj, case.TestCase) and obj not in (case.TestCase, case.FunctionTestCase) + and not inspect.isabstract(obj) ): tests.append(self.loadTestsFromTestCase(obj)) @@ -181,6 +185,9 @@ def loadTestsFromName(self, name, module=None): elif (isinstance(obj, types.FunctionType) and isinstance(parent, type) and issubclass(parent, case.TestCase)): + if inspect.isabstract(parent): + raise TypeError( + "Cannot instantiate abstract test case %s" % parent.__name__) name = parts[-1] inst = parent(name) # static methods follow a different path diff --git a/Misc/NEWS.d/next/Library/2024-06-18-04-08-37.gh-issue-120665.x7T1hV.rst b/Misc/NEWS.d/next/Library/2024-06-18-04-08-37.gh-issue-120665.x7T1hV.rst new file mode 100644 index 000000000000000..27e93988ed11efb --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-06-18-04-08-37.gh-issue-120665.x7T1hV.rst @@ -0,0 +1 @@ +Fixed an issue where ``unittest`` loaders would load and instantiate :class:`unittest.TestCase`-derived subclasses that are also abstract base classes, which can't be instantiated. From 5949b40d1403ffa069181cbd3b7ceb8c7cb7f336 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Wed, 17 Jun 2026 15:37:08 +0300 Subject: [PATCH 182/185] gh-151584: Remove `PyUnstable_Module_SetGIL` call in `_remote_debugging` (#151585) --- Modules/_remote_debugging/module.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/Modules/_remote_debugging/module.c b/Modules/_remote_debugging/module.c index 984213d18817523..36115f20d9d4ccc 100644 --- a/Modules/_remote_debugging/module.c +++ b/Modules/_remote_debugging/module.c @@ -1624,9 +1624,6 @@ _remote_debugging_exec(PyObject *m) return -1; } -#ifdef Py_GIL_DISABLED - PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED); -#endif int rc = PyModule_AddIntConstant(m, "PROCESS_VM_READV_SUPPORTED", HAVE_PROCESS_VM_READV); if (rc < 0) { return -1; From 508ff283133816b75d1dde41d66d2e77d206fbec Mon Sep 17 00:00:00 2001 From: Pablo Galindo Salgado Date: Wed, 17 Jun 2026 14:17:16 +0100 Subject: [PATCH 183/185] gh-150966: Fix live profiling error tests (GH-151020) --- .../test_live_collector_ui.py | 27 +++++++++++++++---- ...-06-06-16-22-00.gh-issue-150966.7N9x5Q.rst | 2 ++ 2 files changed, 24 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Tests/2026-06-06-16-22-00.gh-issue-150966.7N9x5Q.rst diff --git a/Lib/test/test_profiling/test_sampling_profiler/test_live_collector_ui.py b/Lib/test/test_profiling/test_sampling_profiler/test_live_collector_ui.py index 59373a8d00c03cf..ae4ee969f34d8b3 100644 --- a/Lib/test/test_profiling/test_sampling_profiler/test_live_collector_ui.py +++ b/Lib/test/test_profiling/test_sampling_profiler/test_live_collector_ui.py @@ -825,17 +825,34 @@ def test_get_all_lines_full_display(self): class TestLiveModeErrors(unittest.TestCase): """Tests running error commands in the live mode fails gracefully.""" + class QuitWhenFinishedDisplay(MockDisplay): + def __init__(self, collector): + super().__init__() + self.collector = collector + + def get_input(self): + ch = super().get_input() + if ch != -1: + return ch + # Sampling only stops once the target process has exited, at + # which point the collector is marked finished. Quit then so the + # run can surface the target's stderr. We must not rely on the + # target's pid still being signalable: once it exits it lingers + # as a zombie (it is reaped after sample_live returns), so a + # liveness check would never observe it gone and would hang. + if self.collector.finished: + return ord('q') + return -1 + def mock_curses_wrapper(self, func): func(mock.MagicMock()) def mock_init_curses_side_effect(self, n_times, mock_self, stdscr): - mock_self.display = MockDisplay() - # Allow the loop to run for a bit (approx 0.5s) before quitting - # This ensures we don't exit too early while the subprocess is - # still failing + mock_self.display = self.QuitWhenFinishedDisplay(mock_self) + # Feed non-input events so live mode keeps polling while the target + # process is still running; once it exits the display quits on its own. for _ in range(n_times): mock_self.display.simulate_input(-1) - mock_self.display.simulate_input(ord('q')) def test_run_failed_module_live(self): """Test that running a existing module that fails exits with clean error.""" diff --git a/Misc/NEWS.d/next/Tests/2026-06-06-16-22-00.gh-issue-150966.7N9x5Q.rst b/Misc/NEWS.d/next/Tests/2026-06-06-16-22-00.gh-issue-150966.7N9x5Q.rst new file mode 100644 index 000000000000000..3bbb471163d64e1 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2026-06-06-16-22-00.gh-issue-150966.7N9x5Q.rst @@ -0,0 +1,2 @@ +Avoid prematurely terminating failing live sampling profiler test targets, +which made stderr assertions flaky on ASAN buildbots. From 95034cce258bd9adade917008db06b110b50c1f9 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 17 Jun 2026 15:27:02 +0200 Subject: [PATCH 184/185] gh-149044: Improve Py_tp_base[s] docs & error message for non-type bases (GH-151252) The initial implementation of PEP 820 worsened the error message when non-types are given as base types in Py_tp_bases & Py_tp_base. Bring back the 'bases must be types' wording and add a 'got' note for easier debugging. Improve slot ID documentation, and soft-deprecate Py_tp_base (as per the PEP). --- Doc/c-api/type.rst | 43 ++++++++++++++++--- Doc/c-api/typeobj.rst | 11 ++--- Doc/tools/removed-ids.txt | 4 ++ Doc/whatsnew/3.15.rst | 6 +++ Lib/test/test_capi/test_misc.py | 2 +- Lib/test/test_capi/test_slots.py | 35 +++++++++++++++ ...-06-10-15-22-44.gh-issue-149044.O7KEcs.rst | 3 ++ Modules/_testlimitedcapi/slots.c | 43 +++++++++++++++++++ Objects/typeobject.c | 18 ++++---- 9 files changed, 142 insertions(+), 23 deletions(-) create mode 100644 Misc/NEWS.d/next/C_API/2026-06-10-15-22-44.gh-issue-149044.O7KEcs.rst diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst index 4771d0a7781bd60..48eb16bd90834ba 100644 --- a/Doc/c-api/type.rst +++ b/Doc/c-api/type.rst @@ -563,10 +563,10 @@ but need extra remarks for use as slots: :c:member:`Slot ID ` for the name of the type, used to set :c:member:`PyTypeObject.tp_name`. - This slot (or :c:func:`PyType_Spec.name`) is required to create a type. + This slot (or :c:member:`PyType_Spec.name`) is required to create a type. This may not be used in :c:member:`PyType_Spec.slots`. - Use :c:func:`PyType_Spec.name` instead. + Use :c:member:`PyType_Spec.name` instead. .. impl-detail:: @@ -585,7 +585,7 @@ but need extra remarks for use as slots: The value must be positive. This may not be used in :c:member:`PyType_Spec.slots`. - Use :c:func:`PyType_Spec.basicsize` instead. + Use :c:member:`PyType_Spec.basicsize` instead. This slot may not be used with :c:func:`PyType_GetSlot`. Use :c:member:`PyTypeObject.tp_basicsize` instead if needed, but be aware @@ -616,7 +616,7 @@ but need extra remarks for use as slots: :c:macro:`!Py_tp_extra_basicsize` is an error. This may not be used in :c:member:`PyType_Spec.slots`. - Use negative :c:func:`PyType_Spec.basicsize` instead. + Use negative :c:member:`PyType_Spec.basicsize` instead. This slot may not be used with :c:func:`PyType_GetSlot`. @@ -648,7 +648,7 @@ but need extra remarks for use as slots: - With the :c:macro:`Py_TPFLAGS_ITEMS_AT_END` flag. This may not be used in :c:member:`PyType_Spec.slots`. - Use :c:func:`PyType_Spec.itemsize` instead. + Use :c:member:`PyType_Spec.itemsize` instead. This slot may not be used with :c:func:`PyType_GetSlot`. @@ -663,13 +663,44 @@ but need extra remarks for use as slots: :c:func:`PyType_FromSpecWithBases` sets it automatically. This may not be used in :c:member:`PyType_Spec.slots`. - Use negative :c:func:`PyType_Spec.basicsize` instead. + Use negative :c:member:`PyType_Spec.basicsize` instead. This slot may not be used with :c:func:`PyType_GetSlot`. Use :c:func:`PyType_GetFlags` instead. .. versionadded:: 3.15 +.. c:macro:: Py_tp_bases + + :c:member:`Slot ID ` for type flags, used to set + :c:member:`PyTypeObject.tp_bases`. + + The slot can be set to a tuple of type objects which the newly created + type should inherit from, like the "positional arguments" of + a Python :ref:`class definition `. + + Alternately, the slot can be set to a single type object to specify + a single base. + The effect is the same as specifying a one-element tuple. + + .. versionchanged:: 3.15 + + Previously, :c:macro:`!Py_tp_bases` required a tuple of types. + +.. c:macro:: Py_tp_base + + Equivalent to :c:macro:`Py_tp_bases` (with ``s`` at the end). + If both are specified, :c:macro:`!Py_tp_bases` takes priority and + this slot is ignored. + + .. versionchanged:: 3.15 + + Previously, :c:macro:`!Py_tp_base` required a single type, not a tuple. + + .. soft-deprecated:: 3.15 + + When not targetting older Python versions, pefer :c:macro:`!Py_tp_bases`. + The following slots do not correspond to public fields in the underlying structures: diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index dcc9e243c2f3147..16dcb880712d244 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -1936,12 +1936,12 @@ and :c:data:`PyType_Type` effectively act as defaults.) .. c:member:: PyTypeObject* PyTypeObject.tp_base - .. corresponding-type-slot:: Py_tp_base - An optional pointer to a base type from which type properties are inherited. At this level, only single inheritance is supported; multiple inheritance require dynamically creating a type object by calling the metatype. + For the corresponding slot ID, see :c:macro:`Py_tp_base`. + .. note:: .. from Modules/xxmodule.c @@ -2253,17 +2253,12 @@ and :c:data:`PyType_Type` effectively act as defaults.) .. c:member:: PyObject* PyTypeObject.tp_bases - .. corresponding-type-slot:: Py_tp_bases - Tuple of base types. This field should be set to ``NULL`` and treated as read-only. Python will fill it in when the type is :c:func:`initialized `. - For dynamically created classes, the :c:data:`Py_tp_bases` - :c:type:`slot ` can be used instead of the *bases* argument - of :c:func:`PyType_FromSpecWithBases`. - The argument form is preferred. + For the corresponding slot ID, see :c:macro:`Py_tp_bases`. .. warning:: diff --git a/Doc/tools/removed-ids.txt b/Doc/tools/removed-ids.txt index 05fc89d9fe2d278..81c0f098e4c8a83 100644 --- a/Doc/tools/removed-ids.txt +++ b/Doc/tools/removed-ids.txt @@ -29,3 +29,7 @@ reference/expressions.html: grammar-token-python-grammar-enclosure reference/expressions.html: grammar-token-python-grammar-list_display reference/expressions.html: grammar-token-python-grammar-parenth_form reference/expressions.html: grammar-token-python-grammar-set_display + +# Moved to a different page +c-api/typeobj.html: c.Py_tp_base +c-api/typeobj.html: c.Py_tp_bases diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 7f9a0f0e286645d..db6903da9f63444 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -2476,6 +2476,12 @@ New features * :c:func:`PyModule_FromDefAndSpec2` * :c:func:`PyModule_ExecDef` + + The slots :c:macro:`Py_tp_bases` and :c:macro:`Py_tp_base` are now + equivalent: they can be set either to a single type or a tuple of types. + The :c:macro:`Py_tp_bases` slot is preferred; the other is ignored if both + are specified. + (Contributed by Petr Viktorin in :gh:`149044`.) * Add :c:func:`PyUnstable_ThreadState_SetStackProtection` and diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index 3debc6369e89fb4..6d84f0b8c305dfb 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -924,7 +924,7 @@ def test_tp_bases_slot(self): def test_tp_bases_slot_none(self): self.assertRaisesRegex( TypeError, - "metaclass conflict", + "bases must be types", _testcapi.create_heapctype_with_none_bases_slot ) diff --git a/Lib/test/test_capi/test_slots.py b/Lib/test/test_capi/test_slots.py index c78b118712b11d5..b8b6d00b5f84d55 100644 --- a/Lib/test/test_capi/test_slots.py +++ b/Lib/test/test_capi/test_slots.py @@ -312,3 +312,38 @@ def test_repeat_error(self): _testlimitedcapi.module_from_slots("repeat_exec", FakeSpec()) with self.assertRaisesRegex(SystemError, "multiple"): _testlimitedcapi.module_from_slots("repeat_gil", FakeSpec()) + + def test_bases_slots(self): + create = _testlimitedcapi.type_from_base_slots + + # Py_tp_bases overrides Py_tp_base + cls = create(base=int, bases=float) + self.assertEqual(cls.mro(), [cls, float, object]) + + # type is equivalent to one-element tuple + cls = create(base=None, bases=int) + self.assertEqual(cls.mro(), [cls, int, object]) + + cls = create(base=None, bases=(int,)) + self.assertEqual(cls.mro(), [cls, int, object]) + + cls = create(base=int) + self.assertEqual(cls.mro(), [cls, int, object]) + + cls = create(base=(int,)) + self.assertEqual(cls.mro(), [cls, int, object]) + + # Tuple of bases works + class Custom: + pass + cls = create(bases=int) + sub = create(base=float, bases=(Custom, cls, int)) + self.assertEqual(sub.mro(), [sub, Custom, cls, int, object]) + + # Reasonable error message for non-types + with self.assertRaisesRegex(TypeError, + "bases must be types; got 'NoneType'"): + create(base=None) + with self.assertRaisesRegex(TypeError, + "bases must be types; got 'str'"): + create(bases="a string") diff --git a/Misc/NEWS.d/next/C_API/2026-06-10-15-22-44.gh-issue-149044.O7KEcs.rst b/Misc/NEWS.d/next/C_API/2026-06-10-15-22-44.gh-issue-149044.O7KEcs.rst new file mode 100644 index 000000000000000..fe0730b1bf87c4d --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2026-06-10-15-22-44.gh-issue-149044.O7KEcs.rst @@ -0,0 +1,3 @@ +Improved error message when specifying non-type base classes in +:c:macro:`Py_tp_bases`, :c:macro:`Py_tp_base`, and *bases* argument to +:c:func:`PyType_FromMetaclass` and other ``PyType_From*`` functions. diff --git a/Modules/_testlimitedcapi/slots.c b/Modules/_testlimitedcapi/slots.c index 7a8d6466e53a096..9abe53d21154645 100644 --- a/Modules/_testlimitedcapi/slots.c +++ b/Modules/_testlimitedcapi/slots.c @@ -607,6 +607,47 @@ module_from_null_slot(PyObject* Py_UNUSED(module), PyObject *args) }, spec); } + + +static PyObject * +type_from_base_slots( + PyObject *self, PyObject *args, PyObject *kwargs) +{ + PyObject *base = NULL; + PyObject *bases = NULL; + if (!PyArg_ParseTupleAndKeywords( + args, kwargs, "|OO", + (char*[]){"base", "bases", NULL}, + &base, &bases)) + { + return NULL; + } + + PySlot empty_slots[] = { + PySlot_END + }; + + PySlot base_slots[] = { + PySlot_DATA(Py_tp_base, base), + PySlot_END + }; + + PySlot bases_slots[] = { + PySlot_DATA(Py_tp_bases, bases), + PySlot_END + }; + + PySlot slots[] = { + PySlot_STATIC_DATA(Py_tp_name, "_testcapi.HeapCTypeWithBases"), + PySlot_UINT64(Py_tp_flags, Py_TPFLAGS_BASETYPE), + PySlot_DATA(Py_slot_subslots, base ? base_slots: empty_slots), + PySlot_DATA(Py_slot_subslots, bases ? bases_slots: empty_slots), + PySlot_END + }; + + return PyType_FromSlots(slots); +} + static PyMethodDef _TestMethods[] = { {"type_from_slots", type_from_slots, METH_VARARGS}, {"module_from_gil_slot", module_from_gil_slot, METH_VARARGS}, @@ -614,6 +655,8 @@ static PyMethodDef _TestMethods[] = { {"type_from_null_spec_slot", type_from_null_spec_slot, METH_VARARGS}, {"module_from_slots", module_from_slots, METH_VARARGS}, {"module_from_null_slot", module_from_null_slot, METH_VARARGS}, + {"type_from_base_slots", _PyCFunction_CAST(type_from_base_slots), + METH_VARARGS | METH_KEYWORDS}, {NULL}, }; static PyMethodDef *TestMethods = _TestMethods; diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 881ed58d275ac7a..59593fd0f6a0b78 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -3717,9 +3717,9 @@ find_best_base(PyObject *bases) for (i = 0; i < n; i++) { PyObject *base_proto = PyTuple_GET_ITEM(bases, i); if (!PyType_Check(base_proto)) { - PyErr_SetString( + PyErr_Format( PyExc_TypeError, - "bases must be types"); + "bases must be types; got '%T'", base_proto); return NULL; } PyTypeObject *base_i = (PyTypeObject *)base_proto; @@ -4167,8 +4167,9 @@ _PyType_CalculateMetaclass(PyTypeObject *metatype, PyObject *bases) for (i = 0; i < nbases; i++) { tmp = PyTuple_GET_ITEM(bases, i); tmptype = Py_TYPE(tmp); - if (PyType_IsSubtype(winner, tmptype)) + if (PyType_IsSubtype(winner, tmptype)) { continue; + } if (PyType_IsSubtype(tmptype, winner)) { winner = tmptype; continue; @@ -5529,6 +5530,12 @@ type_from_slots_or_spec( } } + /* Calculate best base, and check that all bases are type objects */ + PyTypeObject *base = find_best_base(bases); // borrowed ref + if (base == NULL) { + goto finally; + } + /* Calculate the metaclass */ if (!metaclass) { @@ -5551,11 +5558,6 @@ type_from_slots_or_spec( goto finally; } - /* Calculate best base, and check that all bases are type objects */ - PyTypeObject *base = find_best_base(bases); // borrowed ref - if (base == NULL) { - goto finally; - } // find_best_base() should check Py_TPFLAGS_BASETYPE & raise a proper // exception, here we just check its work assert(_PyType_HasFeature(base, Py_TPFLAGS_BASETYPE)); From f2aa42e02a450554f1500698c0253af27f397ad9 Mon Sep 17 00:00:00 2001 From: Phil Thompson Date: Wed, 17 Jun 2026 18:16:18 +0100 Subject: [PATCH 185/185] Document that `PyErr_SetRaisedException()` accepts `NULL` Adopted the suggestion to use a note rather than a warning. --- Doc/c-api/exceptions.rst | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst index 841754e60881a83..167fe3ba41f92b9 100644 --- a/Doc/c-api/exceptions.rst +++ b/Doc/c-api/exceptions.rst @@ -498,9 +498,13 @@ Querying the error indicator .. c:function:: void PyErr_SetRaisedException(PyObject *exc) - A stolen reference to *exc* is set as the exception currently being raised, - clearing the existing exception if one is set. *exc* may be ``NULL`` in - which case any current exception is cleared. + Set *exc* as the exception currently being raised, clearing the existing + exception if one is set. *exc* may be ``NULL`` in which case any current + exception is cleared. + + .. note:: + + This function "steals" a reference to *exc*. .. versionadded:: 3.12